IO多路复用:
select实现TCP并发服务器:
#include <myhead.h>
#define PORT 8888
#define IP "192.168.115.119"
int main(int argc, const char *argv[])
{
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd == -1)
{
perror("socket error");
return -1;
}
printf("绑定成功\n");
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
printf("设置端口快速重用\n");
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
printf("绑定成功\n");
if(listen(sfd,128)==-1)
{
perror("listen error");
return -1;
}
struct sockaddr_in cin;
cin.sin_family = AF_INET;
socklen_t socklen = sizeof(cin);
//定义一个用于检测文件描述符的集合
fd_set readfds,tempfds; //在栈区定义
//清空容器内容
FD_ZERO(&readfds);
//将要检测的文件描述符放入集合中
FD_SET(sfd,&readfds); //将sfd文件描述符放入
FD_SET(0,&readfds); //将0号文件描述符放入
//定义一个容器
char buf[128] = "";
int res = 0; //接收select的返回值
int newfd = -1; //用于存放最新连接客户端的套接字
int maxfd = sfd; //定义控制select函数中最大文件描述符
struct sockaddr_in saveCin[1024]; //用于存放客户端地址信息结构体
while(1)
{
//将集合内容复制一份
tempfds = readfds;
//使用select阻塞等待集合中的文件描述符有事件产生
res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
if(res == -1)
{
perror("select error");
return -1;
}
else if(res == 0)
{
printf("timeout\n");
return -1;
}
//遍历所有集合中文件描述符
for(int i=0;i<maxfd+1;i++)
{
//判断当前i是否在集合中,如果不在,直接判断下一个
if(!FD_ISSET(i,&tempfds))
{
continue;
}
//判断sfd是否还在集合中
if(i == sfd)
{
//阻塞接收客户端的连接请求,并且获取客户端的地址信息
newfd = accept(sfd,(struct sockaddr*)&cin,&socklen);
if(newfd == -1)
{
perror("accept error");
return -1;
}
printf("accept success\n");
//将newfd放入readfds中
FD_SET(newfd,&readfds);
//更新maxfd
if(newfd > maxfd)
{
maxfd = newfd;
}
//最新的客户端套接字放入数组的下标为new的位置
saveCin[newfd] = cin;
}
else if(i == 0) //判断是否是终端输入
{
//从终端获取数据
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
printf("触发终端输入事件:%s\n",buf);
sprintf(buf,"系统消息:%s",buf);
//将数据发送给所有客户端
for(int j=4;j<=maxfd;j++)
{
send(j,buf,sizeof(buf),0);
}
}
else
{
//收发数据使用newfd完成通信
char buf[128] = "";
//清空字符串
bzero(buf,sizeof(buf));
int ret = recv(i,buf,sizeof(buf),0); //从套接字中读取客户端发来的信息
//判断收到的结果
if(ret == 0)
{
printf("客户端已经下线\n");
close(i);
//将当前文件描述符从集合中删除
FD_CLR(i,&readfds);
//更新maxfd
for(int j=maxfd;j>=0;j--)
{
//判断当前的j是否在集合中,如果在,则为maxfd
if(FD_ISSET(j,&readfds))
{
maxfd = j;
break;
}
}
continue;
}
else if(ret < 0)
{
perror("recv error");
return -1;
}
printf("[%s:%d]>>%s\n",inet_ntoa(saveCin[i].sin_addr),ntohs(saveCin[i].sin_port),buf);
//将读取的信息,加上一些字符发送回去
strcat(buf,"*>-<*");
send(i,buf,sizeof(buf),0);
}
}
}
close(sfd);
return 0;
}
客户端代码:
#include <myhead.h>
#define PORT 8888
#define IP "192.168.115.198"
int main(int argc, const char *argv[])
{
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("connect error");
return -1;
}
printf("连接成功\n");
//定义一个用于检测文件描述符的集合
fd_set readfds,tempfds; //在栈区定义
//清空容器内容
FD_ZERO(&readfds);
//将要检测的文件描述符放入集合中
FD_SET(cfd,&readfds); //将cfd文件描述符放入
FD_SET(0,&readfds); //将0号文件描述符放入
//定义一个容器
char buf[128] = "";
char rbuf[128] = "";
int res = 0; //接收select的返回值
while(1)
{
//将集合内容复制一份
tempfds = readfds;
//使用select阻塞等待集合中的文件描述符有事件产生
res = select(cfd+1,&tempfds,NULL,NULL,NULL);
if(res == -1)
{
perror("select error");
return -1;
}
else if(res == 0)
{
printf("timeout");
return -1;
}
for(int i=0;i<=cfd;i++)
{
if(!FD_ISSET(i,&tempfds))
{
continue;
}
if(i == cfd)
{
bzero(rbuf,sizeof(rbuf));
recv(cfd,rbuf,sizeof(rbuf),0);
printf("rbuf = %s\n",rbuf);
}
if(i == 0)
{
bzero(buf,sizeof(buf));
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
send(cfd,buf,sizeof(buf),0);
}
}
}
close(cfd);
return 0;
}
poll:
客户端代码:
#include <myhead.h>
#define SERPORT 8888
#define SERIP "192.168.115.198"
#define CLIPORT 6666
#define CLIIP "192.168.115.198"
int main(int argc, const char *argv[])
{
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd == -1)
{
perror("socket error");
return -1;
}
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("connect error");
return -1;
}
printf("连接成功\n");
char buf[128] = "";
char rbuf[128] = "";
//定义一个集合管理0号文件描述符和cfd
struct pollfd fds[2];
//将0号文件描述符放入
fds[0].fd = 0;
fds[0].events = POLLIN;
//将cfd放入集合
fds[1].fd = cfd;
fds.events = POLLIN;
int res = 0; //接收poll的返回值
while(1)
{
res = poll(fds,2,-1);
if(res == -1)
{
perror("select error");
return -1;
}
else if(res == 0)
{
printf("timeout");
return -1;
}
bzero(rbuf,sizeof(rbuf));
bzero(buf,sizeof(buf));
if(fds[0].revents == POLLIN)
{
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]='\0';
send(cfd,buf,sizeof(buf),0);
}
if(fds[1].revent = POLLIN)
{
recv(cfd,rbuf,sizeof(rbuf),0);
printf("rbuf = %s\n",rbuf);
}
}
close(cfd);
return 0;
}
服务端代码:
#include <myhead.h>
#define PORT 8888
#define IP “192.168.115.198”
int main(int argc, const char *argv[])
{
int sfd = socket(AF_INET,SOCK_STREAM,0);
if(sfd == -1)
{
perror(“socket error”);
return -1;
}
int reuse = 1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
printf("设置端口快速重用\n");
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
{
perror("bind error");
return -1;
}
printf("绑定成功\n");
if(listen(sfd,128)==-1)
{
perror("listen error");
return -1;
}
struct sockaddr_in cin;
cin.sin_family = AF_INET;
socklen_t socklen = sizeof(cin);
struct pollfd fds[1024];
fds[0].fd = 0;
fds[0].events = POLLIN;
fds[1].fd = cfd;
fds.events = POLLIN;
int res =0;
//定义一个容器
char buf[128] = "";
int newfd = -1;
int maxfd = 2;
while(1)
{
res = poll(fds,maxfd,-1)
if(res == -1)
{
perror("select error");
return -1;
}
else if(res == 0)
{
printf("timeout\n");
return -1;
}
for(int i=0;i<=maxfd;i++)
if(i = sfd)
{
//阻塞接收客户端的连接请求,并且获取客户端的地址信息
newfd = accept(sfd,(struct sockaddr*)&cin,&socklen);
if(newfd == -1)
{
perror("accept error");
return -1;
}
printf("accept success\n");
maxfd = newfd;
fds[
}
else if(i == 0) //判断是否是终端输入
{
char buf1[128] = "";
bzero(buf1,sizeof(buf1));
//从终端获取数据
fgets(buf1,sizeof(buf1),stdin);
buf1[strlen(buf1)-1]='\0';
printf("触发终端输入事件:%s\n",buf1);
// sprintf(buf1,“%s%s”,“系统消息:”,buf1);
//将数据发送给所有客户端
for(int j=4;j<=maxfd;j++)
{
send(j,buf1,sizeof(buf1),0);
}
}
else if(i == 1||i == 2)
{
continue;
}
else
{
//收发数据使用newfd完成通信
char buf[128] = "";
//清空字符串
bzero(buf,sizeof(buf));
int ret = recv(i,buf,sizeof(buf),0); //从套接字中读取客户端发来的信息
//判断收到的结果
if(ret == 0)
{
printf("客户端已经下线\n");
close(i);
continue;
}
else if(ret < 0)
{
perror("recv error");
return -1;
}
printf("[%s:%d]>>%s\n",inet_ntoa(saveCin[i].sin_addr),ntohs(saveCin[i].sin_port),buf);
//将读取的信息,加上一些字符发送回去
strcat(buf,"*>-<*");
send(i,buf,sizeof(buf),0);
}
}
}
close(sfd);
return 0;
}