1.poll io服务器(单线程处理所有请求)
server.c
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>
#include <strings.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h>
#include <unistd.h>
#include <arpa/inet.h> /* inet(3) functions */
#include <poll.h> /* poll function */
#include <limits.h>
#include "pollio_server.h"
static struct pollfd client_fds[MAX_SELECT_CLIENT_NUM] = {0};
static reg_rcv_handle_call_back callbackfunc = NULL;
static int server_create(int port);
static void *poll_loop(void *arg);
/*****************************************
*func:server_init
*describe:初始化服务器端
*para: port 端口号
*return:成功则返回0,error则返回RET_VAL_FAILED
*****************************************/
int server_init(const int port, const reg_rcv_handle_call_back func)
{
int cnt = 10;
static int ret_fd = -1;
int ret;
for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
{
client_fds[i].fd=-1;
}
while (cnt--)
{
ret_fd = server_create(port);
if (RET_VAL_FAILED != ret_fd)
{
if (NULL == callbackfunc)
callbackfunc = func;
break;
}
usleep(100 * 1000);
}
if (ret_fd < 0)
{
return RET_VAL_FAILED;
}
cnt = 10;
while (cnt--)
{
pthread_t loop_thread_id;
ret = pthread_create(&loop_thread_id, NULL, poll_loop, &ret_fd);
if (0 == ret)
{
pthread_detach(loop_thread_id);
printf("init done ,wait client connect...\n");
return RET_VAL_SUCCESS;
}
}
return RET_VAL_FAILED;
}
/*****************************************
*func:server_create
*describe:创建监听的fd
*para: port 端口号
*return: 成功则返回监听的fd,error则返回RET_VAL_FAILED
*****************************************/
int server_create(int in_port)
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int server_fd, retval;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; // Return IPv4 and IPv6 choices.
hints.ai_socktype = SOCK_STREAM; // We want a TCP socket.
hints.ai_flags = AI_PASSIVE; // All interfaces.
char port[16]={0};
snprintf(port,sizeof(port),"%d",in_port);
//数填写的套接口地址结构,port可以是http ftp字符
retval = getaddrinfo(NULL, port, &hints, &result);
if (retval != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retval));
return RET_VAL_FAILED;
}
for (rp = result; rp != NULL; rp = rp->ai_next)
{
server_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (server_fd == RET_VAL_FAILED)
continue;
设置端口复用,防止被占用,服务器退出时再次绑定不会影响再次绑定
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)) < 0)
{
printf(" set server_fd option SO_REUSEADDR error !");
goto failed;
}
//bind soucket
retval = bind(server_fd, rp->ai_addr, rp->ai_addrlen);
if (retval == 0)
{
// We managed to bind successfully!
break;
}
goto failed;
}
if (rp == NULL)
{
fprintf(stderr, "server_fd could not bind\n");
goto failed;
}
freeaddrinfo(result);
//设置不阻塞模式
int flags, s;
flags = fcntl(server_fd, F_GETFL, 0);
if (flags == RET_VAL_FAILED)
{
perror("server_fd fcntl set error ,F_GETFL\n");
goto failed;
}
flags |= O_NONBLOCK;
s = fcntl(server_fd, F_SETFL, flags);
if (s == RET_VAL_FAILED)
{
perror("server_fd fcntl set error F_SETFL\n");
goto failed;
}
//绑定端口号
retval = listen(server_fd, SOMAXCONN);
if (retval == RET_VAL_FAILED)
{
perror("server_fd listen error\n");
goto failed;
}
client_fds[0].fd=server_fd;//监听fd
client_fds[0].events = POLLRDNORM;
return server_fd;
failed:
close(server_fd);
return RET_VAL_FAILED;
}
void *poll_loop(void *arg)
{
int listen_fd = *((int *)arg);
int max_fd = 0;
int retval;
int wait_timeout_ms=1000;//1s
//select多路复用
while (1)
{
max_fd = listen_fd;
for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++)
{
if (client_fds[i].fd > 0)
{
if (max_fd <= client_fds[i].fd)
{
max_fd = client_fds[i].fd;
}
}
}
retval = poll(client_fds,max_fd+1,wait_timeout_ms);
if (retval > 0)
{
if ((client_fds[0].revents)&POLLRDNORM) //有新的客户端连接
{
struct sockaddr_in client_address;
socklen_t address_len;
int client_sock_fd = accept(listen_fd, (struct sockaddr *)&client_address, &address_len);
if (client_sock_fd > 0)
{
int flags = RET_VAL_FAILED;
//一个客户端到来分配一个fd,超过CLI_NUM以后跳出for循环,flags重新被赋值为RET_VAL_FAILED
for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++)
{
if (client_fds[i].fd <= 0)
{
flags = i;
client_fds[i].fd = client_sock_fd;
client_fds[i].events= POLLRDNORM;
client_fds[i].revents = 0;//目前还没有任何事件返回,置为零
break;
}
}
if (flags != RET_VAL_FAILED)
{
printf("new user client[%d]=%d add sucessfully!\n", flags, client_sock_fd);
}
else //flags=RET_VAL_FAILED
{
printf("the client nums is more than %d, can't login.close now!\n", MAX_SELECT_CLIENT_NUM);
close(client_sock_fd);
}
}
}
else
{
//deal with the message
for (int i = 1; i < MAX_SELECT_CLIENT_NUM; i++)
{
if ((client_fds[i].revents)&POLLRDNORM)
{
char rcv_buff[MAX_RCV_BUFF_SIZE] = {0};
int len = recv(client_fds[i].fd, rcv_buff, MAX_RCV_BUFF_SIZE, MSG_DONTWAIT);
if (len > 0)
{
int send_len = send(client_fds[i].fd, rcv_buff, len, MSG_DONTWAIT);
printf("message form client[%d]=%d,rcv len%d,send_len=%d\n", i, client_fds[i].fd, len, send_len);
if (NULL != callbackfunc)
callbackfunc(client_fds[i].fd, rcv_buff, len);
continue;
}
else if (len <= 0)
{
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue;
}
close(client_fds[i].fd);
client_fds[i].fd = -1;
if (0 == len)
{
printf("clien[%d] exit by itself!bye\n", i);
}
else
{
//printf("clien[%d] is error!\n", i);
}
}
}
}
}
}
if (retval < 0)
{
printf("poll error:%s\n", strerror(errno));
continue;
}
else if (0 == retval)
{
//printf("poll timeout!\n");
continue;
}
}
}
void get_all_client(int out_clients[], const int size)
{
if (size > MAX_SELECT_CLIENT_NUM)
{
for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
{
out_clients[i] = client_fds[i].fd;
}
}
else
{
for (int i = 0; i < size; i++)
{
out_clients[i] = client_fds[i].fd;
}
}
}
server.h
#ifndef _pollio_server_H_
#define _pollio_server_H_
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef RET_VAL_SUCCESS
#define RET_VAL_SUCCESS (0)
#endif
#ifndef RET_VAL_FAILED
#define RET_VAL_FAILED (-1)
#endif
#ifndef MAX_SELECT_CLIENT_NUM
#define MAX_SELECT_CLIENT_NUM (50200)//数量无限制
#endif
#ifndef MAX_RCV_BUFF_SIZE
#define MAX_RCV_BUFF_SIZE (4096*100)
#endif
typedef struct _client_info_t
{
int fd;
int login_id;
}client_info_t;
typedef int (*reg_rcv_handle_call_back)(const int fd,const void *data,const int data_len);
/*****************************************
*func:server_init
*describe:初始化服务器端
*para: port 端口号
*return:成功则返回0,error则返回-1
*****************************************/
int server_init(const int port,const reg_rcv_handle_call_back func);
void get_all_client(int out_clients[],const int size);
#ifdef __cplusplus
}
#endif
#endif
main.c
#include <stdio.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>
#include <strings.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h>
#include "pollio_server.h"
int sysUsecTime(char *pTime)
{
struct timeval tv;
struct timezone tz;
struct tm *p;
gettimeofday(&tv, &tz);
p = localtime(&tv.tv_sec);
sprintf(pTime, "%04d-%02d-%02d-%02d:%02d:%02d:.%06ld", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec, tv.tv_usec);
return 0;
}
int handle_call_back(const int fd,const void *data,const int data_len)
{
static int cat_fd=5;
if(cat_fd==fd)
{
char pTime[64]={0};
sysUsecTime(pTime);
printf("[%s]:fd=%d,len=%d\n",pTime,fd,data_len);
}
}
int main(int argc, char **argv)
{
server_init(11111, handle_call_back);
int out_clients[1024]={0};
while (1)
{
#if 0
get_all_client(out_clients, 1024);
int print_flag=-1;
for (int i = 0; i < 1024; i++)
{
if (out_clients[i] > 0)
{
if(print_flag<0)
printf("client:");
print_flag=1;
printf("client[%d]=%d ",i ,out_clients[i]);
}
}
if(print_flag>0)
printf("\n");
#endif
sleep(5);
}
}
2.epoll io 服务器(单线程)
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>
#include <strings.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h>
#include <unistd.h>
#include <arpa/inet.h> /* inet(3) functions */
#include <sys/epoll.h>
#include <limits.h>
#include "epollio_server.h"
#define MAX_EPOOL_EVENTS (1024)
static int client_fds[MAX_SELECT_CLIENT_NUM] = {0};
static reg_rcv_handle_call_back callbackfunc = NULL;
static int server_create(int port);
static int epoll_init(int listen_fd);
static void *epool_loop(void *arg);
/*****************************************
*func:server_init
*describe:初始化服务器端
*para: port 端口号
*return:成功则返回0,error则返回RET_VAL_FAILED
*****************************************/
int server_init(const int port, const reg_rcv_handle_call_back func)
{
int cnt = 10;
static int ret_fd = -1;
int ret;
for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
{
client_fds[i] = -1;
}
while (cnt--)
{
ret_fd = server_create(port);
if (RET_VAL_FAILED != ret_fd)
{
if (NULL == callbackfunc)
callbackfunc = func;
break;
}
usleep(100 * 1000);
}
if (ret_fd < 0)
{
return RET_VAL_FAILED;
}
cnt = 10;
while (cnt--)
{
int ret =epoll_init(ret_fd);
if (RET_VAL_FAILED != ret)
{
printf("init done ,wait client connect...\n");
return RET_VAL_SUCCESS;
}
usleep(100 * 1000);
}
return RET_VAL_FAILED;
}
/*****************************************
*func:server_create
*describe:创建监听的fd
*para: port 端口号
*return: 成功则返回监听的fd,error则返回RET_VAL_FAILED
*****************************************/
int server_create(int in_port)
{
struct addrinfo hints;
struct addrinfo *result, *rp;
int server_fd, retval;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; // Return IPv4 and IPv6 choices.
hints.ai_socktype = SOCK_STREAM; // We want a TCP socket.
hints.ai_flags = AI_PASSIVE; // All interfaces.
char port[16] = {0};
snprintf(port, sizeof(port), "%d", in_port);
//数填写的套接口地址结构,port可以是http ftp字符
retval = getaddrinfo(NULL, port, &hints, &result);
if (retval != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(retval));
return RET_VAL_FAILED;
}
for (rp = result; rp != NULL; rp = rp->ai_next)
{
server_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (server_fd == RET_VAL_FAILED)
continue;
设置端口复用,防止被占用,服务器退出时再次绑定不会影响再次绑定
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(int)) < 0)
{
printf(" set server_fd option SO_REUSEADDR error !");
goto failed;
}
//bind soucket
retval = bind(server_fd, rp->ai_addr, rp->ai_addrlen);
if (retval == 0)
{
// We managed to bind successfully!
break;
}
goto failed;
}
if (rp == NULL)
{
fprintf(stderr, "server_fd could not bind\n");
goto failed;
}
freeaddrinfo(result);
//设置不阻塞模式
int flags, s;
flags = fcntl(server_fd, F_GETFL, 0);
if (flags == RET_VAL_FAILED)
{
perror("server_fd fcntl set error ,F_GETFL\n");
goto failed;
}
flags |= O_NONBLOCK;
s = fcntl(server_fd, F_SETFL, flags);
if (s == RET_VAL_FAILED)
{
perror("server_fd fcntl set error F_SETFL\n");
goto failed;
}
//绑定端口号
retval = listen(server_fd, SOMAXCONN);
if (retval == RET_VAL_FAILED)
{
perror("server_fd listen error\n");
goto failed;
}
client_fds[0] = server_fd; //监听fd
return server_fd;
failed:
close(server_fd);
return RET_VAL_FAILED;
}
int epoll_init(int listen_fd)
{
static int listenfd=0;
listenfd=listen_fd;
int cnt = 10;
int ret = -1;
while (cnt--)
{
pthread_t loop_thread_id;
ret = pthread_create(&loop_thread_id, NULL, epool_loop, &listenfd);
if (0 == ret)
{
pthread_detach(loop_thread_id);
return RET_VAL_SUCCESS;
}
usleep(10 * 1000);
}
return RET_VAL_FAILED;
}
void *epool_loop(void *arg)
{
int listen_fd = *((int *)arg);
int max_fd = 0;
int retval;
int wait_timeout_ms = 1000; //1s
int ret = -1;
int efd;
struct epoll_event event;
struct epoll_event *events;
//除了参数size被忽略外,此函数和epoll_create完全相同
efd = epoll_create1(0);
if (efd == -1)
{
perror("epoll_create error,exit");
exit(-1);
}
event.data.fd = *((int *)arg);
event.events = EPOLLIN | EPOLLET; //读入,边缘触发方式
ret = epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &event);
if (ret == -1)
{
perror("epoll_ctl error,exit");
exit(-1);
}
/* Buffer where events are returned */
events = calloc(MAX_EPOOL_EVENTS, sizeof(event));
//select多路复用
while (1)
{
int n = 0, i = 0;
n = epoll_wait(efd, events, MAX_EPOOL_EVENTS, -1);
for (i = 0; i < n; i++)
{
if ((events[i].events & EPOLLIN))
{
if (listen_fd != events[i].data.fd)
{
int len=-1;
do
{
char rcv_buff[MAX_RCV_BUFF_SIZE] = {0};
len = recv(events[i].data.fd, rcv_buff, MAX_RCV_BUFF_SIZE, MSG_DONTWAIT);
if (len > 0)
{
int send_len = send(events[i].data.fd, rcv_buff, len, MSG_DONTWAIT);
//printf("message form client=%d,rcv len%d,send_len=%d\n", events[i].data.fd, len, send_len);
if (NULL != callbackfunc)
callbackfunc(events[i].data.fd, rcv_buff, len);
continue;
}
else if (len <= 0)
{
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue;
}
close(events[i].data.fd);
for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++)
{
if( client_fds[k]==events[i].data.fd)
{
client_fds[k]=-1;
break;
}
}
if (0 == len)
{
printf("clien[%d] exit by itself!bye\n", events[i].data.fd);
}
else
{
printf("clien[%d] is error!\n", events[i].data.fd);
}
events[i].data.fd = -1;
}
} while (len==MAX_RCV_BUFF_SIZE);
}
else
{
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept(listen_fd, &in_addr, &in_len);
if (infd == -1)
{
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
{
/* We have processed all incoming connections. */
continue;
}
else
{
perror("accept error\n");
continue;
}
}
//将地址转化为主机名或者服务名
ret = getnameinfo(&in_addr, in_len, hbuf, sizeof hbuf, sbuf, sizeof sbuf, NI_NUMERICHOST | NI_NUMERICSERV); //主机地址和服务地址,flag参数:以数字名返回
if (ret == 0)
{
printf("Accepted connection on descriptor %d (host=%s, port=%s)\n", infd, hbuf, sbuf);
}
else if (ret == -1)
{
perror("accept:getnameinfo error \n");
close(infd);
continue;
}
event.data.fd = infd;
event.events = EPOLLIN | EPOLLET;
ret = epoll_ctl(efd, EPOLL_CTL_ADD, infd, &event);
if (ret == -1)
{
perror("epoll_ctl error ,");
close(infd);
continue;
}
for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++)
{
if(-1== client_fds[k])
{
client_fds[k]=infd;
break;
}
}
}
}
else if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP))
{
/* An error has occured on this fd, or the socket is not
ready for reading (why were we notified then?) */
fprintf(stderr, "epoll error\n");
close(events[i].data.fd);
for(int k=0;k<MAX_SELECT_CLIENT_NUM;k++)
{
if( client_fds[k]==events[i].data.fd)
{
client_fds[k]=-1;
break;
}
}
continue;
}
}
}
free(events);
close(listen_fd);
}
void get_all_client(int out_clients[], const int size)
{
if (size > MAX_SELECT_CLIENT_NUM)
{
for (int i = 0; i < MAX_SELECT_CLIENT_NUM; i++)
{
out_clients[i] = client_fds[i];
}
}
else
{
for (int i = 0; i < size; i++)
{
out_clients[i] = client_fds[i];
}
}
}
3.测试客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/select.h>
#include <strings.h>
#include <string.h>
#define MAXLINE 4096*100
int sockfd[1000] = {0};
char recvLine[MAXLINE], sendLine[MAXLINE];
void *recv_func(void *arg)
{
fd_set ser_fdset;
int max_fd =2;
struct timeval mytime;
while (1)
{
max_fd =2;
FD_ZERO(&ser_fdset);
for (int i = 0; i < 1000; i++)
{
if (sockfd[i] > 0)
{
FD_SET(sockfd[i], &ser_fdset);
if(sockfd[i]>max_fd)
{
max_fd=sockfd[i];
}
}
}
mytime.tv_sec = 1;
int ret = select(max_fd+1, &ser_fdset, NULL, NULL, &mytime);
if (ret < 0)
{
perror("select failure\n");
continue;
}
else if (ret == 0)
{
//printf("time out! \n");
continue;
}
else
{
for (int i = 0; i < 1000; i++)
{
if (FD_ISSET(sockfd[i], &ser_fdset))
{
int len = -1;
memset(recvLine, 0, sizeof(recvLine));
if (sockfd[i] > 0)
{
len = recv(sockfd[i], recvLine, sizeof(recvLine), MSG_DONTWAIT);
printf("recv len=%d\n", len);
}
}
}
}
}
}
int main(int argc, char **argv)
{
int n;
struct sockaddr_in servaddr;
while (1)
{
static int i = 0;
if ((sockfd[i] = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
return 0;
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(11111);
if (inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr) <= 0)
{
printf("inet_pton error for 127.0.0.1\n");
return 0;
}
if (connect(sockfd[i], (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
//客户端需要调用connect()连接服务器,connect和bind的参数形式一致,区别在于bind的参数是自己的地址,而connect的参数是对方的地址。connect()成功返回0,出错返回-1
printf("connect error: %s(errno: %d)\n", strerror(errno), errno);
sockfd[i]=-1;
}
usleep(1 * 100);
i++;
if (i >= 1000)
break;
}
printf("send msg to server: \n");
memset(sendLine, 0, sizeof(sendLine));
for (int i = 0; i < MAXLINE; i++)
{
sendLine[i] = 'c';
}
sendLine[MAXLINE - 1] = 0;
int len = strlen(sendLine);
pthread_t recv_pth;
pthread_create(&recv_pth, NULL, recv_func, NULL);
pthread_detach(recv_pth);
while (1)
{
for (int i = 0; i < 1000; i++)
{
if (sockfd[i] > 0)
{
int ret = send(sockfd[i], sendLine, len, MSG_DONTWAIT);
if (ret < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
}
printf("send len=%d\n", ret);
usleep(3);
}
}
}
for (int i = 0; i < 1000; i++)
close(sockfd[i]);
return 0;
}
测试结果:
1000个客户端连接,每3微秒发100K数据
100个客户端连接,每3微秒发100K数据