TCP/IP网络编程学习(4):实现基于TCP的服务端/客户端

# TCP服务端默认调用顺序

  1. socket(): 创建套接字
  2. bind() : 分配套接字的地址
  3. listen() : 等待连接请求状态
  4. accept() : 允许连接
  5. read()/write(): 交换数据
  6. close(): 关闭连接

我们已调用 bind函数给套接字分配了地址,接下来就要通过调用listen函数进入等待连接请求状态 。 只有调用了listen函数,客户端可发出连接请求。换言之,这时客户端才能调用connect函数(若提前调用将发生错误)。

#include <sys/socket.h>
int listen(int sock, int backlog);
//成功时返回 0 ,失败时返回-1
//sock 希望进入等待连接请求状态的套接字文件描述符,传递的描述符套接字参数成为服务器端套接字(监听套接字) 。
//backlog 连接请求等待队列( Queue )的长度,若为5 ,则队列长度为 5 ,表示最多使5个连接请求进入队列 。

服务器端处于等待连接请求状态是指,客户端请求连接时,受理连接前一直使请求处于等待状态 。

在这里插入图片描述
服务端处于等待连接请求状态时,客户端的所有连接请求信息都通过套接字连接到服务端的连接请求等待队列上。

受理客户端连接请求

连接请求等待队列上有请求时,服务端就进行受理,进入可接受数据的状态。服务器端创建的套接字用来接受客户端的连接请求。当调用accept()函数后自动创建套接字用于连接到客户端进行数据传输。如果队列中没有请求,accept()函数将阻塞到有请求。

#include <sys/socket.h>
int accept(int sock, struct sockaddr '* addr, socklen_t * addrlen);
// 成功时返回创建的套接字文件描述符,失败时返回-1
//sock: 服务器套接字的文件描述符。
//addr: 保存发起连接请求的客户端地址信息的变量地址值,调用函数后向传递来的地址充客户端地址信息。
//addrlen: 第二个参数addr结构体的长度量, 但是存有长度的变量地址。函数调用完成后,该变量即被填入客户端地址长度。

accept函数受理连接请求等待 队列中待处理的客户端连接请求 。 函数调用成功时accept函数内部将产生用于数据I/O的套接字并返回其文件描述符。需要强调的是,套接字是自动创建的,并自动与发起连接请求的客户端建立连接 。
在这里插入图片描述

客户端的调用顺序

  1. socket():创建套接字
  2. connect():请求连接
  3. read()/write():交换数据
  4. close():断开连接
#include <sys/socket.h>
int connect(int sock , struct sockaddr * servaddr, socklen_t addrlen);
//成功时返回 0 ,失败时返回-1
//sock: 客户端套接字文件描述符。
//servaddr: 保存目标服务器端地址信息的变量地址值
//addrlen: 以字节为单位传递已传递给第二个结构体参数servaddr的地址变量长度。

服务器端接受连接将连接放入连接请求等待队列
在这里插入图片描述

基于 TCP 的服务器端/客户端函数调用关系

服务器端创建套接字后连续调用bindlisten函数进入等待状态,客户端通过调用connect函数发起连接请求。需要注意的是,客户端只能等到服务器端调用 listen函数后才能调connect函数。同时要清楚,客户端调用 connect函数前,服务器端有可能率先调用accept函数 。当然,此时服务器端在调用 accept函数时进入阻塞 ( blocking ) 状态, 直到客户端调connect函数为止 。
在这里插入图片描述

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

编写回声(echo)服务端、客户端。服务器将客户端收到的字符串数据原封不动的传回客户端。

服务器端在同一时刻只与一个客户端相连,并提供回声服务 。
服务器端依次向 5个客户端提供服务并退出 。
客户端接收用户输入的字符串并发送到服务器端 。
服务器端将接收的字符串数据传 回客户端,即"回声 " 。
服务器端与客户端之间的字符串回声一直执行到客户端输入Q为止 。

服务器端代码:

for(i=0; i<5; i++){
	//每一次服务一个客户端,上一个关闭后阻塞到下一个客户端连接
	clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
	if(clnt_sock==-1)
		error_handling("accept() error");
	else
		printf("Connected client %d \n", i+1);
	//读取客户端发过来的信息
	while((str_len=read(clnt_sock, message, BUF_SIZE))!=0)
		write(clnt_sock, message, str_len);//将信息写回客户端,直到客户端关闭
	close(clnt_sock);//关闭与该客户端连接的套接字。
}

客户端代码:

while(1) //循环读取用户输入的数据
{
	fputs("Input message(Q to quit): ", stdout);
	fgets(message, BUF_SIZE, stdin);
	
	if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
		break;

	write(sock, message, strlen(message));
	str_len=read(sock, message, BUF_SIZE-1);
	message[str_len]=0;
	printf("Message from server: %s", message);
}
close(sock)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值