精选 select 模型:实现服务器端和客户端代码 (select模型服务器端和客户端代码)
精选 SELECT 模型:实现服务器端和客户端代码
作为现代计算机应用开发中最广泛使用的两个平台,服务器和客户端是解决客户和服务交互的重要基础。而实现这两个平台的代码,则必须是高度灵活和功能强大的,以满足不同应用的需要。为此,开发人员需要使用一种可靠,可拓展并且强大的模型。
SELECT模型是一种高效的模型,它是开发人员用于创建高度可扩展,可伸缩和底层 I/O 传输的关键工具。通过数据流聚合输入为套接字的事件,SELECT模型允许开发人员以非阻止或异步方式获取数据,并向客户端提供可用。
在本文中,我们将深入研究SELECT模型的原理,并演示如何使用它来构建服务器和客户端代码。
SELECT模型的原理
SELECT模型是Linux和Windows操作系统中很常见的系统调用。该模型通过一种集中的单线程事件循环机制来管理多个套接字连接。当SELECT模型开始监听套接字时,它会等待客户端发送数据,而不会阻塞服务列表中的事件。
选择模型涉及使用select系统调用来侦听I/O事件,如读取和写入。从而使一个I/O流被处理,而不需要使线程一直阻塞在该操作上,同时它还可进行轮询等操作。
SELECT模型是管理现代计算机应用程序中多个连接的一种有效方式。换句话说,它使一个进程能够同时监听多个套接字,实现跨网络连接的数据传输。
如下所示,SELECT模型的主要组件:
1. 文件描述符列表:用于存储套接字描述符。
2. 输入类型:用于存储套接字的输入类型(读取,写入,错误)。
3. 时间值:指示SELECT模型应该等待事件的时间戳,以避免频繁检查描述符表。
SELECT模型有优秀的伸缩性,因为其设计支持处理许多并发的网络连接。 它的工作原理如下:
SELECT模型将套接字添加到描述符列表中。然后,它轮流等待事件,并选择发生I/O事件的描述符。一旦有一个连接事件发生,接收数据的套接字就准备好处理数据。然后,开发人员可以执行必要的操作来处理数据。SELECT模型更新时间值并检查下一个事件。
使用SELECT模型构建服务器端代码
在这个案例中,我们将使用SELECT模型来开发一个简单的Web服务器。 我们声明必要的库:
“`
#include
#include
#include
#include
#include
#include
#include
#include
“`
声明主要变量
创建描述符表fd_set来保存所有Socket,并建立一个监听套接字serverfd:
“`
fd_set master_fds, fds;
int max_sockfd, clientfd [1024], max_client = -1, sockfd, len;
char buffer[1025];
struct sockaddr_in serveraddr, clientaddr;
“`
初始化描述符表fd_set
使用FD_ZERO初始化描述符表fd_set,并将serverfd添加到主描述符表中:
“`
FD_ZERO(&master_fds);
FD_SET(serverfd, &master_fds);
“`
监听套接字serverfd
使用listen函数将serverfd绑定在给定的IP地址和端口号上。
“`
serverfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(PORT);
serveraddr.sin_addr.s_addr = htons(INADDR_ANY);
bind(serverfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
listen(serverfd, 5);
“`
使用fd_set描述符来接受传入套接字连接
使用FD_ISSET函数检查用于接收连接的描述符和主描述符。然后,使用accept函数接受连接,将连接套接字添加到connfd中。在客户端连接列表中存储该客户端套接字。
“`
while (true) {
fds = master_fds;
if (select(max_sockfd + 1, &fds, NULL, NULL, NULL) == -1) {
perror(“select”);
exit(EXIT_FLURE);
}
for (sockfd = 0; sockfd
if (FD_ISSET(sockfd, &fds)) {
if (sockfd == serverfd) {
len = sizeof(clientaddr);
clientfd[max_client + 1] = accept(serverfd, (struct sockaddr *)&clientaddr, &len);
if(clientfd[max_client + 1] == -1) {
perror(“accept”);
} else {
FD_SET(clientfd[max_client + 1], &master_fds);
if (clientfd[max_client + 1] > max_sockfd) {
max_sockfd = clientfd[max_client + 1];
}
if (max_client
max_client = max_sockfd;
}
printf(“Accept connection from %s on port %d\n”, inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
}
} else {
if ((len = recv(sockfd, buffer, sizeof(buffer), 0))
if (len == 0) {
printf(“Connection closed with Client with server FD: %d\n”, sockfd);
} else {
perror(“recv”);
}
close(sockfd);
FD_CLR(sockfd, &master_fds);
if (sockfd == max_sockfd) {
while (FD_ISSET(max_sockfd, &master_fds) == false) {
max_sockfd -= 1;
}
}
} else {
buffer[len] = ‘\0’;
printf(“Received data from Client with Server FD: %s\n”, buffer);
}
}
}
}
}
“`
使用SELECT模型构建客户端代码
在这个案例中,我们将使用SELECT模型来开发一个简单的客户端,它连接到同一SERVER FD并发送字符串。
声明必要的库:
“`
#include
#include
#include
#include
#include
#include
#include
#include
#include
“`
声明变量
然后,声明需要的变量,包括套接字描述符clientfd和描述符列表fds:
“`
int sockfd, len;
struct sockaddr_in serveraddr;
char buffer[1025];
fd_set fds;
“`
建立套接字
通过socket函数建立TCP套接字。
“`
clientfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serveraddr, 0, sizeof(serveraddr));
“`
设置服务器地址
使用inet_pton函数将IP地址转换为网络字节序,并将其存储在服务器地址结构体中。
“`
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(ipaddr); //IP地址
serveraddr.sin_port = htons(portno); //端口号
“`
与服务器建立连接
使用connect函数与服务器建立连接。
“`
if (connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))
perror(“Connect()”);
exit(EXIT_FLURE);
}
“`
发送消息
使用FD_ZERO,FD_SET和FD_ISSET等函数,通过SELECT模型检查描述符列表。在建立连接后,使用write函数将要发送的消息存储在buffer中,并将其发送给服务器。
“`
while(strncmp(buffer, “END”, strlen(buffer)-1) != 0) {
FD_ZERO(&fds);
FD_SET(clientfd, &fds);
FD_SET(STDIN_FILENO, &fds);
select(clientfd + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(STDIN_FILENO, &fds)) {
fgets(buffer, sizeof(buffer), stdin);
len = strlen(buffer);
if (write(clientfd, buffer, len) != len ) {
perror(“Write() error\n”);
exit(EXIT_FLURE);
}
}
}
“`
关闭连接
使用close函数关闭客户端套接字。
“`
printf(“Exiting client.\n”);
close(clientfd);
return 0;
“`
结论