**关于TCP编程中的流式套接字说明**
TCP编程中的流式套接字sockfd分为两种类型:主动生成和被动生成。主动生成是指通过socket函数生成的流式
套接字。服务器端通过socket函数创建的流式套接字通过bind函数变为监听套接字之后用于监听和接收客户端的连接
请求;客户端通过socket函数创建的流式套接字用于向服务器发送连接请求。被动生成的套接字是当客户端向服务器
发送连接请求之后,服务器通过accept函数接收连接请求时返回的通信套接字,它主要用于服务器与客户端之间的通
信息。
监听套接字:是服务器端用于监听请求队列的套接字,不能用于和客户端之间的通信。socket函数创建的流式
套接字通过bind函数之后变为监听套接字。
通信套接字:它是accept函数的返回值,用于服务器与客户端之间的通信。多个客户端与服务器之间进行通信
是在不同的通信套接字上进行的。假设有3个客户端与服务器之间进行通信,则服务器上有4个套接字文件描述符。其
中一个监听套接字,3个通信套接字。
流式套接字、监听套接字、通信套接字的区分主要是为了进行分工。
服务器和客户端使用TCP的流程如下:
1、socket函数
函数原型:int socket(int family, int type, int protocol);
函数功能:创建一个socket流式套接字。
函数参数:family 协议簇,一般用宏来表示。
AF_INET,表示使用IPV4协议
AF_INET6,表示使用IPV6协议
type 套接字的类型,通常使用一下三种类型。
SOCK_STREAM,流式套接字,常用于TCP通信中。
SOCK_DGRAM,数据报套接字,常用于UDP通信中。
SOCK_RAW,原始套接字,一般不直接使用,而是用于定义一个协议。
protocol 套接字对应的编号,一般常用0(除了原始套接字外)。一般指定了具体的套接字类型的话
就不用再给定套接字对应的编号,但是原始套接字除外。
返回值:成功,则返回创建的流式套接字;失败,则返回-1。
2、bind函数
函数原型:int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
函数功能:将服务器的流式套接字与服务器的地址信息进行绑定,流式套接字转变为监听套接字。
函数参数:sockfd 通过socket函数创建的流式套接字
my_addr 需要绑定的服务器的地址信息
addrlen 地址长度
返回值:成功,则返回0;失败,则返回-1。
说明:struct sockaddr 是通用地址类型,如果使用IPV4协议进行通信,则需要将struct sockaddr_in进行强制
转换,转换为struct sockaddr类型。
3、listen函数
函数原型:int listen(int listen_fd, int backlog);
函数功能:监听客户端的连接请求
函数参数:listen_fd 服务器端监听套接字文件描述符
backlog 请求队列中允许的最大请求数
返回值:成功,则返回0;失败,则返回-1
4、accept函数
函数原型:int accept(int listen_fd, struct sockaddr_in *addr, socklen_t *addr);
函数功能:接收请求队列中客户端的连接请求,如果请求队列中无请求,则accept函数一直阻塞。
函数参数:listen_fd 监听套接字文件描述符
addr 用来保存客户端的地址信息
addrlen 地址长度指针
函数返回值:成功,则返回被动分配的通信套接字用于服务器与客户端之间进行通信。
失败,则返回-1。
5、connect函数
函数原型:int connect(int client_fd, struct sockaddr *serv_addr, int addrlen);
函数功能:客户端通过connect函数向服务器发送连接请求
函数参数:client_fd 客户端通过socket函数创建的流式套接字
serv_addr 存放服务器的地址信息,用于去连接服务器
addrlen 地址长度
返回值:成功,则返回0;失败,则返回-1
6、recv、read函数
1、read函数:
函数原型:size_t read(int fd, void *buf, size_t count);
函数功能:“期望”从文件描述符fd相关联的文件中读取count个字节的数据存放在缓冲区buf中。
参数:fd 文件描述符
buf 存放读取到的数据的缓冲区首地址
count “期望”读取的字节数
返回值:> 0 表示成功读到的字节数
=0 表示读取到了文件的尾部
-1 读取失败,返回-1,并设置errno
(1)read函数说明:
1)读取某个文件的方式
while(ret > 0){
读取文件;
}
分析:当读取到文件的尾部时ret = 0;当读取文件失败ret == -1;因此读取某个文件的条件是ret > 0
2)read读取文件至少循环两次:
首先,把文件中的所有数据全部读取完毕,返回读取到的字节数;其次,从文件尾部开始读取,读到了文件
末尾,返回0。
2、recv函数
函数原型:int recv(int connect_fd, void *buf, int size, unsigned int flags);
函数功能:读取通信套接字中的数据
函数参数:connect_fd 通信套接字文件描述符
buf 存放接收数据的缓冲区
size “期望”接收数据的大小。一般 sizeof(buf)-1
flags 标志位,一般为0,以阻塞的方式接收数据;
0,表示以阻塞的方式接收数据;
MSG_DONTWAIT,表示以非阻塞的方式接收数据
MSG_WAITALL,表示“期望”接收size个字节的数据就必须接收这么多
返回值:>0,表示实际接收到的数据字节数;
=0,表示对方断开了连接(通信的另一方断开了连接)
=-1,表示本地接收数据发生了错误
7、send、write函数
1、write函数:
函数原型:size_t write(int fd, const void *buf, size_t count);
功能:“期望”将缓冲区buf中存放的数据中的count个字节写入到与文件描述符fd相关联的文件中。
参数:fd 文件描述符
buf 数据存放的缓冲区
count “期望”向文件中写入的字节数目,一般count = strlen(buf),有效写入。
返回值:成功,返回实际写入到文件中的字节数;失败,返回-1,并设置errno。
2、send函数:
函数原型:int send(int connect_fd, const void *buf, int len, int flags);
函数功能:向通信套接字中写入数据
函数参数:connect_fd 通信套接字
buf 存放发送数据的缓冲区
len 发送数据的长度
flags 标志位,一般为0,以阻塞的方式写入数据。
0以阻塞的方式发送数据
MSG_DONTWAIT,表示以非阻塞的方式接收数据
MSG_NOSIGNAL,表示当连接异常的时候别发送信号
返回值:>0,表示实际发送数据的字节数;
=0,表示连接断开(通信的另一方断开了连接)
=-1,表示数据发送失败(自己的问题)
例1:服务器进程接收数据、客户端进行发送数据,实现基于IPV4、TCP的进程间通信。
服务器端代码:
客户端代码:
运行结果分析如下: