/* TCP通信过程中,服务器端实现的流程
1>socket 函数创建一个用于连接的套接字文件描述符
2>bind 函数绑定IP和端口号
3>listen函数将套接字设置为被动监听模式,准备接收客户请求
4>accecpt函数;等待客户请求到来;当请求到来以后,接受连接请求,
返回一个新的对应于此次连接的套接字。
5>用返回的套接字和客户端进行通信,(send/recv函数)。
6>返回:等待另一客户请求*/
// select函数的使用
int main(int argc, const char *argv[])
{
//1 为通信创建一个端点
int sfd=socket(AF_INET,SOCK_STREAM,0);
//参数1 说明使用的是IPv4网络
//参数2 说明使用的是TCP面向连接的通讯方式
//参数3,由于参数2已经指定通信方式,直接为0
if(sfd ==-1)
{
perror("socket error");
return -1;
}
printf("socket success sfd=%d\n",sfd); // 3 (0 stdin ,stdout 1,stderr 2)
// 设置地址能够快速重用
int reuse =1;
if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
{
perror("setsockopt error");
return -1;
}
// 2 绑定IP地址和端口号 (服务端)
// 2.1 准备地址信息结构体
struct sockaddr_in sin;
sin.sin_family =AF_INET; //通信域
sin.sin_port =htons(SER_PORT); //端口号
sin.sin_addr.s_addr =inet_addr(SER_IP); // ip地址
// 2.2 绑定工作
if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)))
{
perror("bind error");
return -1;
}
printf("bind success\n");
// 3 将套接字设置成被动监听状态;
//
if(listen(sfd,128)==-1)
{
perror("listen error");
return -1;
}
printf("listen success\n");
// 4 阻塞等待客户端的连接
// 4.1 定义用于接受客户端信息的容器
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
// 11 准备检测文件描述符的集合
fd_set readfds,tempfds;
// 22 清空集合中的内容
FD_ZERO(&readfds);
// 33 将要检测的文件描述符放入集合中
FD_SET(sfd,&readfds);
FD_SET(0,&readfds);
int newfd=0; //定义用于存放客户端套接字的变量
while(1)
{
//执行阻塞函数之前现将文件描述符集合备份一份
tempfds=readfds;
//使用阻塞函数,检测文件描述符集合中是否有事件发生
int res=select(sfd+1,&tempfds,NULL,NULL,NULL);
if(res ==-1)
{
perror("select error");
return -1;
}
else if(res ==0)
{
printf("time out\n");
return -1;
}
//当程序执行至此,表示检测容器已经中已经有一个或者多个事件发生
//只需要判断哪些文件描述符还在集合中,还在集合中的一定发生了事件,直接取相关逻辑
//判断sfd是否触发了上面的操作
if(FD_ISSET(sfd,&tempfds))
{
newfd =accept(sfd,(struct sockaddr*)&cin,&addrlen);
if(newfd ==-1)
{
perror("accept error");
return -1;
}
printf("accept success\n");
printf("[%s %d]发送来连接请求\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
}
//判断0号文件描述符(stdin)是否触发了操作
if(FD_ISSET(0,&tempfds))
{
char buf[128]={}; //用于读取消息内容的容器
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0; // 读取结束
printf("键盘完成输入事件:%s\n",buf);
}
}
// 6 关闭套接字
close(sfd);
return 0;
}
// 4 阻塞等待客户端的连接
// 4.1 定义用于接受客户端信息的容器
struct sockaddr_in cin;
socklen_t addrlen = sizeof(cin);
// 11 准备检测文件描述符的集合
fd_set readfds,tempfds;
// 22 清空集合中的内容
FD_ZERO(&readfds);
// 33 将要检测的文件描述符放入集合中
FD_SET(sfd,&readfds);
FD_SET(0,&readfds);
int newfd=0; //存放客户端套接字变量
int maxfd=sfd; //存放容器中最大的文件描述符
struct sockaddr_in cin_arr[1024]; //存放客户端地址信息结构体的容器
while(1)
{
//在执行阻塞函数之前将文件描述符集合先备份一份
tempfds=readfds;
//使用阻塞函数,检测文件描述符集合中是否有事件产生
int res =select(maxfd+1,&tempfds,NULL,NULL,NULL);
if(res ==-1)
{
perror("select error");
return -1;
}
else if(res ==0)
{
printf("time out\n");
return -1;
}
}
//当执行至次行代码,说明文件描述集合中已经有一个或者多个事件产生
//此时只需要判断还有哪些文件描述符还在集合中,还在集合的描述符一定产生了事件,直接去执行
//将所有的文件描述符全部遍历一遍
for(int i=0;i<maxfd;i++)
{
//判断当前的i文件描述符是否还没有触发事件
if(!FD_ISSET(i,&tempfds)) //文件描述符集合还没有触发事件
{
continue; //退出本层循环
}
//若 执行至此处,则说明文件描述符已经触发事件
//那么就要判断一下是触发了哪一个事件
//判断sfd是否触发了相关事件
if(i==sfd)
{
newfd=accept(i, (struct sockaddr*)&cin,&addrlen);
if(newfd ==-1)
{
perror("accept error");
return -1;
}
printf("accept success\n");
printf("[%s %d]发送来连接请求\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
//将newfd放入到readfds容器当中,以便检测是否有客户端发来数据
FD_SET(newfd,&tempfds);
//更新maxfd
if(newfd>maxfd)
{
maxfd=newfd;
}
//将套接字地址信息结构体放入数组当中
cin_arr[newfd]=cin;
}
else if(i==0)
{
//判断是否是0号文件描述符解除阻塞
char buf[128]="";
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
printf("键盘完成事件输入:%s\n",buf);
//循环将消息发送给所有客户端
for(int k=4;k<maxfd;k++)
{
send(k,buf,strlen(buf),0);
}
}else
{
// 5 与客户端进行相互通信
char rbuf[128]={}; //用于读取消息内容的容器
while(1)
{
// 清空容器
bzero(rbuf,sizeof(rbuf));
//从套接字读取数据
int res =read(newfd,rbuf,sizeof(rbuf));
if(res == 0)
{
printf("客户端已经下线\n");
// 6 关闭套接字
close(i);
//将当前的套接字文件描述符从容器中移除
FD_CLR(i,&tempfds);
//更新maxfd
for(int j=maxfd;j>sfd;j--)
{
if(FD_ISSET(j,&readfds))
{
maxfd=j;
break;
}
}
continue;
}
//将读取的消息展示出来
printf("[%s %d]:%s\n",inet_ntoa(cin_arr[i].sin_addr),ntohs(cin_arr[i].sin_port),rbuf);
//将消息转发给所有客户端
for(int k=4;k<maxfd;k++)
{
if(k!=i)
{
send(k,rbuf,strlen(rbuf),0);
}
}
}
}
}
close(sfd);
return 0;