UDP下的epoll并发框架

        网上有一些人关注UDP下的epoll并发框架,但是能够搜到内容一般就是服务器端将采用bind将服务器地址同文件描述符绑定,之后将文件描述符注册到epoll中,就是说服务器同所有的客户端的通信仅使用这一个文件描述符,个人感觉似乎并没有发挥出epoll的性能。

       于是今天仿照kcpev中的udp中epoll的使用方式写了一个udp的echoserver。每当有新的客户端端发出请求的时候,客户端都新创建一个文件描述符,这样的使用方式,就和TCP中epoll一样了。

       就是说,新的连接出现时,采用connect函数将客户端的地址同fd进行绑定,或者说新创建的fd通过bind函数以及connect函数实现了一个类似于TCP中的accept的功能。需要注意的是,在udp中的connect函数并没有发起syn数据包,这在UDP中当然是不会出现的。

       刚开始的时候,我觉得新创建的fd没有必要采用bind函数将服务器的IP:PORT同新创建的fd进行绑定。但是发现客户端程序发送的消息的时候,仍是采用之前的绑定的fd。我个人猜测,一个fd中包含的属性应该有远端与目的端的地址(IP:PORT),对于新的连接,消息就只能走“监听”的fd。根据【1】中的资料,在UDP协议栈,内核中采用udp4_lib_lookup_skb对socket进行查找。

      另本人实现了一个简单的协议。消息的第一个字节表示消息类型,0表示客户端请求连接,1表示服务器允许连接,2表示消息,3表示断开连接。

server端代码如下:

#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include
 #include 
 

#define ECHO_LEN	1025
 

#define MAXBUF 1024
#define MAXEPOLLSIZE 100
 
#define NI_MAXHOST  1025
#define NI_MAXSERV	32




static unsigned int myport=1234;
/*
  setnonblocking – 设置句柄为非阻塞方式
  */
 int setnonblocking(int sockfd)
 {
   if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1)
   {
     return -1;
   }
   return 0;
 }
 

static void add_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);
} 
static void delete_event(int epollfd,int fd,int state)
{
    struct epoll_event ev;
    ev.events = state;
    ev.data.fd = fd;
    epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);
}
static void do_write(int epollfd,int fd,char *buf)
{
    int nwrite;
    nwrite = write(fd,buf,strlen(buf));
    if (nwrite == -1)
    {
        perror("write error:");
        close(fd);
        delete_event(epollfd,fd,EPOLLOUT);
    }
}
int udp_socket_connect(int epollfd,struct sockaddr_in  *servaddr)
{
	
struct sockaddr_in my_addr, their_addr;
int fd=socket(PF_INET, SOCK_DGRAM, 0);
	
	  /*设置socket属性,端口可以重用*/
   int opt=SO_REUSEADDR;
   setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
 

   setnonblocking(fd);
   bzero(&my_addr, sizeof(my_addr));
   my_addr.sin_family = PF_INET;
   my_addr.sin_port = htons(myport);
   my_addr.sin_addr.s_addr = INADDR_ANY;
   if (bind(fd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) 
   {
     perror("bind");
     exit(1);
   } 
   else
   {
     printf("IP and port bind success \n");
   }
	if(fd==-1)
		return  -1;
	connect(fd,(struct sockaddr*)servaddr,sizeof(struct sockaddr_in));
	add_event(epollfd,fd,EPOLLIN);
	return fd;
	
}
 
void accept_client(int epollfd,int fd)
{
	struct sockaddr_storage client_addr;
	socklen_t addr_size = sizeof(client_addr);
	char buf[1024];
	int ret = recvfrom(fd, buf,1024, 0, (struct sockaddr *)&client_addr, &addr_size);
	//check(ret > 0, "recvfrom error");
	buf[ret] = '\0';
	char type=buf[0];
	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
	ret = getnameinfo((struct sockaddr *)&client_addr, addr_size, hbuf, sizeof(hbuf), \
		sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
	//check(ret == 0, "getnameinfo");

	printf("recvfrom client [%s:%s] : %d\n", hbuf, sbuf, buf[0]);
	
	if(type!=0)
	{
		return;
	}
	int new_sock=udp_socket_connect(epollfd,(struct sockaddr_in*)&client_addr);
	buf[0]=1;
	do_write(epollfd,new_sock,buf);
	
}

void msg_process(int epollfd,int fd)
{
	int nread=0;
	char buf[MAXBUF];
	char type;
	nread = read(fd,buf,MAXBUF);
	//check(nread > 0, "recvfrom error");
	buf[nread] = '\0';
	type=buf[0];
	if(type==2)
	{
		printf("recv msg %s \n",buf+1);
		do_write(epollfd,fd,buf);
	}
	else{
		delete_event(epollfd,fd,EPOLLOUT);
	}
	
}
int main(int argc, char **argv)
 {
   int listener,kdpfd, nfds, n, curfds;
   socklen_t len;
   struct sockaddr_in my_addr, their_addr;
   
   struct epoll_event ev;
   struct epoll_event events[MAXEPOLLSIZE];
 

  
 

  /* 开启 socket 监听 */
   if ((listener = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
   {
     perror("socket create failed !");
     exit(1);
   }
   else
   {
     printf("socket create  success /n");
   }
 

  /*设置socket属性,端口可以重用*/
   int opt=SO_REUSEADDR;
   setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
 

   setnonblocking(listener);
   bzero(&my_addr, sizeof(my_addr));
   my_addr.sin_family = PF_INET;
   my_addr.sin_port = htons(myport);
   my_addr.sin_addr.s_addr = INADDR_ANY;
   if (bind(listener, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) 
   {
     perror("bind");
     exit(1);
   } 
   else
   {
     printf("IP and port bind success \n");
   }
  
   /* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */
   kdpfd = epoll_create(MAXEPOLLSIZE);
   len = sizeof(struct sockaddr_in);
   ev.events = EPOLLIN | EPOLLET;
   ev.data.fd = listener;
   if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listener, &ev) < 0) 
   {
     fprintf(stderr, "epoll set insertion error: fd=%d/n", listener);
     return -1;
   }
   else
   {
     printf("listen socket added in  epoll success /n");
   }
 

  while (1) 
   {
     /* 等待有事件发生 */
     nfds = epoll_wait(kdpfd, events, 10000, -1);
     if (nfds == -1)
     {
       perror("epoll_wait");
       break;
     }
 

    /* 处理所有事件 */
     for (n = 0; n < nfds; ++n)
     {
       if (events[n].data.fd == listener) 
       {
        accept_client(kdpfd,listener);
       }
	   else
	   {
		 msg_process(kdpfd,events[n].data.fd);  
	   }
      }
   }
   close(listener);
   return 0;
 }



客户端代码如下:

/*
 * =====================================================================================
 *
 *       Filename:  client.c
 *
 *    Description:  client to link the server
 *
 *        Version:  1.0
 *        Created:  2013/01/11 10时13分18秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  YOUR NAME (), 
 *        Company:  
 *
 * =====================================================================================
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAXBUF 100

int main(int argc, const char *argv[])
{
    

    char *server = "127.0.0.1";
    char *serverport = "1234";
    char *echostring = "helloworld";

    struct addrinfo client,*servinfo;
    memset(&client,0,sizeof(client));
    client.ai_family = AF_INET;
    client.ai_socktype = SOCK_DGRAM;
    client.ai_protocol= IPPROTO_UDP;

    if(getaddrinfo(server,serverport,&client,&servinfo)<0)
    {
        printf("error in getaddrinfo");
        exit(-1);
    }

    int sockfd = socket(servinfo->ai_family,servinfo->ai_socktype,servinfo->ai_protocol);
    if(sockfd <0)
    {
        printf("error in socket create");
        exit(-1);
    }
    char bufmsg[2];
	bufmsg[0]=0;
	bufmsg[1]='\0';
    ssize_t numBytes = sendto(sockfd,bufmsg,strlen(bufmsg),0,servinfo->ai_addr,servinfo->ai_addrlen);
    if(numBytes<0)
    {
        printf("error in send the data");
    }

	
	



	struct sockaddr_storage fromaddr;
    socklen_t fromaddrlen = sizeof(fromaddr);
    char buf[MAXBUF+1];
    numBytes = recvfrom(sockfd,buf,MAXBUF+1,0,(struct sockaddr *)&fromaddr,&fromaddrlen);
	if(buf[0]==1)
	{
		printf("connected to the server\n");
	}
	
	
	
	
	char  echomsg[1024];
	echomsg[0]=2;
    char *ptr=echomsg+1;
	strcpy(ptr,echostring);
	numBytes=0;
	numBytes = sendto(sockfd,echomsg,strlen(echomsg),0,servinfo->ai_addr,servinfo->ai_addrlen);
    if(numBytes<0)
    {
        printf("error in send the data");
    }	
	numBytes=0;
	numBytes = recvfrom(sockfd,buf,MAXBUF+1,0,(struct sockaddr *)&fromaddr,&fromaddrlen);
	if(buf[0]==2)
	{
		char *str=buf+1;
		printf("recv msg from server %s\n",str);
	}
	
	numBytes=0;
	buf[0]=3;
	numBytes = sendto(sockfd,buf,2,0,servinfo->ai_addr,servinfo->ai_addrlen);
    if(numBytes<0)
    {
        printf("error in send the data");
    }	

    freeaddrinfo(servinfo);

    printf("Received:%s \n",buf);
    close(sockfd);
    return 0;
}

参考【1】http://www.procedurego.com/article/23607.html

【2】linux网络协议栈-UDP

【3】UDP的epoll并发框架-UDPListener解决OpenVPN的并发问题

       

你可能感兴趣的:(C语言)