IO多路复用服务器
#include <myhead.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__\n",__LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.123.126"
#define PORT 6666
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd = socket(AF_INET,SOCK_STREAM,0);
if (sfd < 0)
{
ERR_MSG("socket");
return -1;
}
//允许端口被快速重用
int reuse = 1;
if (setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("端口被快速重用成功\n");
//填充服务器的地址信息结构体给bind用
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT);
sin.sin_addr.s_addr = inet_addr(IP);
//绑定服务器的ip和端口
if (bind(sfd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
{
ERR_MSG("bind");
return -1;
}
printf("bind success\n");
//将套接字设置为被动接听状态
if (listen(sfd,128) < 0)
{
ERR_MSG("listen");
return -1;
}
printf("listen success\n");
//创建集合
fd_set readfds,tempfds;
//初始化集合
FD_ZERO(&readfds);
FD_ZERO(&tempfds);
//将需要监测的文件描述符放入集合中
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
//存最大的文件描述符
int maxfd = sfd;
//存储客户端的信息结构体
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
//存储newfd对应的客户端信息
struct sockaddr_in savecin[1024-3];
//定义容器
int newfd = -1;
char buf[128] = "";
ssize_t res = 0;
int s_res = 0;
//循环选择
while (1)
{
tempfds = readfds;
//让内核监测集合中的文件描述符是否准备就绪
s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
if (s_res < 0)
{
perror("select");
return -1;
}
else if (0 == s_res)
{
printf("time out......\n");
break;
}
//循环遍历集合中的文件描述符,从0开始遍历
for (int i = 0; i <= maxfd; i++)
{
//判断i是否在集合中
if (FD_ISSET(i,&tempfds) == 0)
{
continue;
}
//键盘输入
if (0 == i)
{
printf("触发键盘输入事件\n");
//存储要发送给哪个客户端
int sndfd;
res = scanf("%d %s",&sndfd,buf);
while(getchar()!=10);
//判断输入的数据格式
if (res != 2)
{
fprintf(stderr,"输入的数据格式错误:fd string\n");
continue;
}
//判断输入的文件描述符是否正确
if (sndfd <= 2 ||sndfd > 1023 || !FD_ISSET(sndfd,&readfds))
{
fprintf(stderr,"输入的文件描述符错误\n");
continue;
}
if (send(sndfd,buf,sizeof(buf),0) < 0)
{
ERR_MSG("send");
continue;
}
printf("send success\n");
}
else if (sfd == i)
{
printf("触发客户端连接事件\n");
//创建新的文件描述符接收客户端信息
newfd = accept(sfd,(struct sockaddr *)&cin,&addrlen);
if (newfd < 0)
{
ERR_MSG("accept");
continue;
}
printf("[%s : %d] newfd = %d 客户端连接成功\n",\
inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
savecin[newfd-3] = cin;
//将newfd存入集合中
FD_SET(newfd,&readfds);
//更新maxfd
maxfd = maxfd>newfd?maxfd:newfd;
}
else
{
printf("触发客户端交互事件\n");
bzero(buf,sizeof(buf));
//接收数据
res = recv(i,buf,sizeof(buf),0);
if (res < 0)
{
ERR_MSG("recv");
continue;
}
else if (0 == res)
{
printf("[%s : %d] newfd = %d 客户端下线\n",\
inet_ntoa(savecin[i-3].sin_addr),ntohs(savecin[i-3].sin_port),i);
//关闭文件描述符
close(i);
//从集合中删除i的文件描述符
FD_CLR(i,&readfds);
//更新maxfd
while(!FD_ISSET(maxfd,&readfds) && maxfd-->=0);
continue;
}
printf("[%s : %d] newfd = %d : %s\n",\
inet_ntoa(savecin[i-3].sin_addr),ntohs(savecin[i-3].sin_port),i,buf);
//发送数据
strcat(buf,"*_*");
if (send(i,buf,sizeof(buf),0) < 0)
{
ERR_MSG("send");
continue;
}
printf("send success\n");
}
}
}
//关闭文件描述符
if (close(sfd) < 0)
{
ERR_MSG("close");
return -1;
}
return 0;
}
IO多路复用客户端
#include <myhead.h>
#define ERR_MSG(msg) do{\
fprintf(stderr,"__%d__\n",__LINE__);\
perror(msg);\
}while(0)
#define IP "192.168.123.126"
#define PORT 6666
int main(int argc, const char *argv[])
{
//创建流式套接字
int cfd = socket(AF_INET,SOCK_STREAM,0);
if (cfd < 0)
{
ERR_MSG("socket");
return -1;
}
//允许端口被快速重用
int reuse = 1;
if (setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) < 0)
{
ERR_MSG("setsockopt");
return -1;
}
printf("端口被快速重用成功\n");
//填充服务器的地址信息结构体给connect用
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)) < 0)
{
ERR_MSG("connect");
return -1;
}
printf("connect success\n");
//创建集合
fd_set readfds,tempfds;
//初始化集合
FD_ZERO(&readfds);
FD_ZERO(&tempfds);
//将需要监测的文件描述符放入集合中
FD_SET(0,&readfds);
FD_SET(cfd,&readfds);
//定义容器
char buf[128] = "";
ssize_t res = 0;
int s_res = 0;
//循环选择
while (1)
{
tempfds = readfds;
//让内核监测集合中的文件描述符是否准备就绪
s_res = select(cfd+1,&tempfds,NULL,NULL,NULL);
if (s_res < 0)
{
perror("select");
return -1;
}
else if (0 == s_res)
{
printf("time out......\n");
break;
}
if (FD_ISSET(0,&tempfds))
{
printf("触发键盘输入事件\n");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = 0;
if (send(cfd,buf,sizeof(buf),0) < 0)
{
ERR_MSG("send");
continue;
}
printf("send success\n");
}
if (FD_ISSET(cfd,&tempfds))
{
printf("触发服务器交互事件\n");
bzero(buf,sizeof(buf));
//接收数据
res = recv(cfd,buf,sizeof(buf),0);
if (res < 0)
{
ERR_MSG("recv");
continue;
}
else if (0 == res)
{
printf("[%s : %d] cfd = %d 服务器下线\n",\
inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),cfd);
continue;
}
printf("[%s : %d] cfd = %d : %s\n",\
inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),cfd,buf);
}
}
//关闭文件描述符
if (close(cfd) < 0)
{
ERR_MSG("close");
return -1;
}
return 0;
}