-----初更----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