select多路复用实现多客户端连接服务器
程序员文章站
2024-03-23 09:40:34
...
.>应用程序中同时需要处理多路输入输出流时,若采用阻塞模式,将得不到预期的目的;
.>若采用非阻塞模式,对多个输入进行轮训有太耗费时间;
.>若设置多个进程分别处理一天数据通路,将产生新的进程同步通信问题,使程序更复杂;
比较好的方法就是采用多路复用,其基本思想就是:
》》》先创建一张有关描述符的的表,然后调用一个函数,当这些文件描述符中的一个或者多个已经准备好进行IO操作时函数才返回;
》》》函数返回时告诉进程哪个描述符已就绪,可以进行IO操作;
函数原型:
int select(int nfds,fd_set *readset,fd_set *writeset,fd_set* exceptset,struct timeval *timeout);
nfds: 第一个参数是:最大的文件描述符值+1;
readset: 可读描述符集合;
writeset: 可写描述符集合;
exceptset: 异常描述符;
timeout:select 的监听时长,如果这短时间内所监听的 socket 没有事件发生。
设置文件描述符的几个宏:
FD_ZERO(fd_set *) 从fd_set中清除所有文件描述符;
FD_SET(int fd, fd_set *) 将fd添加到fd_set中;
FD_CLR(int fd, fd_set *) 将fd从fd_set中清除;
FD_ISSET(int fd, fd_set *) 判断fd是否在fd_set中;
通俗点解释,就是将需要监听的文件描述符添加到fd_set这个文件描述符集合中,然后用宏函数FD_ISSET()判断该文件描述符是否存在,因为进行循环后只有有需要处理的文件描述符才会留在这个文件描述符集合中,否则就会被清除,在对相应描述符进行操作;
//tcp 多路复用服务器端
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#define SIZE 32
#define IP "127.0.0.1"
#define PORT 54321
#define LISTENNUM 5
enum SOCK_OP
{
ERROR = -1,
SUCCESS = 0,
};
int main()
{
int socketID = 0;
int maxFd = 0;
int ret = 0;
int newID = 0;
fd_set readFds;
int addrLength = 0;
struct sockaddr_in addr;
char buf[SIZE] = {0};
//init socket
socketID = initSocket();
if (0 > socketID)
{
printf("socket init error\r\n");
return ERROR;
}
printf("init socket success\r\n");
addrLength = sizeof(addr);
//多路复用
maxFd = socketID;
FD_ZERO(&readFds);
FD_SET(socketID, &readFds);
while(1)
{
//设定:把所有要监听的描述符都放到监听集合中
fd_set tmp = readFds;
ret = select(maxFd + 1, &tmp, NULL, NULL, NULL);
if (0 > ret)
{
perror("select error");
return ERROR;
}
else if (0 == ret)
{
printf("select time out\r\n");
}
else
{
//判断哪一个描述符可读
int i = 0;
for (i = 0; i <= maxFd; i++)
{
if (FD_ISSET(i, &tmp))
{
if (i == socketID)
{
memset(&addr, 0, addrLength);
newID = accept(socketID, (struct sockaddr *)&addr, &addrLength);
if (0 > newID)
{
perror("accept error");
return ERROR;
}
printf("client %d connected, IP=%s,port=%u\r\n", newID, (char *)inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
//把newID加入到监听描述符集合中
FD_SET(newID, &readFds);
if (newID > maxFd)
{
maxFd = newID;
}
continue;
}//if (i == socketID)
//判断newID是否可读
memset(buf, 0, SIZE);
ret = recv(i, buf, SIZE - 1, 0);
if (0 > ret)
{
perror("recv error");
close(i);
FD_CLR(i, &readFds);
printf("client %d closed\r\n", i);
}
else if (0 == ret)
{
close(i);
FD_CLR(i, &readFds);
printf("client %d closed\r\n", i);
}
else
printf("client %d said:%s\r\n", i, buf);
}//if (FD_ISSET(i, &tmp))
}//for(i = 0; i <= maxFd; i++)
}//else
}//while(1)
close(socketID);
return 0;
}
int initSocket()
{
int socketID = 0;
int addrLength = 0;
int flag = 1;
struct sockaddr_in addr;
//创建socket
socketID = socket(AF_INET, SOCK_STREAM, 0);
if (socketID < 0)
{
perror("socket error");
return ERROR;
}
//设置地址可以重复绑定
if( setsockopt(socketID, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1)
{
perror("setsockopt");
return ERROR;
}
//bind
addrLength = sizeof (addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
if (0 > bind(socketID, (struct sockaddr *)&addr, addrLength))
{
perror("bind error");
return ERROR;
}
//listen
if (0 > listen(socketID, LISTENNUM))
{
perror("listen error");
return ERROR;
}
return socketID;
}
/*tcp 通信, 客户端*/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#define SIZE 32
#define IP "127.0.0.1"
#define PORT 54321
#define LISTENNUM 5
enum SOCK_OP
{
ERROR = -1,
SUCCESS = 0,
};
int main()
{
//定义变量
int socketID = 0;
int addrLength = 0;
int ret = 0;
struct sockaddr_in addr;
char buf[SIZE] = {0};
//创建套接字
socketID = socket(AF_INET, SOCK_STREAM, 0);
if (socketID < 0)
{
perror("socket error");
return -1;
}
//设定对方的IP/PORT
addrLength = sizeof(addr);
memset(&addr, 0, addrLength);
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr(IP);
//发送连接请求
ret = connect(socketID, (const struct sockaddr *)(&addr), addrLength);
if (ret < 0)
{
perror("connect error");
close(socketID);
return -1;
}
printf("connect success\r\n");
//通信(发,收)
while(1)
{
fgets(buf, SIZE - 1, stdin);
ret = send(socketID, buf, strlen(buf), 0);
if (ret < 0)
{
perror("send error");
close(socketID);
return -1;
}
printf("send success %s,%d\r\n", buf, ret);
if (strncmp(buf, "quit", 4) == 0)
{
break;
}
}
//关闭套接字
close(socketID);
return 0;
}
程序可实现多客户端与服务器端进行连接,而服务器都可及时响应客户端,不会出现服务器
阻塞;
转载于:https://blog.51cto.com/11313964/1901141
上一篇: LeetCode2.1.3:二分查找
推荐阅读
-
Java网络编程实现多客户端连接服务器
-
select多路复用实现多客户端连接服务器
-
服务器连接异常即将退出请重新进入游戏 PHP实现多服务器session共享之NFS共享的方法
-
Python—实现sftp客户端(连接远程服务器)
-
Go语言实践_实现一(服务器端)对多(客户端)在线聊天室
-
python socket多线程实现客户端与服务器连接
-
TCP实现服务器与客户端的连接(多线程)
-
玩一下linux下的IO多路复用之select-----实现多客户端访问服务器
-
linux c IO多路复用之select函数模型,构建C/S来实现服务器与客户端之间进行通信
-
Python—实现sftp客户端(连接远程服务器)