基于TCP的服务器端和客户端

在之前我们已经学过了如何创建套接字和分配地址端口,接下来我们主要说说如何收发数据,这一篇主要讲面向连接的传输方式。

1、理解TCP/UDP
TCP是Transmission Control Protocol(传输控制协议)的简写,意为“对数据传输过程的控制”

在这里插入图片描述
可以看出,TCP/IP协议栈又分为四层,分层可以将大问题化解成小问题
;当然更重要的原因是,通过标准化设计开放式系统,在技术日益发展的今天,所有人遵循一种标准,才能持久发展。编程中面向对象的方法也是标准化思想的体现。

链路层负责硬件标准,ip层负责路径选择,但不保证数据可靠传输。TCP对于丢失的数据会重传,确认机制保证了数据传输的可靠性。
上面这些都是套接字内部自动实现的,网络编程的大部分内容就是设计应用层协议。

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

TCP服务器端的默认函数调用顺序
在这里插入图片描述
调用socket函数创建套接字,声明并初始化地址信息结构体变量,调用bind函数向套接字分配地址。这两个过程之前讨论过,下面讲一下之后的过程。

(3)进入等待连接请求状态
我们已调用bind函数给套接字分配了地址,接下来就要通过调用listen函数进入等待连接请求状态。只有调用了listen函数,客户端才能进入可发出连接请求的状态(客户端才能调用connect函数,不然会发生错误)。

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

客户端连接请求本身也是从网络中接收到的一种数据,而要想接收就需要套接字。此任务就由服务器端套接字完成,即第一个参数。

(4)受理客户端连接请求

调用listen函数后,若有新的连接请求,则应按序受理。受理请求意味着进入可接收数据的状态。那么我们又需要用到套接字了!大家可能认为可以使用服务器端套接字,但服务器端套接字是做门卫的。如果在与客户端的数据交换中使用门卫,那就没办法受理下一个连接请求并领到等候室去了。因此需要另一个套接字,但没有必要亲自创建。下面函数将自动创建套接字,并连接到发起请求的客户端。

#include <sys/socket.h>
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
/*
 * 成功时返回创建的套接字文件描述符,失败时返回-1
 * sock 服务器套接字的文件描述符
 * addr 保存发起连接请求的客户端地址信息的变量地址值,调用函数后向传递来的地址变量参数填充客户端地址信息
 * 

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

3、TCP客户端的默认函数调用顺序
创建套接字和请求连接就是客户端的全部内容。与服务器端相比,区别就在于“请求连接“,它是创建客户端套接字后向服务器端发起的连接请求。服务器端调用listen函数后创建连接请求等待队列,之后客户端即可请求连接。

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

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

服务器端接收连接请求。
发生断网等异常情况而终端连接请求。
需要注意,所谓”接收连接“并不意味着服务器端调用accept函数,其实是服务器端把连接请求信息记录到等待队列。因此connect函数返回后并不立即进行数据交换。

客户端套接字地址信息在哪?

何时? 调用connect函数时。

何地? 操作系统,准确地说实在内核中。

如何? IP用计算机的IP,端口随机。

客户端的IP地址和端口在调用connect函数时自动分配,无需调用标记的bind函数进行分配。

4、基于TCP的服务器端/客户端函数调用关系
在这里插入图片描述
5、实现迭代服务器端/客户端
之前我们读取一个数据后就退出服务了,我们可以服务一次后关闭这个客户端的套接字,但是服务器端套接字还在,还可以循环调用accept函数,然后接受其他客户端的请求,每次又把服务器端接受的数据传输给客户端。
服务器端在同一时刻只与一个客户端相连,并提供回身服务。

服务器端依次向5个客户端提供服务并退出。

客户端接收用户输入的字符串并发送到服务器端。

服务器端将接收的字符串数据传回客户端

服务器端与客户端之间的字符串回声一直执行到客户端输入Q为止。
在这里可能会面临数据一次传输不完,写多次读一次的情况,这不是我们希望看到的结果,因为面向连接的TCP是没有数据边界的,如何解决?在下面一篇会给出答案。

猜你喜欢

转载自blog.csdn.net/weixin_53344209/article/details/114065643