1. 程式人生 > 實用技巧 >在windows下使用C語言實現回聲伺服器和客戶端(socket)

在windows下使用C語言實現回聲伺服器和客戶端(socket)

  記錄一下在windows平臺實現基本的socket程式設計,實現一個簡易的回聲伺服器和客戶端,廢話不多說,直接上程式碼,裡面有詳盡的註釋。

作業系統:win10 64位

編輯器:vscode,需要安裝c/c++外掛。

編譯器:MinGw編譯器

  伺服器:

  1 #include <stdio.h>
  2 #include <winsock2.h>
  3 #pragma comment(lib,"ws2_32.lib")
  4 
  5 #define PORT 6666
  6 
  7 int main()
  8 {
  9     WSADATA wsaData = {0
}; //定義一個結構體,用來接收函式給的引數 10 int err = -1; 11 //第一步:定義我們需要的winsock的版本,這裡是2.2版本(目前最高的版本號) 12 err = WSAStartup(MAKEWORD(2,2), &wsaData); 13 if(0 != err) //返回非0 ,代表出錯 14 { 15 printf("failed with error:%d\n", err); 16 system("pause"); 17 return 1; 18 } 19
//第二部:檢視當前系統支援的版本,如果不支援我們上面定義的版本就用不了 20 if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2) 21 { 22 printf("system not support for this version\n"); 23 WSACleanup(); //釋放系統資源 24 system("pause"); 25 return 1; 26 } 27 else 28 { 29
printf("The WinSock 2.2 dll was found\n"); 30 } 31 //第三步:開始建立套接字 32 SOCKET server = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 33 if(INVALID_SOCKET == server) 34 { 35 printf("create socket failed with error:%d\n",WSAGetLastError()); 36 WSACleanup(); 37 system("pause"); 38 return 1; 39 } 40 //第四步:準備結構體,繫結socket 41 SOCKADDR_IN addr = {}; 42 addr.sin_family = AF_INET; 43 addr.sin_port = htons(PORT); //將本地位元組序轉換成網路位元組序 44 addr.sin_addr.S_un.S_addr = INADDR_ANY; 45 //如果需要指定IP,可以這樣 46 //addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); 47 if(SOCKET_ERROR == bind(server,(SOCKADDR*)&addr,sizeof(addr))) //成功返回0 48 { 49 printf("bind socket failed with error:%d\n",WSAGetLastError()); 50 closesocket(server); 51 WSACleanup(); 52 system("pause"); 53 return 1; 54 } 55 //第五步:監聽 56 if(SOCKET_ERROR == listen(server,SOMAXCONN)) //成功返回0 57 { 58 printf("listen socket failed with error:%d\n",WSAGetLastError()); 59 closesocket(server); 60 WSACleanup(); 61 system("pause"); 62 return 1; 63 } 64 //第六步,等待連線 65 SOCKADDR_IN cli_addr = {}; 66 int len = sizeof(cli_addr); 67 printf("all is okay,waitting for client......\n"); 68 while(1) //因為可以接收很多的客戶,這裡使用無限迴圈 69 { 70 // 這一步將會阻塞,直到有客戶端連線進來(接客) 71 SOCKET cli = accept(server,(SOCKADDR*)&cli_addr,&len); 72 //這一步可以優化,用執行緒來做,主執行緒只負責接客,子執行緒來服務客人(有效的連線)。 73 if(INVALID_SOCKET == cli) 74 { 75 printf("invalide socket,error:%d\n",WSAGetLastError()); 76 closesocket(server); 77 WSACleanup(); 78 system("pause"); 79 return 1; 80 } 81 //客戶資訊有效,列印下看看吧 82 printf(">>client$\t%s:%d connected!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port)); 83 84 //第七步:開始通訊,這裡寫一個回聲伺服器(將收到的資料,原數發回給客戶端) 85 char buff[MAXBYTE] = {}; //儲存客戶端發來的資訊 86 int bufflen = 0; 87 do //客戶端可能會發送多次資料,這裡暫使用無限迴圈 88 { 89 //每次接收資料前,需要將上一次接收的緩衝區資料清空 90 ZeroMemory(buff,sizeof(buff)); //該函式底層呼叫的memset函式 91 bufflen = recv(cli,buff,sizeof(buff),0); 92 if(bufflen == 0) //recv函式可以接受0個引數,代表對方關閉連線了 93 { 94 printf("connection closed!\n"); 95 } 96 else if (SOCKET_ERROR == bufflen) 97 { 98 printf("recv failed with error:%d\n",WSAGetLastError()); 99 } 100 else //接收到了資料 101 { 102 buff[bufflen] = '\0'; 103 printf("received:%s\n",buff); 104 //send函式可以傳送大於等於0的資料 105 int len = send(cli,buff,bufflen,0); 106 if(SOCKET_ERROR == len) 107 { 108 printf("send failed with error:%d\n",WSAGetLastError()); 109 break; 110 } 111 else 112 { 113 printf("send successfully!\nrecv:%d Bytes,send:%d Bytes\n",bufflen,len); 114 } 115 116 } 117 118 } while(bufflen>0); 119 closesocket(cli); 120 } 121 closesocket(server); 122 WSACleanup(); 123 getchar(); 124 return 0; 125 }

客戶端:

 1 #include <stdio.h>
 2 #include <winsock2.h>
 3 #pragma comment(lib,"ws2_32.lib")
 4 
 5 #define PORT 6666
 6 #define SERVERADDR "127.0.0.1"
 7 
 8 int main()
 9 {
10     WSADATA wsaData = {};
11     int err = WSAStartup(MAKEWORD(2,2),&wsaData);
12     if(0 != err)
13     {
14         printf("failed with error:%d\n", err);
15         system("pause");
16         return 1;
17     }
18     //建立套接字
19     SOCKET client = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
20     if(INVALID_SOCKET == client)
21     {
22         printf("create socket failed with error:%d\n",WSAGetLastError());
23         WSACleanup();
24         system("pause");
25         return 1;
26     }
27     //準備伺服器的資訊
28     SOCKADDR_IN server_addr = {};
29     server_addr.sin_family = AF_INET;
30     server_addr.sin_port = htons(PORT); //將本地位元組序轉換成網路位元組序
31     server_addr.sin_addr.S_un.S_addr = inet_addr(SERVERADDR);
32     //開始連線伺服器
33     if(SOCKET_ERROR == connect(client,(SOCKADDR*)&server_addr,sizeof(server_addr)))
34     {
35         printf("connect to server with error:%d\n",WSAGetLastError());
36         closesocket(client);
37         WSACleanup();
38         system("pause");
39         return 1;
40     }
41     int len = 0;
42     char buff[MAXBYTE] = {0};
43     char recv_buff[MAXBYTE];
44     int flag=0;
45     do
46     {
47         memset(buff,'\0',MAXBYTE);
48         printf("請輸入需要傳送的內容:\n");
49         rewind(stdin);
50         flag = scanf("%s",buff);
51         if(EOF!= flag)
52         {
53             //printf("buff:%s,%d\n",buff,strlen(buff));
54             char t_buff[MAXBYTE] = "你好啊哈";
55             int len = send(client,t_buff,strlen(t_buff),0);
56             if(SOCKET_ERROR == len)
57             {
58                 printf("send failed with error:%d\n",WSAGetLastError());
59                 break;
60             }
61             //準備接收伺服器的回聲
62             memset(recv_buff,0,sizeof(recv_buff));
63             int recv_len = recv(client,recv_buff,sizeof(recv_buff),0);
64             if(recv_len == 0)  //對方關閉連線了
65             {
66                 printf("service closed!\n");
67                 break;
68             }
69             else if (SOCKET_ERROR == recv_len)
70             {
71                 printf("recv failed with error:%d\n",WSAGetLastError());
72                 break;
73             }
74             //列印接收到的資料
75             printf("received from server:%s\n",recv_buff);
76 
77         }
78     } while(flag!=EOF);
79     closesocket(client);
80     WSACleanup();
81     system("pause");
82     return 0;
83 }

總結:

1. 伺服器的程式在vscode上親測沒有問題,客戶端程式通過檢視程式碼可以看到,每次發的都是固定的字串,原因是使用scanf函式獲取字串失敗,目前沒有找到原因,希望有人幫指點下。

2. vscode的控制檯使用的是utf-8字元編碼格式,所以出現中文不會有亂碼,如果是使用的是windows的控制檯(ASCII編碼格式)開啟程式,出現中文會顯示亂碼,這是需要轉碼才能正常顯示。

參考:MultiByteToWideChar和WideCharToMultiByte函式,查詢下MSDN。

最後貼上一個用python寫的簡易客戶端程式,我當時是用來快速檢驗服務端程式的,證明服務端程式能正常執行。

 1 from socket import socket
 2 s=socket()
 3 s.connect(('127.0.0.1',6666))
 4 while True:
 5     send_str = input("請輸入需要傳送的內容:\n")
 6     if len(send_str)>0:
 7         s.send(send_str.encode())
 8     else:
 9         break
10     res = s.recv(1024).decode()
11     print(res)
12     print('='*20)
13 s.close()

是不是感覺程式碼很簡易,其實底層的實現都是一樣的,python是做了一個封裝,使用起來比較方便而已。