-----初更----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 }
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 }
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 }
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 }
0x07 參考
【1】特別參考:http://c.biancheng.net/cpp/html/3031.html
【2】特別參考:http://c.biancheng.net/cpp/html/3030.html
