#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define MAXFD 10 //数组的大小用宏定义出来
void fds_init(int fds[])// 初始化存放描述符的数组,因为正确的描述符都是正数,
{ //所以将数组中的所有元素都初始化为“-1”
int i = 0;
for( ; i < MAXFD; i++)
{
fds[i] = -1;
}
}
void fds_add(int fds[], fd)//将新描述符添加到描述符数组中
{
int i = 0;
for(; i < MAXFD; i++)// 遍历数组中,只要发现某一个元素是初始值,就代表没有描述符存放
{ //在该位置,所以就可以将新的描述符存进去
if( fds[i] == -1)
{
fds[i] = fd;// 注意,一旦找到存放位置,在存放后就要返回,如果不返回,i就会遍历
return ; //整个数组,将新描述符存进整个数组
}
}
}
void fds_del(int fds[], int fd)//将数组中与fd相等的描述符删除
{
int i = 0;
for( ; i < MAXFD; i++)
{
if(fds[i] == fd)
{
fds[i] = -1;//注意,一旦找到目标描述符,删掉后就要返回。原因同存放描述符函数相同
return ;
}
}
}
int main()
{
int sockfd = socket(AF_INET, SOCK_STREAM, 0); //创建套接字
assert(sockfd != NULL);
struct sockaddr_in saddr, caddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6000);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //设置协议族端口号和ip地址
int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); //命名套接字
assert(res != -1);
listen(sockfd, 5);//监听套接字
int fds[MAXFD];//定义一个数组用来存放描述符
fds_init(fds);//初始化数组
fds_add(fds, sockfd);//将sockfd先放进数组中
while(1)
{
fd_set fdset; //给select创建一个“集合”
FD_ZERO(&fdset);//把集合里面清零
int maxfd = -1; //设置一个记录最大描述符的变量,可以告诉select函数监视的描述符范围,减少时间浪费
int i = 0;
for( ; i < MAXFD; i++)//开始遍历整个存放描述符的数组
{
if(fds[i] == -1)// 如果数组中的元素的初始值被更改,见代表存放了描述符进去,如果初始值没有
{ //更改,就代表没有存放描述符进去,
break;
}
FD_SET(fds[i], &fdset);//按照描述符的大小在集合中偏移后做标记
if(fds[i] > maxfd)
{
maxfd = fds[i]; //集合中每进入一个描述符就将最大的用maxfd标记起来
}
}
struct timeval tv = {5, 0}; //给设置select函数设置监视集合的时间
int n = select(maxfd + 1, &fdset, NULL, NULL, &tv); //时间、大小、描述符存放好后开始让select监视
if( n == -1) //如果在规定的时间内select返回值为“-1”就代表select函数发生错误,返回去继续监视集合
{
printf("select error!\n");
continue;
}
if( n == 0)// 如果在规定时间内select监视的集合中没有描述符上面有数据传来,就打超时时一次,继续监视
{
printf("time out!\n");
continue;
}
else//如果在监视的时间内发现有描述符传来数据
{
int i = 0;
for( ; i < MAXFD; i++)//遍历整个数组,找存放的描述符
{
if(fds[i] == -1)
{
continue;
}
if(FD_ISSET(fds[i] , &fdset))//每找到一个描述符就测试fds[i]是否可读,即是否网络上有数据
{
if(fds[i] == sockfd)// 如果fds[i]有数据传来 ,就得判断是新连接的客户机还是已经建立
{ //连接的客户机发送给服务器的数据
int len = sizeof(caddr);
int c = accept(sockfd, (struct sockaddr*)caddr, &len); // 如果是新连接的客户机,
if( c < 0) //就将客户机的端口号(文件
{ //描述符)存在数组中
continue;
}
printf("accept = %d\n",c); //将新客户机描述符打印出
fds_add(fds, c); //将新文件描述符存进数组
}
else
{
char buff[128] = {0}; //如果是已经建立连接的客户机发来的数据,就将数据接收在buff中
int num = recv(fds[i], buff, 127, 0);
if( num <= 0) // 如果recv接受数据后返回错误值“-1”就代表接收失败,如果返回“0”
{ //就代表客户端断开了与服务器的连接,此时服务器端也断开个描述符所对应的客户端
close(fds[i]);
fds_del(fds, fds[i]); //将这个描述符从数组中拿出去丢掉
printf("one client over!\n");
}
else
{
printf("recv(%d) = %s\n",fds[i],buff);//如果recv返回的值大于0,就代表客户端发来了数据,输出服务器发送来数据
send(fds[i], "ok", 2, 0);//给客户端发送数据接收成功确认
}
}
}
}
}
}
exit(0);
}