4.1 理解TCP和UDP
TCP是TransmissionControl Protocol(传输控制协议)的简写。
以多个标准为依据设计的系统称为开放式系统。
TCP和UDP层以IP层提供的路径信息为基础完成实际的数据传输,故该层又称传输层(Transport)。
IP层是面向消息的、不可靠的协议。只关注1个数据包(数据传输的基本单位)的传输过程。若只利用IP层传输数据,则有可能导致数据丢失或者数据包收、发顺序不一致。TCP和UDP存在于IP层之上,决定主机之间的数据传输方式。
编写软件的过程中,需要根据程序特点决定服务器和客户端之间的数据传输规则(规定),这便是应用层协议。网络编程的大部分内容就是设计并实现应用层协议。
4.2 实现基于TCP的服务器端/客户端
1. TCP端的默认函数调用顺序
进入等待连接请求状态:
#include <sys/socket.h>
int listen(int sock, int backlog);
-sock: 希望进入等待连接请求状态的套接字文件描述符,传递的描述符套接字参数成为服务器端套接字(监听套接字);
-backlog: 连接请求等待队列(Queue)的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列;连接请求队列的大小始终根据实验结果而定。
受理客户端连接请求:
#include<sys/socket.h>
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
-sock: 服务器套接字的文件描述符;
-addr: 保存发起连接的客户端地址信息的变量地址值,调用函数后向传递来的地址变量参数填充客户端地址信息;
-addrlen: 第二个参数addr的长度,注意是该参数是指针变量,应传入长度变量的地址;
accept函数受理连接请求等待队列中待处理的客户端连接请求。函数调用成功后,accept函数内部将产生用于数据I/O的套接字,并返回其文件描述符。
另外,调用accept函数时,若等待队列为空,则accept函数不会返回,直到队列中出现新的客户端连接。
2. TCP客户端的默认函数调用顺序
与服务器端相比,区别就在于“请求连接”,服务器端调用listen函数后创建连接请求等待队列,之后客户端即可请求连接。
#include <sys/socket.h>
int connect(int sock, struct sockaddr*servaddr, socklen_t addrlen);
-sock: 客户端套接字文件描述符;
-servaddr: 保存目标服务器端地址信息的变量地址值;
-addrlen: 以字节为单位传递已传递给第二个结构体参数servaddr的地址变量长度;
客户端调用connect函数后,发生以下情况之一才会返回:
A.服务器端接收连接请求(服务器端将请求消息记录到等待队列),因此,connect函数返回后并不立即进行数据交换;
B.发生断网等异常情况而中断连接请求;
网络数据交换必须分配IP和端口,客户端的IP地址和端口在调用connect函数时自动分配(操作系统,内核),端口号随机分配,无需调用标记的bind函数进行分配。
3. 基于TCP的服务器端/客户端函数调用关系
4.3 实现迭代服务器端/客户端
1. 迭代回声服务器端/客户端
程序运行方式:
服务器端在同一时刻只与一个客户端相连,并提供回声服务;
服务器端依次为5个客户端连接提供服务并退出;
客户端接收用户输入的字符串并发送到服务器端;
服务器端将接收的字符串数据传回客户端,即“回声”;、
服务器端与客户端之间的字符串回声一直执行到客户端输入Q为止;
2. 回声客户端存在的问题
多次调用write函数传递的字符串有可能一次性传递到服务器端,此时客户端有可能从服务器端收到多个字符串,这不是希望的结果。