基於Select模型的通訊模擬--win32程式設計程式碼

刘倩_网安2211發表於2024-11-18

目錄
  • 基於Select模型的通訊模擬--win32程式設計程式碼
    • 程式設計環境
    • 服務端
    • 客戶端
    • 相關連結
      • 基於Select模型的通訊模擬(詳解)

基於Select模型的通訊模擬--win32程式設計程式碼

程式設計環境

Visual C++ 6.0

服務端

#include<stdio.h>
#include<string.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	AllocConsole();
	freopen("CONOUT$","w",stdout);
	freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stderr);
	// 初始化 Winsock
	WSADATA wsaData;
	int result=WSAStartup(MAKEWORD(2, 2), &wsaData);

	if (result != 0) 
	{
		 printf("WSAStartup failed with error: %d\n", result);
		 return 1;
	}

	//1.建立socket套接字
	/*
	SOCKET socket(
    _In_ int af,		//協議地址簇	IPv4:AF_INET/IPv6:AF_INET6
    _In_ int type,		//型別			流式協議:SOCK_STREAM/資料包協議:SOCK_DGRAM
    _In_ int protocol	//保護協議		上層協議tcp、udp不用寫,填0即可
    );
	*/
	SOCKET listen_socket=socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET==listen_socket)
	{
		printf("create listen socket failed!!! errcode: %d\n", GetLastError());
		WSACleanup();
		return -1;
	}

	//2.給socket繫結埠號
	/*
	* ip地址結構體
	struct sockaddr_in {
	ADDRESS_FAMILY sin_family;	//協議地址簇
    USHORT sin_port;			//埠號	2位元組
    IN_ADDR sin_addr;			//ip地址	4位元組
    CHAR sin_zero[8];			//保留位元組	8位元組
	};
	struct sockaddr {
	u_short sa_family;					//協議地址簇
	CHAR sa_data[14];                   //14位元組
	}
	*/
	struct sockaddr_in local = {0};

	local.sin_family = AF_INET;
	local.sin_port = htons(8080);//繫結埠 大小端轉化:中間裝置使用大端序(路由器),但是電腦使用的是小端序,所以要轉化
	/*
	電腦上有多個網路卡,伺服器可以選擇接收哪個或哪些網路卡傳過來的資料。
	如:127.0.0.1(本地環回),則伺服器只能和自己通訊	INADDR_LOOPBACK
	一般寫全0地址(0.0.0.0),表示接收全部網路卡的資料	INADDR_ANY
	*/
	//local.sin_addr.s_addr = htonl(INADDR_ANY);//接收全部網路卡的資料 大小端轉化
	local.sin_addr.s_addr = inet_addr("0.0.0.0");//接收全部網路卡的資料 字串ip轉成整數ip

	/*
	int bind(
    SOCKET s,							//繫結的套接字
    const struct sockaddr FAR * name,	//ip地址結構體
    int namelen							//ip地址結構體的長度
    );
	*/
	bind(listen_socket,(struct sockaddr*)&local,sizeof(local));

	//3.給socke開啟監聽屬性,只用來接收連線
	/*
	int listen(
    _In_ SOCKET s,		//監聽套接字
    _In_ int backlog	//
    );
	*/
	if (-1 == listen(listen_socket, 10))
	{
		printf("start listen failed!!! errcode: %d\n", GetLastError());
		WSACleanup();
		return -1;
	}
    fd_set redset;
    //4、初始化檔案描述符集合  
    FD_ZERO(&redset);
    //5、新增要檢測的監聽檔案描述符    
    FD_SET(listen_socket,&redset);

    
    printf("This is SERVER!\n");
    while (1)
    {
        fd_set tmp = redset;
        /* 6、不停地檢測檔案描述符
              6.1 超時  select() = 0 再次檢測或關閉套接字
              6.2 異常  select() = -1 異常處理
              6.3 成功  selct() >0
       */
        printf("selecting...\n");
        int ret = select(-1, &tmp, NULL, NULL, NULL);
        if (ret <= 0)
        {
            printf("select failed!!! errcode: %d\n", GetLastError());
            closesocket(listen_socket);
            WSACleanup();
            return -1;
        }
       // printf("select = %d\n",ret);
        //7、判斷檔案描述符屬於哪一類
            //透過將原來redset集合中的檔案描述符與select處理過的tmp集合比較,判斷哪些檔案描述符就緒,如果就緒,是哪一類
        for (int i = 0; i < (int)redset.fd_count; i++)
        {
            if (FD_ISSET(redset.fd_array[i], &tmp))//判斷檔案描述符(套接字)i的讀緩衝區是否有資料
            {
                //就緒檔案描述符是監聽描述符
                if (redset.fd_array[i] == listen_socket)    // 監聽套接字接收到新連線
                {
                    if (redset.fd_count < FD_SETSIZE)
                    {
                        sockaddr_in addrRemote;
                        int nAddrLen = sizeof(addrRemote);
                        //接收客戶端的連線請求
                        SOCKET client_socket = ::accept(listen_socket, (SOCKADDR*)&addrRemote, &nAddrLen);
                        FD_SET(client_socket, &redset);
                        printf("與主機 %s 建立連線\n", inet_ntoa(addrRemote.sin_addr));
                    }
                    else
                    {
                        printf("Too much connections!\n");
                        continue;
                    }
                }
                else//就緒檔案描述符不是監聽描述符,是通訊描述符
                {
                    //接收資訊
                    char rbuffer[1024] = { 0 };
                    int len = recv(redset.fd_array[i], rbuffer, 1024, 0);

                    if (len <= 0)
                    {
                        printf("The client %d has disconnected.\n", i);
                        FD_CLR(redset.fd_array[i], &redset);
                        shutdown(redset.fd_array[i], SD_BOTH);
                        closesocket(redset.fd_array[i]);
                        break;
                    }
                    printf("recive from client%d:\t%s\n", i, rbuffer);

                    //傳送資訊
                    char sbuffer[1024] = { 0 };
                    // 檢查接收到的訊息
                    if (strcmp(rbuffer, "計算從1到100的奇數和") == 0)
                    {
                        int sum = 0;
                        for (int j = 1; j <= 100; j += 2)
                        {
                            sum += j;
                        }
                        printf("send to client%d:\t1到100的奇數和是 %d\n", i, sum);

                        sprintf(sbuffer, "1到100的奇數和是 %d\n", sum);
                    }
                    else
                    {
                        printf("send to client%d:\tunknow!\n", i);
                        sprintf(sbuffer, "unknow!");
                    }

                    len = send(redset.fd_array[i], sbuffer, strlen(sbuffer), 0);
                    if (len == -1)
                    {
                        perror("send error");
                        exit(1);
                    }
                   
                }
            }
        }
    }

    closesocket(listen_socket);
	FreeConsole();
	// 清理 Winsock
    WSACleanup();


	return 0;
}

客戶端

#include<stdio.h>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
	AllocConsole();
	freopen("CONOUT$","w",stdout);
	freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stderr);
	// 初始化 Winsock
	WSADATA wsaData;
	int result=WSAStartup(MAKEWORD(2, 2), &wsaData);

	if (result != 0) 
	{
		 printf("WSAStartup failed with error: %d\n", result);
		 return 1;
	}

	//1.建立socket套接字
	SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (INVALID_SOCKET == client_socket)
	{
		printf("create socket failed!!! errcode: %d\n", GetLastError());
		WSACleanup();
		return -1;
	}
	//2.連線伺服器
	struct sockaddr_in target;//目標伺服器的ip結構體
	target.sin_family = AF_INET;
	target.sin_port = htons(8080);
	target.sin_addr.s_addr = inet_addr("127.0.0.1");

	if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target)))
	{
		printf("connect server failed!!!\n");
		shutdown(client_socket, SD_BOTH);
		closesocket(client_socket);
		WSACleanup();
		return -1;
	}
	//3.開始通訊

	printf("This is  Cilent1.\n\n");
	while (1)
	{
		//傳送資訊
		printf("send:\t");
		char sbuffer[1024] = { 0 };
		scanf("%s", sbuffer);
		send(client_socket, sbuffer, strlen(sbuffer),0);

		//接收訊息
		char rbuffer[1024] = { 0 };
		int ret=recv(client_socket, rbuffer, 1024, 0);
		if (ret <= 0)
		{
			break;//斷開連線
		}
		printf("recive:\t%s\n", rbuffer);
	}
	
	//4.關閉連線
	shutdown(client_socket, SD_BOTH);shutdown(client_socket, SD_BOTH);
	closesocket(client_socket);
	
	FreeConsole();
	// 清理 Winsock
    WSACleanup();


	return 0;
}

相關連結

基於Select模型的通訊模擬(詳解)

https://www.cnblogs.com/wa2211lq/p/18553178

相關文章