基本TCP套接口的编程流程

基本编程流程概述

要实现客户端和服务器端的通信,则需要客户端和服务器端共同完成。其中,TCP服务器和客户端的编程流程如图所示:在这里插入图片描述
首先由服务器端创建socket,然后bind绑定通信端口,创建listen监听队列。之后便开始了通讯的过程。此时服务器阻塞在accept这里,直到tcp客户端主动进行connect进行三次握手建立连接。此时客户端的write函数向服务器发送请求,服务器端read来读取请求,并且进行处理。处理完成之后调用服务器端的write函数给客户端发送应答报文,客户端则通过read来接受回应信息。至此整个数据交换结束,客户端可以主动关闭。

客户端和服务器端的socket编程

在这里插入图片描述
其中family通常在IPV4协议下下是AF_INET,IPv6协议下是AF_INET6。type在TCP传输协议下是SOCK_STREAM,UDP协议下是SOCK_DGRAM.一般来说,函数socket的参数protocol设置为0,除非用在原始套接口上

客户端connect函数和服务器建立链接

TCP客户端用connect函数来和TCP服务器建立连接。在这里插入图片描述
sockfd是socket函数的返回值。第二个参数是指向套接口地址结构体的指针,第三个参数是结构体的大小。在这里插入图片描述
sin_port是端口号,S_addr是服务器网络ip地址。通过这段程序,客户端即可连接到客户端的端口和ip上,也就是建立链接。

bind绑定套接字和ip端口

bind给套接口分配一个本地协议地址,对于网络协议,协议地址是32位Ipv4地址或者128位Ipv6地址。
在这里插入图片描述
第一个参数是socket函数的返回值,第二个和第三个同connect函数中所描述的。在这里插入图片描述
建立两个套接口对象,一个是面向服务器,一个面向客户端。

服务器listen监听队列

listen仅仅被TCP服务器调用,主要完成两个功能:
在这里插入图片描述
backlog指的是最大链接个数。对于一个给定的监听套接口,内核要维护两个队列:
1.以完成链接队列
2.未完成连接队列在这里插入图片描述
backlog之前一直被定义为两个队列总和的最大值。通常试验环境下,将backlog设置成5。但是对于现代服务器来说是远远不够的。记住backlog不能设置成0,这会导致程序发生缺陷。如果不想要其他人来链接,要关闭此端口。

服务器端的accept

accept由服务器调用,用来链接客户端发送的connect请求,由此建立通过三次挥手建立连接。在这里插入图片描述
请注意,如果accept执行成功,返回值是一个由内核自动生成的全新描述符,代表和客户端的TCP链接。 这个描述符称之为已连接套接字,而把socket函数的返回值称之为监听套接字。一个监听套接字sockfd往往在服务器程序中只生成一个,并且一直存在。而已连接套接字C代表着客户端和服务器完成了三次握手,当客户端断开时,则关闭C套接字。
当然,如果C<0,则代表已经建立链接失败,则会阻塞一直等待客户端去链接。在这里插入图片描述

write和read

write和read是两种状态,分别代表处理数据和接收数据。举个例子,在程序进行通信的时候,通常write由send系统调用来表示,而read通常通过recv系统调用来表示。
客户端的send和recv:在这里插入图片描述
服务器端的send和recv:在这里插入图片描述

关闭套接字close

至此,TCP套接口的编程流程就基本结束,只需要关闭掉套接字描述符即可。

实验结果

三次握手建立链接:在这里插入图片描述
持续读取数据和客户端发送数据:在这里插入图片描述
服务器源代码:

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

int main()
{
    
    
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(-1!=sockfd);

	struct sockaddr_in saddr,caddr;
	memset(&saddr,0,sizeof(saddr));    //注意memset是取地址

	saddr.sin_family=AF_INET;
	saddr.sin_port=htons(6000);//主机转网络字节,网络是大段;
	saddr.sin_addr.s_addr=inet_addr("127.0.0.1");

	int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	assert(res!=-1);

	listen(sockfd,5);    //以完成3次握手队列长度是5


	while(1)
	{
    
    
		int len=sizeof(caddr);
		int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
		
		//这里是c也是套接字,类似与上面代码的sockfd;

		if(c<0)
		{
    
    
			continue;
		}
		printf("accept c=%d\n",c);     //链接套接子
		while(1)
		{
    
    
			char buff[128]={
    
    0};
			int n=recv(c,buff,127,0);
			//如果对方关闭了发送,recv返回0;-1是失败
			if(n<=0)
			{
    
    
				break;
			}
			printf("buff=%s\n",buff);
			send(c,"ok",2,0);
		}
		close(c);
	}
}



客户端源代码:

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

int main()
{
    
    
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd!=-1);

	struct sockaddr_in saddr;
	memset(&saddr,0,sizeof(saddr));
	saddr.sin_family=AF_INET;
	saddr.sin_port=htons(6000);
	saddr.sin_addr.s_addr=inet_addr("127.0.0.1");

	int res =connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
	assert(res!=-1);

	while(1)
	{
    
    
		char buff[128]={
    
    0};
		printf("input:\n");
		fgets(buff,128,stdin);

		if(strncmp(buff,"end",3)==0)
		{
    
    
			break;
		}
		send(sockfd,buff,strlen(buff),0);
		memset(buff,0,128);
		recv(sockfd,buff,128,0);
		printf("buff=%s\n",buff);
	}
	exit(0);
}

猜你喜欢

转载自blog.csdn.net/ALITAAAA/article/details/109413541