【Server】Socket编程原理详解

这篇文章将对一个简单的socket例程进行剖析。

代码

这里提供的例子就是一个简单的TCP client/server程序,client主动连接server,并从服务器中得到一条欢迎消息:“[server] welcome client!”。程序的流程参考如下:

【Server】Socket编程原理详解_第1张图片

参考代码如下:

server端:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>

int main(void)
{
	int listenfd = 0,connfd = 0;
	struct sockaddr_in serv_addr;
	char sendBuff[1025];
	
	listenfd = socket(AF_INET, SOCK_STREAM, 0);
	
	memset(&serv_addr, '0', sizeof(serv_addr));
	memset(sendBuff, '0', sizeof(sendBuff));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(55555);
	
	bind(listenfd, (struct sockaddr*)&serv_addr,sizeof(serv_addr));
        printf("server start successfully!\n");
	
	if(listen(listenfd, 10) == -1){
		printf("Failed to listen\n");
		return -1;
	}
    
	while(1) {
		connfd = accept(listenfd, (struct sockaddr*)NULL ,NULL); // accept awaiting request
		
		strcpy(sendBuff, "[server] welcome client!");
		write(connfd, sendBuff, strlen(sendBuff));
		
		close(connfd);
		sleep(1);
        }
	
	return 0;
}

客户端代码:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>

int main(void)
{
	int sockfd = 0,n = 0;
	char recvBuff[1024];
	struct sockaddr_in serv_addr;
	
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0) {
		printf("\n Error : Could not create socket \n");
		return 1;
    }
	
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(55555);
	serv_addr.sin_addr.s_addr = inet_addr("192.168.1.101"); 
	if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0)
    {
		printf("\n Error : Connect Failed \n");
		return 1;
    }
    
	memset(recvBuff, '0' ,sizeof(recvBuff));
	while((n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) {
		recvBuff[n] = 0;
		if(fputs(recvBuff, stdout) == EOF) {
			printf("\n Error : Fputs error");
		}
		printf("\n");
    }
	
	if( n < 0) {
		printf("\n Read Error \n");
    }
    
	return 0;
}


原理

先谈谈服务器端的listen的原理,listen函数的声明如下:

int listen(int sockfd, int backlog);

其中backlog参数表明服务器内核能够同时承受的最大的连接数(这里的例子是10)。继续深入backlog。其实内核维护了两个队列:1)连接未完成队列( incomplete connection queue); 2) 连接完成队列( completed connection queue)。 "连接未完成队列"可以理解为空闲队列,当客户端到来,将占用一个 "连接未完成队列",与服务器进行3次握手成功后,将从“连接未完成队列”移动到“连接完成队列”中。如下图所示:

【Server】Socket编程原理详解_第2张图片

接下来讨论客户端和服务器端的三次握手连接。

【Server】Socket编程原理详解_第3张图片

上图描述的情况简述如下:在三次握手之前,服务器端进行了socket,bind和listen函数的调用,在内核中设立一个socket资源,并准备了相关的连接队列,等待客户端连接的到来;之后服务器就阻塞在accept函数上面。当客户端准备好要连接的服务信息(即为struct sockaddr_in结构体指定特定的值),并传给connect来实现和服务器的三次握手,并确立tcp连接(server端accept函数将返回,客户端connect将返回)。

建立tcp连接之后,就是数据的通讯了,在通讯结束后,要关闭连接,由于tcp是全双工的,要关闭读和写,每次操作要确认,所以关闭tcp需要四次数据包的传递。如下图:

【Server】Socket编程原理详解_第4张图片

实验

原理介绍清楚了,到了进行试验的时候了。这里,我将使用Wireshark工具在mac平台上进行试验,具体如下:

1. 工具的准备。Wireshark可以在官网上下载到。

2.  在mac运行,还需要设置一下权限,否则Wireshark无法访问网卡(参考《No Interfaces Available In Wireshark Mac OS X》)。在控制台运行如下命令:

sudo chmod 644 /dev/bpf*
3. 配置Wireshark;在Wireshark的工具栏点击 ,选择相应的网卡(这里走本地网卡,所以选择lo0),如下图:

4. 由于使用xcode进行调试代码,调试信息也是走本地网络的,所以,我要做一些过滤,以便更好地观察试验结果。具体在工具栏下面的Filter下,填写过滤条件:

ip.addr==192.168.1.101

这里表明将只显示目标或者源地址中包含192.168.1.101的数据包(调试信息,地址为127.0.0.1,所以可以被过滤掉)。

点击开始按钮,就可以对数据包进行监听了。

在这个试验中,我在xcode中设置了几个断点,具体如下:

服务器端:

【Server】Socket编程原理详解_第5张图片

客户端:

【Server】Socket编程原理详解_第6张图片

这里约定,服务器代码第36行记作s36, 客户端代码第25行记作c25。

确保WireShark启动的状态下,启动服务器端(这时会阻塞在s34,accept函数处),然后启动客户端。

1)当代码运行到c25行时,wireShark并没有纪录到任何信息。

2)当客户端运行到c33时(执行完connect函数后,客户端将阻塞在read函数上),服务器运行到s36。这样服务器和客户端建立了tcp连接,wireShark纪录到了三次握手的信息,如下:


3)当服务器端运行到s39,服务器端就向客户端发送了“[server] welcome client!”字符串,这时候客户端从c33唤醒,运行到c38(经过read的调用,程序从内核中取出数据,并打印,运行过c38后,客户端又被阻塞在c33行,等待服务器的消息)。这个时候,wireShark纪录到数据的传输,如下图:

【Server】Socket编程原理详解_第7张图片

4)当服务器端运行过s39,执行完close之后,客户端将从c33唤醒,这个时候的返回值为0,跳出循环到c41;wireShark纪录到结束的四次数据包交换,如下图:


ok,tcp的剖析暂时到这里,接下来我将写点select,poll等相关的nonblock的文章。


参考:

《Unix Networking Programming Volume 1, Third Edition》

https://langui.sh/2010/01/31/no-interfaces-available-in-wireshark-mac-os-x/

你可能感兴趣的:(C++,server,networking,socket原理,tcp剖析)