linux中网络编程I/O模型—select(非阻塞模型)
-
select函数原型:
int select(int maxfdp, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)
这里用到两个结构体fd_set和timeval。fd_set可以理解为一个集合,时存放文件描述符的(想STDOUT_FILENO、STDIN_FILENO以及socket函数的返回的文件的句柄)
select参数列表:
maxfd:指集合中所有文件描述负的范围,即所有文件描述符的最大值加1
readfds:监视可读的文件描述符的集合,即readfds中有文件可读,select返回一个大于0的值,在根据timeout判断是否超时,超时返回0,发生错误返回负数。
writefds:监视可读文件描述集合
errorfds:监视文件描述符异常的集合
timeout:设置超时时间 -
fd_set相关的函数:
fd_set set; //初始化
FD_ZERO(&set); //清空
FD_SET(fd, &set); //添加文件描述符到set
FD_CLR(fd, &set); //将fd从set中删除
FD_ISSET(fd, &set); //检查set中fd是否可读 -
下面使用select模型实现一个简单的server、client
server.cpp
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <errno.h>
#define DEFAUT_PORT 6666
int main()
{
int serverfd; //监听socket:serverfd;数据传输socket:acceptfd
struct sockaddr_in my_addr; //本机地址信息
unsigned int lisnum = 10;
//初始化流式socket
if ((serverfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
return -1;
}
printf("socket ok\n");
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(DEFAUT_PORT); //将主机字节序的监听端口转换成网络字节序
my_addr.sin_addr.s_addr = INADDR_ANY; //监听来自所有网络的消息
bzero(&(my_addr.sin_zero), 0);
//将需要监听的socket:serverfd与本地主机绑定
if (bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind");
return -2;
}
printf("bind ok\n");
if (listen(serverfd, lisnum) == -1)
{
perror("listen");
return -3;
}
printf("listen ok\n");
fd_set client_fdst; //监控文件描述符集合
int maxsock; //监控文件描述符中最大的文件号
struct timeval tv; //超时返回时间
int client_sockfd[5]; //存放存活的sockfd
bzero((void*)client_sockfd, sizeof(client_sockfd));
int conn_amount = 0; //记录描述符数量
maxsock = serverfd;
char buffer[1024];
int ret = 0;
while (1)
{
//初始化文件描述符到集合
FD_ZERO(&client_fdst);
//将需要监控的服务器socket:serverfd添加到文件描述符集合
FD_SET(serverfd, &client_fdst);
//设置超时时间
tv.tv_sec = 30;
tv.tv_usec = 0;
//将活动的句柄添加到集合
for (int index = 0; index < 5; index++)
{
//程序listen的参数为5,故index必须小于5
if (client_sockfd[index] != 0)
{
FD_SET(client_sockfd[index], &client_fdst);
}
}
ret = select(maxsock + 1, &client_fdst, NULL, NULL, &tv);
if (ret < 0)
{
perror("select error");
break;
}
else if (ret == 0)
{
printf("timeout!\n");
continue;
}
//轮询所有的文件描述符
for (int index = 0; index < conn_amount; index++)
{
//FD_ISSET检查client_sockfd是否可读, >0可读写
if (FD_ISSET(client_sockfd[index], &client_fdst))
{
printf("start recv from client[%d]:\n", index);
ret = recv(client_sockfd[index], buffer, sizeof(buffer), 0);
if (ret <= 0)
{
printf("client[%d] close\n", index);
close(client_sockfd[index]);
//将文件描述符从集合中删除
FD_CLR(client_sockfd[index], &client_fdst);
client_sockfd[index] = 0;
--conn_amount;
}
else
{
printf("recv from client[%d] : %s\n", index, buffer);
}
}
}
//检查是否有新链接,如果有新连接,添加到client_sockfd集合中
if (FD_ISSET(serverfd, &client_fdst))
{
struct sockaddr_in client_addr;
size_t size = sizeof(struct sockaddr_in);
int sock_client = accept(serverfd, (struct sockaddr*)(&client_addr), (unsigned int*)(&size));
if (sock_client < 0)
{
perror("accept error");
continue;
}
//把链接添加到集合中
if (conn_amount < 5)
{
client_sockfd[conn_amount++] = sock_client;
bzero(buffer, sizeof(buffer));
strcpy(buffer, "This is server! Welcome!\n");
send(sock_client, buffer, sizeof(buffer), 0);
printf("New connection clinet[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
printf("new client : %d\n", sock_client);
printf("new maxsock : %d\n", maxsock);
bzero(buffer, sizeof(buffer));
ret = recv(sock_client, buffer, sizeof(buffer), 0);
if (ret < 0)
{
perror("recv error!\n");
close(serverfd);
return -1;
}
printf("recv : %s\n", buffer);
if (sock_client > maxsock)
{
maxsock = sock_client;
}
else
{
printf("max connections!!!quit!\n");
break;
}
}
}
}
for (int index = 0; index < 5; index++)
{
if (client_sockfd[index] != 0)
{
close(client_sockfd[index]);
}
}
close(serverfd);
return 0;
}
client.cpp
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#define DEFAUT_PORT 6666
int main(int argc, char *argv[])
{
int connfd = 0;
int clen = 0;
struct sockaddr_in client;
if (argc < 2)
{
printf("Uasge : Client [Server IP address]\n");
return -1;
}
client.sin_family = AF_INET;
client.sin_port = htons(DEFAUT_PORT);
client.sin_addr.s_addr = inet_addr(argv[1]);
connfd = socket(AF_INET, SOCK_STREAM, 0);
if (connfd < 0)
{
perror("socket");
return -1;
}
if (connect(connfd, (struct sockaddr*)&client, sizeof(struct sockaddr)) < 0)
{
perror("connect");
return -1;
}
char buffer[1024];
bzero(buffer, sizeof(buffer));
recv(connfd, &buffer, sizeof(buffer), 0);
printf("recv : %s\n", buffer);
bzero(buffer, sizeof(buffer));
strcpy(buffer, "this is client!\n");
send(connfd, buffer, sizeof(buffer), 0);
while (1)
{
bzero(buffer, sizeof(buffer));
scanf("%s", buffer);
int p = strlen(buffer);
buffer[p] = '\n';
send(connfd, buffer, sizeof(buffer), 0);
printf("i have send buffer\n");
}
close(connfd);
return 0;
}