#include "Network.h"
#define MAX_FD 10
int main(int argc, const char **argv)
{
int fd = -1, newfd = -1, ret;
struct sockaddr_in server_addr;
int i = 0;
char buf[BUFSIZ];
/*1、创建套接字*/
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
return -1;
}
/*2、填充网络信息结构体*/
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
//优化:允许任意IP地址的客户端连接
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
/*3、绑定=====将名称分配给套接字*/
if(bind(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
perror("bind");
return -1;
}
/*4、监听=====把主动套接字转换为被动套接字*/
if(listen(fd, 5) < 0)
{
perror("listen");
return -1;
}
printf("***Server start success***\n");
fd_set rset, temps;
int num;
struct sockaddr_in client_addr;
socklen_t AddrLen = sizeof(client_addr);
char ipv4_addr[16];
int fd_all[MAX_FD]; //保存所有描述符,用于select调用后,判断哪个可读
memset(fd_all, -1, sizeof(fd_all));
fd_all[0] = fd; //第一个为监听套接字
FD_ZERO(&rset); //清零
FD_SET(fd, &rset); //监听套接字fd加进集合,监视fd
int maxfd = fd_all[0]; //监听的最大套接字
/*
5、用Select函数,达到I/O多路复用,一个服务器支持多个客户端
*/
while(1)
{
temps = rset; //因为无事件发生的fd会被清空,需要重新复制
if((num = select(maxfd + 1, &temps, NULL, NULL, NULL)) < 0)
{
perror("select");
return -1;
}
if(num < 0)
{
perror("select");
return -1;
}
if(num == 0)
{
printf("timeout\n");
}
if(FD_ISSET(fd, &temps)) //监听套接字如果可读,就说明有新客户端连接进来了
{
/*
6、接受=====阻塞等待客户端连接
*/
if((newfd = accept(fd, (struct sockaddr *)&client_addr, &AddrLen)) < 0)
{
perror("accept");
close(fd);
return -1;
}
//客户端连接成功后在服务器端打印出来
if(!inet_ntop(AF_INET, (void *)&client_addr.sin_addr.s_addr, ipv4_addr, sizeof(client_addr)))
{
perror("ionet_ntop");
close(newfd);
close(fd);
return -1;
}
printf("Client [%s:%d] connected!\n", ipv4_addr, htons(client_addr.sin_port));
/* 将新连接套接字加入进fd_all集合 和 rset集合 */
for(i = 0;i <= MAX_FD; i++)
{
if(fd_all[i] == -1)
{
fd_all[i] = newfd;
printf("client fd_all[%d] join in.\n", i);
break;
}
else
{
continue;
}
}
if(newfd == FD_SETSIZE)
{
printf("error: too many client newfd\n");
return -1;
}
if(newfd > -1 && newfd < FD_SETSIZE)
{
FD_SET(newfd, &rset);
}
if(maxfd < newfd)
{
maxfd = newfd; //更新maxfd
}
}
/*
7、读写数据,并且将数据返回给客户端
循环判断哪个文件描述符
*/
for(int i = 1;i < maxfd; i++)
{
if(FD_ISSET(fd_all[i], &temps))
{
bzero(buf, BUFSIZ);
do {
ret = read(fd_all[i], buf, BUFSIZ-1);
}while(ret < 0 && EINTR == errno);
if(ret < 0)
{
perror("read");
return -1;
}
if(!ret) //客户端退出,关闭套接字,并从监听集合清除
{
FD_CLR(fd_all[i], &rset);
close(fd_all[i]);
fd_all[i] = -1;
continue;
}
if(!strncasecmp(buf, "quit", 4))
{
printf("Client all_fd[%d] has left!\n", i);
continue;
}
printf("[Server]Receive data: %s\n",buf);
}
}
}
return 0;
}
select实现并发服务器
于 2023-04-18 15:09:51 首次发布