在前面章节中讲到了如何利用多进程进行并发tcp通信,由于进程开销大,占用资源多,面对现在的高并发的应用场景,可能同时在线人数都是百万千万级别,不可能一个socket连接就fork一个单独的进程处理,所以多进程来处理多tcp连接情况,了解即可。那是不是也需要采用多线程来处理高并发的情况呢,笔者对此研究不多,如果有大神还请指教,再次猜测一下,估计是利用io复用模型结合多线程来处理吧。
现在学习了解一下select如何使用,首先需要了解几个函数,在linux控制台下面man select可以得到select的相关介绍,简介如下:
/*
nfds:最大描述符加1
readfds:需要监控的读的描述符集合
writefds:需要监控的写的描述符集合
exceptfds:需要监控的except的描述符的集合
timeout: 超时时间,如果超时时间设置为NULL,则表示阻塞,直到有消息,如果超时时间设置为0,则表示函数立即返回,
*/
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
FD_ZERO(fd_set *fdset) 将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。
FD_SET(fd_set *fdset) 用于在文件描述符集合中增加一个新的文件描述符。
FD_CLR(fd_set *fdset) 用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset) 用于测试指定的文件描述符是否在该集合中。
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <resolv.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
const int MAXBUF = 1024;
const unsigned int MAXACCEPT = 20;
int acceptfd[MAXACCEPT];
int acceptCount = 0;
// 创建tcp监听套接字并设置
int tcpSocket(int port)
{
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in src;
src.sin_family = AF_INET;
src.sin_port = htons(port);
src.sin_addr.s_addr = htonl(INADDR_ANY);
memset(src.sin_zero, 0, sizeof(src.sin_zero));
int ret = 0;
ret = bind(socketfd, (struct sockaddr *)&src, sizeof(struct sockaddr_in));
if (ret != 0)
{
printf("bind failed ret = %d\n", ret);
close(socketfd);
socketfd = 0;
return 0;
}
listen(socketfd, 1500);
printf("listen socket = %d\n", socketfd);
return socketfd;
}
int main()
{
int serverFD = 0;
serverFD = tcpSocket(10567);
memset(acceptfd, 0, sizeof(acceptfd));
if(acceptfd == 0)
{
printf("create listen socket failed\n");
return -1;
}
fd_set set;
int i = 0;
int maxSocket = 0;
struct timeval timeout = {0, 50000};
clock_t startTime = clock();
clock_t endTime = clock();
char buff[MAXBUF] = {0};
while (1)
{
// 重置监听套接字集合
FD_ZERO(&set);
//存储最大套接字
int socketMax = 0;
FD_SET(serverFD , &set);
socketMax = (serverFD > socketMax ? serverFD : socketMax );
// acceptfd数组中存储的已连接的套接字,循环加入到set集合中
for (int j = 0; j < acceptCount; j++)
{
if (acceptfd[j] != 0)
{
printf("fdset, j = %d, acceptfd[%d] = %d\n", j, j, acceptfd[j]);
FD_SET(acceptfd[j], &set);
socketMax = (acceptfd[j] > socketMax ? acceptfd[j] : socketMax );
}
}
int status = select(socketMax + 1, &set, &set, NULL, NULL);
if (status == -1)
{
printf("select error = %s\n", strerror(errno));
break;
}
else if (status == 0)
{
printf("no avaliable data\n");
continue;
}
// 判断是否可读
if(FD_ISSET(serverFD , &set))
{
//printf("serverFD = %d\n", j,serverFD );
struct sockaddr dst;
i++;
unsigned int sockaddrLen = sizeof(struct sockaddr);
int connectfd = accept(array[j], &dst, &sockaddrLen);
if (connectfd < 0)
{
//printf("connectfd = %d, error = %s\n", connectfd, strerror(errno));
continue;
}
acceptfd[acceptCount] = connectfd;
acceptCount++;
maxSocket++;
printf("accept fd = %d, acceptCount = %u, accept Count = %d\n", connectfd, acceptCount, maxSocket);
}
for (int j = 0; j < acceptCount; ++j)
{
if (FD_ISSET(acceptfd[j], &set))
{
//printf("acceptfd[%d] = %d\n", j, acceptfd[j]);
struct sockaddr dst;
i++;
unsigned int sockaddrLen = sizeof(struct sockaddr);
memset(buff, 0, sizeof(buff));
int ret = recv(acceptfd[j], buff, sizeof(buff), MSG_DONTWAIT);
if((ret < 0) && ((errno == EINTR) || (errno == EWOULDBLOCK) || (errno == EAGAIN)))
{
continue;
}
else if(ret > 0)
{
printf("socket = %d,recv buff = %s, port = %u\n", acceptfd[j], buff, (*(sockaddr_in *)&dst).sin_port);
memset(buff, 0, sizeof(buff));
memcpy(buff, "hello server", strlen("hello server"));
send(acceptfd[j], buff, sizeof(buff), MSG_DONTWAIT);
}
else
{
close(acceptfd[j]);
printf("close acceptfd[%d] = %d\n", j, acceptfd[j]);
FD_CLR(acceptfd[j], &set);
acceptfd[j] = 0;
maxSocket--;
continue;
}
}
}
}
close(serverFD);
serverFD = 0;
return 0;
}