前传02 | 线程模型
一、Socket模型
使用 Socket 模型实现网络通信时,需要经过创建 Socket、监听端口、处
理连接和读写请求等多个步骤。
首先,当我们需要让服务器端和客户端进行通信时,可以在服务器端通过以下三步,来创建监听客户端连接的监听套接字(Listening Socket):
- 调用 socket 函数,创建一个套接字。我们通常把这个套接字称为主动套接字(Active
Socket); - 调用 bind 函数,将主动套接字和当前服务器的 IP 和监听端口进行绑定;
- 调用 listen 函数,将主动套接字转换为监听套接字,开始监听客户端的连接。
- 调用accept 函数,在完成上述三步之后,服务器端就可以接收客户端的连接请求了。为了能及时地收到客户端的连接请求,我们可以运行一个循环流程,在该流程中调用 accept 函数,用于接收客户端连接请求。
- 最后,服务器端可以通过调用 recv 或 send 函数,在刚才返回的已连接套接字上,接收并处理读写请求,或是将数据发送给客户端。
单线程模型:
listenSocket = socket(); //调用socket系统调用创建一个主动套接字
bind(listenSocket); //绑定地址和端口
listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,也就是监听套接字
while (1) { //循环监听是否有客户端连接请求到来
connSocket = accept(listenSocket); //接受客户端连接
recv(connsocket); //从客户端读取数据,只能同时处理一个客户端
send(connsocket); //给客户端返回数据,只能同时处理一个客户端
}
多线程模型,使用多线程来提升服务器端的并发客户端处理能力。
listenSocket = socket(); //调用socket系统调用创建一个主动套接字
bind(listenSocket); //绑定地址和端口
listen(listenSocket); //将默认的主动套接字转换为服务器使用的被动套接字,即监听套接字
while (1) { //循环监听是否有客户端连接到来
connSocket = accept(listenSocket); //接受客户端连接,返回已连接套接字
pthread_create(processData, connSocket); //创建新线程对已连接套接字进行处理
}
//处理已连接套接字上的读写请求
processData(connSocket){
recv(connsocket); //从客户端读取数据,只能同时处理一个客户端
send(connsocket); //给客户端返回数据,只能同时处理一个客户端
}
二、IO多路复用(Reactor的技术实现)
功能差异:
select机制与使用
Linux 针对每一个套接字都会有一个文件描述符,也就是一个非负整数,用来唯一标识该套接字。所以,在多路复用机制的函数中,Linux 通常会用文件描述符作为参数。有了文件描述符,函数也就能找到对应的套接字,进而进行监听、读写等操作。
select函数:
select 机制中的一个重要函数就是 select 函数。
int select (int __nfds, fd_set *__readfds, fd_set *__writefds, fd_set *__excep, int *__timeout)
参数说明:
1)监听的文件描述符数量,__nfds
2)被监听描述符的三个集合,__readfds、__writefds和*__exceptfds
__readfds:读数据事件
__writefds:写数据事件
__exceptfds:异常事件
3)监听时阻塞等待的超时时长,*__timeout
# fd_set 结构体
## __fd_mask类型是 long int 类型的别名,
## __FD_SETSIZE 和 __NFDBITS 这两个宏定义的大小默认为 1024 和 32
typedef struct {
…
__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
…
} fd_set
所以,fd_set 结构体的定义,其实就是一个 long int 类型的数组,该数组中一共有 32 个
元素(1024/32=32),每个元素是 32 位(long int 类型的大小),而每一位可以用来表
示一个文件描述符的状态。
使用 select 函数,进行并发客户端处理的关键步骤和主要函数调用:
int sock_fd,conn_fd; //监听套接字和已连接套接字的变量
sock_fd = socket() //创建套接字
bind(sock_fd) //绑定套接字
listen(sock_fd) //在套接字上进行监听,将套接字转为监听套接字