TCP/IP 第4章 基于TCP的服务器端/客户端(1)

本文详细介绍了基于TCP协议的服务器端与客户端的交互过程,包括函数调用顺序、连接请求处理及数据交换原理等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TCP:Transmission Control Protocol,传输控制协议。

实现基于TCP的服务器端/客户端

TCP服务端的默认函数调用顺序

socket()-bind()-listen()-accept()-r/w-close()

进入连接请求状态:

当已经调用bind函数给套接字分配了地址之后,接下来就要通过调用listen函数进入等待连接请求状态。只有调用了listen函数,客户端才能进入可发出连接请求的状态。换言之,这时客户端才能调用connect函数。

#include <sys/socket.h>

int listen(int sock, int backlog);
//成功返回0,失败返回-1

sock:希望进入等待连接请求状态的套接字文件描述符,然后成为服务器端套接字(监听套接字)。

backlog:连接请求队列的长度。

调用listen函数可以生成监听套接字,用来接受客户端连接请求(这本身也是一种数据)。

受理客户端连接请求:

调用listen函数后,若有新的连接请求,则会按序受理。受理请求代表着进入可接受数据的状态。

那么必然就需要一个新的套接字(监听套接字还需要监听着)。accept函数将自动创建套接字,并连接到发起请求的客户端。

#include <sys/socket>

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
//成功是返回创建的套接字文件描述符,失败时返回-1

sock:服务器套接字的文件描述符。

addr:保存发起连接请求的客户端地址信息的变量地址值,调用函数后向传递来的地址变量参数填充客户端地址信息。

addrlen:第二个参数addr结构体的长度,但是存有长度的变量地址。函数调用后,该变量即被填入客户端地址长度。

TCP客户端的默认函数调用顺序

socket()-connect()-r/w-close()

与服务器端相比,区别就在于connect,它是创建客户端套接字后向服务器端发起的连接请求。服务器端调用listen函数后创建连接请求等待队列,之后客户端即可请求连接。

#include <sys/socket.h>

int connect(int sock, struct sockaddr *servaddr, socklen_t addrlen);

//成功返回0,否则-1

sock:客户端套接字文件描述符

servaddr:保存目标服务器端地址信息的变量地址值。

addrlen:servaddr的地址变量的长度

客户端调用connect函数后,发生以下情况之一才会返回。

  • 服务器端接受连接请求
  • 发生断网等异常情况而中断连接请求

需要注意,所谓的“接受连接”并不意味着服务器端调用accept函数,其实是服务器端把连接请求信息记录到等待队列。

因此connect函数返回后并不立即进行数据交换。

注意:客户端套接字的ip地址和端口在调用connect函数时自动分配,无需调用标记的bind函数进行分配。

 

实现迭代服务器端/客户端

简单来说就是反复调用accept函数,使服务器可以反复接受客户端连接请求。

服务器端主要代码:

        for(int i=0;i<5;i++){//为处理5个客户端连接而添加的循环语句。共调用5次函数。
		int clnt_sock = accept(serv_sock,(struct sockaddr *)&clnt_adr,&clint_adr_sz);
		if(clnt_sock==-1) error("accept error");
		else printf("Connected client %d\n",i+1);
		ssize_t len=0;
                //while遇EOF结束
		while((len=read(clnt_sock, message, SIZE))!=0){
			message[len]=0;
			printf("%d:%s\n",++cnt,message);
			write(clnt_sock, message, len);
		}
		close(clnt_sock);//针对套接字调用close函数,向连接的相应套接字发送EOF。
                         //换言之,客户端套接字若调用close函数,则第50行的循环条件变成false,所以往下执行。
	}
	close(serv_sock);//最终关闭监听套接字

客户端主要代码:

        while(1){
		printf("Input Q to exit \n");
		scanf("%s",message);
		if(strcmp(message,"Q\n")==0) break;
		write(sock, message, strlen(message));
		ssize_t len=read(sock, message,SIZE-1);
		message[len]=0;
		printf("Message from server: %s\n",message);
	}
	close(sock);//调用close向相应套接字发送EOF(EOF意味着中断连接)

回声客户端存在的问题:

        

write(sock, message, strlen(message));
ssize_t len=read(sock, message,SIZE-1);
message[len]=0;
printf("Message from server: %s\n",message);

以上代码有个错误假设:

每次调用r/w函数使都会以字符串为单位执行实际的I/O操作。

但是因为TCP不存在数据边界。因此,多次调用write函数传递的字符串有可能一次性传递到服务器端。此时客户端有可能从服务器端收到多个字符串。这不是我们希望看到的结果。也需要考虑服务器端的如下情况:字符串太长,需要分2个数据包发送。

数据太大,操作系统就有可能把数据分成多个数据包发送给客户端。在此过程中,客户端可能在尚未收到全部数据包时就调用read函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值