【C++】socket編程(二):最簡單的socket通信(下)


-----初更----2018年1月29日(轉載請注明出處)----

前言:

有很多人看完上一節后,一頭霧水,我是誰?我在哪?我在干什么?其實socket問題就類似於把大象放進冰箱總共分幾步的問題,這時候就要咬咬牙,把這兩章需要用得到的函數搞明白,以后就是這些函數的伸展了

#0x01    accept()函數

accept()是服務端的函數。從上一張開始說,服務端bind之后,做好listen的准備,這時候,客戶端說:喂,我要連接(connent),服務器端就要回話:喂,我知道了(accept),這就是accept的意思,當然實際使用accept函數可是非常強大呢!

我們先看一下使用方法

Linux/windows

1     SOCKADDR clntAddr;//客戶端信息
2     int nSize = sizeof(SOCKADDR);//字節大小
3     SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);

可以看到,accept函數和bind()函數很像,在使用之前,需要再建立一個socket結構體,為什么呢?因為服務端作為接受嘛,可能有不同的人在叫他,他不知道誰在叫他,所以在有人叫他時候,就要拿個小本本記下來,知道張三叫了他,地址是*¥@*&¥*#,然后服務器在向這個地址回話:Hello World!這就是accept函數的作用。

#0x02    發送函數

Windows:

在windows下呢,發送嘛,不就是send嘛,沒錯,發送函數就是send(),只是具體用法是:

send(clntSock, str, strlen(str) + sizeof(char), NULL);

 

函數原型是

int send(
  _In_       SOCKET s,//最開始創建的套接字
  _In_ const char   *buf,//需發送的字符串
  _In_       int    len,//發送字符串的長度
  _In_       int    flags//標志位
);

 

 看完原型應該就知道怎么使用了,比較簡單,還有,別問我標志位是什么用,我還不知道,尷尬,只是官方解釋可以忽略。

 Linux:

linux下的原型和windows差不多,只不過沒有了官方說可以忽略的選項,名字換了一下而已。

write(clnt_sock, str, sizeof(str));

 

 write有點意思,好像在哪見過,沒錯,這就是寫入文件函數,因為在linux的思想就是一切皆文件,所以socket通信也被看做是往socket管道中寫入字符,所以就有了write。

#0x03    接收函數

Windows:

windows下接收函數和發送函數參數一樣,只是函數名表不一樣

recv(sock, szBuffer, MAXBYTE, NULL);

 

 Linux:

Linux下也是和發送函數相反

read(sock, buffer, sizeof(buffer)-1);

 

0x04    關閉通道

Windows:

Windows下直接調用函數就可以關閉套接字

closesocket(sock);

 

sock還是最開始創建的套接字

Linux:

Linux下也是一樣,只是函數不一樣

close(sock);

 

0x05    圖解

怎么樣,現在需要用到的函數都講完了,是不是懵逼了,其實只要弄明白每個函數是做什么的就可以了,廢話不多說,直接放圖

有沒有大徹大悟!!!這張圖很好的解釋了socket的工作原理,一定要把這張圖吃透!!!

0x06    代碼

說了一堆堆理論,是時候亮出真家伙了!!!代碼才是最鋒利的武器對不對,不多說直接上代碼(注:一定要把前面的每個函數都弄懂在看代碼,然后自己嘗試寫出來!)

Windows:

 1 #include <stdio.h>
 2 #include <winsock2.h>
 3 #pragma comment (lib, "ws2_32.lib")  //加載 ws2_32.dll
 4 
 5 /*服務器端代碼*/
 6 
 7 int main(){
 8     //初始化 DLL
 9     WSADATA wsaData;
10     WSAStartup( MAKEWORD(2, 2), &wsaData);
11 
12     //創建套接字
13     SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
14 
15     //綁定套接字
16     sockaddr_in sockAddr;
17     memset(&sockAddr, 0, sizeof(sockAddr));  //每個字節都用0填充
18     sockAddr.sin_family = PF_INET;  //使用IPv4地址
19     sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具體的IP地址
20     sockAddr.sin_port = htons(1555);  //端口
21     bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
22 
23     //進入監聽狀態
24     listen(servSock, 20);
25 
26     //接收客戶端請求
27     SOCKADDR clntAddr;
28     int nSize = sizeof(SOCKADDR);
29     SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
30 
31     //向客戶端發送數據
32     char *str = "Hello World!";
33     send(clntSock, str, strlen(str)+sizeof(char), NULL);
34 
35     //關閉套接字
36     closesocket(clntSock);
37     closesocket(servSock);
38 
39     //終止 DLL 的使用
40     WSACleanup();
41 
42     return 0;
43 }
Windows服務器端代碼

 

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <WinSock2.h>
 4 #pragma comment(lib, "ws2_32.lib")  //加載 ws2_32.dll
 5 
 6 /*客戶端代碼*/
 7 
 8 int main(){
 9     //初始化DLL
10     WSADATA wsaData;
11     WSAStartup(MAKEWORD(2, 2), &wsaData);
12 
13     //創建套接字
14     SOCKET sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
15 
16     //向服務器發起請求
17     sockaddr_in sockAddr;
18     memset(&sockAddr, 0, sizeof(sockAddr));  //每個字節都用0填充
19     sockAddr.sin_family = PF_INET;
20     sockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
21     sockAddr.sin_port = htons(1555);
22     connect(sock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
23 
24     //接收服務器傳回的數據
25     char szBuffer[MAXBYTE] = {0};
26     recv(sock, szBuffer, MAXBYTE, NULL);
27 
28     //輸出接收到的數據
29     printf("Message form server: %s\n", szBuffer);
30 
31     //關閉套接字
32     closesocket(sock);
33 
34     //終止使用 DLL
35     WSACleanup();
36 
37     system("pause");
38     return 0;
39 }
Windows客戶端代碼

 

Linux:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <arpa/inet.h>
 6 #include <sys/socket.h>
 7 #include <netinet/in.h>
 8 
 9 /*服務器端代碼*/
10 
11 int main(){
12     //創建套接字
13     int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
14 
15     //將套接字和IP、端口綁定
16     struct sockaddr_in serv_addr;
17     memset(&serv_addr, 0, sizeof(serv_addr));  //每個字節都用0填充
18     serv_addr.sin_family = AF_INET;  //使用IPv4地址
19     serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具體的IP地址
20     serv_addr.sin_port = htons(1555);  //端口
21     bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
22 
23     //進入監聽狀態,等待用戶發起請求
24     listen(serv_sock, 20);
25 
26     //接收客戶端請求
27     struct sockaddr_in clnt_addr;
28     socklen_t clnt_addr_size = sizeof(clnt_addr);
29     int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
30 
31     //向客戶端發送數據
32     char str[] = "Hello World!";
33     write(clnt_sock, str, sizeof(str));
34    
35     //關閉套接字
36     close(clnt_sock);
37     close(serv_sock);
38 
39     return 0;
40 }
Linux服務器端代碼
 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <stdlib.h>
 4 #include <unistd.h>
 5 #include <arpa/inet.h>
 6 #include <sys/socket.h>
 7 
 8 /*客戶端代碼*/
 9 
10 int main(){
11     //創建套接字
12     int sock = socket(AF_INET, SOCK_STREAM, 0);
13 
14     //向服務器(特定的IP和端口)發起請求
15     struct sockaddr_in serv_addr;
16     memset(&serv_addr, 0, sizeof(serv_addr));  //每個字節都用0填充
17     serv_addr.sin_family = AF_INET;  //使用IPv4地址
18     serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");  //具體的IP地址
19     serv_addr.sin_port = htons(1555);  //端口
20     connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
21    
22     //讀取服務器傳回的數據
23     char buffer[40];
24     read(sock, buffer, sizeof(buffer)-1);
25    
26     printf("Message form server: %s\n", buffer);
27    
28     //關閉套接字
29     close(sock);
30 
31     return 0;
32 }
Linux客戶端代碼

 

0x07    參考

【1】特別參考:http://c.biancheng.net/cpp/html/3031.html

【2】特別參考:http://c.biancheng.net/cpp/html/3030.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM