tcp服务器的多路重用
a),服务器端
#include<stdio.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#define SPORT 2024
#define IP "192.168.8.156"
#define PRINTF_ERR(msg)\
do{\
printf("line__%d_",__LINE__);\
perror(msg);\
}while(0)
int main(int argc, const char *argv[])
{
int sfd=0;
//创建流式套接字
sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
PRINTF_ERR("socket");
return -1;
}
//端口快速复用
int reuse=1;
if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) <0)
{
PRINTF_ERR("setsockopt");
return -1;
}
//填充服务器地址信息结构体
struct sockaddr_in sin;
sin.sin_family=AF_INET;
sin .sin_port=htons(SPORT);
sin.sin_addr.s_addr=inet_addr(IP);
//绑定服务器端口和IP
if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
PRINTF_ERR("bind");
return -1;
}
//将套接字设置为监听
if(listen(sfd,10)<0)
{
PRINTF_ERR("listen");
return -1;
}
//创建select函数监听的文件描述符集合
//类型为fd_set
//清空集合并将要将听的文件描述符添加到集合中
//添加到集合中使用 FD_SET
fd_set readfds, tempfds;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
//最大文件描述符
int maxfd=sfd;
ssize_t res=0;
int sres=0;
char buf[34]="";
int new_sfd=0;
struct sockaddr_in cin;
socklen_t addrlen =sizeof(cin);
struct sockaddr_in saveCin[1020];
//循环监听readfds结合内的文件描述符的情况
while(1)
{
tempfds=readfds;
//因为要包括最大的文件描述符所以要讲maxfd+1,并接受返回的有情况变化的文件描述符个数
sres=select(maxfd+1,&tempfds,NULL,NULL,NULL);
if(sres>0)
{
//因为select函数解除阻塞后会将没有发生情况的的文件描述符清空,只留下有情况变化的文件描述符
//循环比较tempfds中的文件描述符的变化情况
for(int i=0;i<=maxfd;i++)
{
//判断文件描述符是否在tempfds中
if(FD_ISSET(i,&tempfds)==0)
{
continue ;
}
if(0 == i) //键盘触发并将键盘获取的数据发送给客户端
{
printf("触发键盘输入事件>>>\n");
int sendfd=0;
res=scanf("%d %s",&sendfd,buf);
while(getchar()!=10);
if(res!=2)
{
printf("请输入正确的格式 :sendfd buf\n");
continue;
}
buf[strlen(buf)-1]=0;
if(send(sendfd,buf,sizeof(buf),0)<0)
{
PRINTF_ERR("send");
continue ;
}
printf("[%s]发送文件描述符为[%d]\n",buf,sendfd);
}
else if(sfd == i)
{
printf("触发客户端链接事件>>>\n");
new_sfd=accept(i,(struct sockaddr *)&cin,&addrlen);
if(new_sfd<0)
{
PRINTF_ERR("accept");
continue ;
}
printf("%d:客户端接入>>[%d:%s]\n",new_sfd,ntohs(cin.sin_port),inet_ntoa(cin.sin_addr));
saveCin[new_sfd-4]=cin;
if(new_sfd>maxfd)
{
maxfd=new_sfd;
}
FD_SET(new_sfd,&readfds);
}
else //客户端交互
{
printf("触发客户端交互事件>>>\n");
res=recv(i, buf, sizeof(buf),0);
if(res<0)
{
PRINTF_ERR("recvfrom");
continue ;
}
else if(0==res)
{
printf("[%s |%d]:客户端断开!\n",inet_ntoa(saveCin[i-4].sin_addr),ntohs(saveCin[i-4].sin_port));
FD_CLR(i,&readfds);
close(i);
for(int j=maxfd;j>0;j--)
{
if(FD_ISSET(j,&readfds))
{
maxfd=j;
break;
}
}
}
else{
//读取到之后打印出来
printf("[%s |%d]-[%d]:%s\n",inet_ntoa(saveCin[i-4].sin_addr),ntohs(saveCin[i-4].sin_port),i,buf);
strcat(buf,"*==*");
if(send(i,buf,sizeof(buf),0)<00)
{
PRINTF_ERR("send");
return -1;
}
}
}
}
}else if(0==sres)
{
break;
}else
{
PRINTF_ERR("select");
return -1;
}
}
close(sfd);
return 0;
}
b ),客户端
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/time.h>
#include<unistd.h>
#include<string.h>
#define SPORT 2024
#define IP "192.168.8.156"
#define PRINTF_ERR(msg)\
do{\
printf("[line:_%d_]\n",__LINE__);\
perror("msg");\
}while(0)
int main(int argc, const char *argv[])
{
//创建流式套接字
int sfd=0;
sfd=socket(AF_INET,SOCK_STREAM,0);
if(sfd<0)
{
PRINTF_ERR("socket");
return -1;
}
//填充服务器地址信息结构体
struct sockaddr_in sin;
struct sockaddr_in cin;
sin.sin_family=AF_INET;
sin.sin_port=htons(SPORT);
sin.sin_addr.s_addr=inet_addr(IP);
//连接服务器
if(connect(sfd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
PRINTF_ERR("connect");
return -1;
}
//创建监听集合
fd_set readfds;
fd_set tempfds;
FD_ZERO(&readfds);
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
char buf[128]="";
int s_res=0;
int max_fd;
max_fd=sfd;
int res=0;
//循环监听文件描述符集合
while(1)
{
tempfds=readfds;
s_res=select(max_fd+1,&tempfds,NULL,NULL,NULL);
if(s_res>0)
{
for(int i=0;i<=max_fd;i++)
{
if(FD_ISSET(i,&tempfds)==0)
{
continue;
}
if(0==i)
{
//发送数据给服务器
printf("键盘触发事件>>>!\n");
memset(buf,0,sizeof(buf));
res=read(i,buf,sizeof(buf));
buf[strlen(buf)-1]=0;
if(res<0)
{
PRINTF_ERR("read");
return -1;
}
printf("[ %s ]以发送给服务器\n",buf);
if(send(sfd,buf,res,0)<0)
{
PRINTF_ERR("send");
return -1;
}
}
else if(sfd==i)
{
printf("服务器连接触发>>>\n");
//接收服务器数据
res=recv(sfd,buf,sizeof(buf),0);
if(res<0)
{
PRINTF_ERR("msg");
return -1;
}else if(0==res)
{
printf("服务器端断开!\n");
close(sfd);
return 0;
}
printf("server>>>>%s\n",buf);
}
}
}
else if(0==s_res)
{
printf("time out !\n");
return -1;
}
else
{
PRINTF_ERR("select");
return -1;
}
}
//关闭文件描述符
close(sfd);
return 0;
}