前言
前面我们已经学习了创建套接字的函数socket()和向套接字分配地址的函数bind(),接下来我们将正式讨论如何通过套接字收发数据;同时我们之前也介绍了面向连接和面向消息的2种数据传输方式,这篇博客也将具体讨论面向连接的服务器端/客户端的编写。
理解TCP和UDP
根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字两种(本篇主要讲解TCP);因为TCP套接字是面向连接的,因此又称基于流(stream)的套接字。
这里推荐大家自行了解一下TCP/IP协议栈。
实现基于TCP的服务器端/客户端
TCP服务器端的默认函数调用顺序
绝大部分的TCP服务器端都按照以下的顺序调用函数:
1. socket() ->创建套接字
2. bind() ->分配套接字地址
3. listen() ->等待连接请求状态
4. accept() ->允许连接
5. read()/write() ->数据交换
6. close() ->断开连接
调用socket函数创建套接字,声明并初始化地址信息结构体变量,调用bind函数向套接字分配地址。这两个阶段之前已经讨论过了,下面讲解之后的几个过程。
进入等待连接请求状态
只有调用了listen函数,客户端才能进入可发出连接请求状态。也就是说,这时客户端才能调用connect函数(提前调用将发生错误)。
#include <sys/socket.h>
int listen(int sock, int backlog);
->成功时返回0,失败时返回-1。
* sock 进入等待连接请求状态的套接字(服务器端套接字)文件描述符。
* backlog 连接请求等待队列的长度,若为5,则表示最多使5个连接请求进入队列。
受理客户端连接请求
如果调用accept函数,函数将会自动创建一个新的套接字,用来连接到发起请求的客户端。
#include <sys/socket.h>
int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);
->成功时返回创建的套接字文件描述符,失败时返回-1。
* sock 服务器套接字的文件描述符。
* addr 客户端地址信息的变量地址值。
* addrlen 第二个参数addr结构体的长度,即存有客户端地址长度的的变量地址。
TCP客户端的默认函数调用顺序
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的地址变量长度
客户端调用connect函数后,发生以下情况之一才会返回(完成函数调用)。
1. 服务器端接收连接请求。
2. 发生断网等异常情况而中断连接请求。
总结
TCP服务器端和客户端两者之间是交互的,并非互相独立,下面我们梳理一下整体流程:
服务器端创建套接字后连续调用bind,listen函数进入等待状态,客户端创建套接字后调用connect函数发起连接请求。(注意:客户端只有等到服务器端调用
listen函数后才能调用connect函数)同时要清楚,客户端调用connect函数之前,服务器端有可能率先调用了accept函数。此时,服务器端在调用accept函数
时进入阻塞状态,直到客户端调用connect函数为止。
自此,TCP服务器端及客户端的实现和相关函数的说明就已经介绍完了,具体示例可以回看前面几篇博客给出的demo,若还有不明白的地方,请多加复习。
本篇完结,白白~~