TCPselect
代码
服务器
#include<myhead.h>
#include<sqlite3.h>
#define PORT 6666 //端口号
#define IP "192.168.0.104" //IP地址
//键盘事件
int jp(fd_set tempfds,int maxfd)
{
char buf[128] = ""; //用来接收数据
char buf1[128] = ""; //用来接收数据
int snfd = -1; //用来接受从终端的文件描述符
bzero(buf,sizeof(buf)); //清空字符串
int res = scanf("%d %s",&snfd,buf); //从终端读取文件描述符和接收数据
while(getchar() != 10); //循环吸收垃圾字符
if(res != 2) //判断是否输入了两个数据
{
printf("输入数据格式错误!\n");
return -1;
}
if(snfd <= 2 || FD_ISSET(snfd,&tempfds) == 0) //判断是否正确输入文件描述符
{
printf("输入非法文件描述符:%d\n",snfd);
return -1;
}
sprintf(buf1,"%c%c%s",'s',':',buf);
if(send(snfd,buf1,sizeof(buf1),0) < 0) //判断是否发送成功
{
ERR_MSG("send");
return -1;
}
return 0;
}
//客户端链接事件
int lj(int sfd,struct sockaddr_in sevecin[1024],fd_set *preadfds,int *maxfd)
{
int newfd = -1;
struct sockaddr_in cin; //客户端信息结构体
socklen_t len = sizeof(cin); //信息结构体真实大小
//连接客户端
newfd = accept(sfd,(struct sockaddr *)&cin,&len);
if(newfd < 0) //判断是否连接成功
{
ERR_MSG("newfd");
return -1;
}
printf("[%s : %d] 客户端连接成功!\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
sevecin[newfd] = cin; //将新的客户端连接到客户端数组中
FD_SET(newfd,preadfds);//将新的文件描述符添加到读集合中
*maxfd = *maxfd > newfd ? *maxfd : newfd;//更新最大文件描述符
return 0;
}
//客户端交互事件
int jh(int fd, struct sockaddr_in sevecin[1024],fd_set *readfds,int *maxfd)
{
char buf[128] = ""; //搬运工
bzero(buf,sizeof(buf)); //清空字符串
//接收
ssize_t res = recv(fd,buf,sizeof(buf),0);
if(res < 0)
{
ERR_MSG("recv");
return -1;
}
else if(res == 0)
{
printf("[%s : %d]客户端已下线!\n",inet_ntoa(sevecin[fd].sin_addr),ntohs(sevecin[fd].sin_port));
close(fd); //关闭文件描述符
FD_CLR(fd,readfds);//从读集合中删除该信息
while(FD_ISSET(*maxfd,readfds) && (*maxfd)-- >= 0);//更新最大文件描述符
return 0;
}
printf("[%s : %d]: %s\n",inet_ntoa(sevecin[fd].sin_addr),\
ntohs(sevecin[fd].sin_port),buf);
//发送
strcat(buf,"*_*");
if(send(fd,buf,sizeof(buf),0) < 0)
{
ERR_MSG("send");
return -1;
}
return 0;
}
//主函数
int main(int argc, const char *argv[])
{
//创建套接字
int sfd = -1;
if((sfd = socket(AF_INET,SOCK_STREAM,0)) < 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;
}
//填充信息结构体
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;
}
//监听
if(listen(sfd,128) < 0)//判断是否设置监听成功
{
ERR_MSG("listen");
return -1;
}
//创建一个读集合
fd_set readfds,tempfds;
//清空集合
FD_ZERO(&readfds);
//将需要监测的文件放入到集合中
FD_SET(0,&readfds);
FD_SET(sfd,&readfds);
//记录最大文件描述符
int maxfd = sfd;
//记录select函数的返回值
int s_res;
//备份连接成功的客户端信息
struct sockaddr_in sevecin[1024];
bzero(sevecin,sizeof(sevecin)); //清空数组
while(1)
{
FD_ZERO(&tempfds);
tempfds = readfds;
s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL); //多路复用
if(s_res < 0)
{
ERR_MSG("select");
return -1;
}
if(s_res == 0)
{
printf("超时!\n");
break;
}
for(int i = 0; i <= maxfd; i++)
{
if(FD_ISSET(i,&tempfds) == 0)
{
continue;
}
if(i == 0)
{
//键盘事件
jp(readfds,maxfd);
}
else if(i == sfd)
{
//客户端链接事件
lj(sfd,sevecin,&readfds,&maxfd);
}
else
{
//客户端交互事件
jh(i,sevecin,&readfds,&maxfd);
}
}
}
if(close(sfd) < 0)
{
ERR_MSG("close");
return -1;
}
return 0;
}
客户端
#include<myhead.h>
#include<sqlite3.h>
#define PORT 6666 //端口号
#define IP "192.168.0.104" //IP
int main(int argc, const char *argv[])
{
//创建流式套接字
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd < 0) //判断是否创建成功
{
ERR_MSG("socket");
}
//填充客户端地址信息
struct sockaddr_in cin;
cin.sin_family = AF_INET;
cin.sin_port = htons(PORT); //端口
cin.sin_addr.s_addr = inet_addr(IP); //IP
//连接
if(connect(cfd,(struct sockaddr *)&cin,sizeof(cin)) < 0)
{
ERR_MSG("connect");
return -1;
}
//定义读集合
fd_set readfds,tempfds;
FD_ZERO(&readfds); //清空集合
FD_SET(0,&readfds); //加入集合
FD_SET(cfd,&readfds);
char buf[128] = ""; //定义搬运工
while(1)
{
tempfds = readfds;
bzero(buf,sizeof(buf));
//多路复用
int s_res = select(cfd+1,&tempfds,NULL,NULL,NULL);
if(s_res < 0)
{
ERR_MSG("select");
return -1;
}
if(s_res == 0)
{
printf("超时!\n");
break;
}
//向服务器发送数据
if(FD_ISSET(0,&tempfds))
{
bzero(buf,sizeof(buf));//清空字符串
fgets(buf,sizeof(buf),stdin); //从终端获取数据
buf[strlen(buf) - 1] = 0; //换行变成'\0'
if(send(cfd,buf,sizeof(buf),0) < 0) //发送数据并判断数据是否发送成功
{
ERR_MSG("send");
return -1;
}
}
//从服务器接受数据
if(FD_ISSET(cfd,&tempfds))
{
bzero(buf,sizeof(buf)); //清空字符串
int res = recv(cfd,buf,sizeof(buf),0);
if(res < 0) //判断是否成功接受
{
ERR_MSG("recv");
return -1;
}
else if(res == 0) //判断服务器是否下线
{
printf("服务器下线!\n");
break;
}
printf("%s\n",buf);
fflush(stdout);
}
}
if(close(cfd)) //关闭文件描述符并判断是否关闭成功
{
ERR_MSG("close");
return -1;
}
return 0;
}
现象