I/O複用的高級應用三:同時處理TCP和UDP服務

對於同一個端口,如果服務器要同時處理該端口上的TCP和UDP請求,則需要建立兩個不同的socket,一個是流socket,另一個是數據報socket,並將它們都綁定到該端口上。

下例回射服務器就能同時處理一個端口上的TCP和UDP請求:

#include<stdio.h>
#include<sys/socket.h>
#include<sys/epoll.h>
#include<sys/types.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#define MAX_EVENT_NUMBER 1024
void set_nonblock(int fd)
{
    int flag = fcntl(fd,F_GETFL);
    flag |= O_NONBLOCK;
    fcntl(fd,F_SETFL,flag);
}
void addfd(int epollfd,int sockfd)
{
    epoll_event event;
    event.data.fd = sockfd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);
    set_nonblock(sockfd);
}
int main(int argc,const char *argv[])
{
    if(argc < 3)
    {
        printf("usage: %s ip_address port_number\n",argv[0]);
        exit(1);
    }

    const char *ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address,connaddr;
    socklen_t connaddrlen = sizeof(connaddr);

    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    inet_pton(AF_INET,ip,&address.sin_addr);

    int tcpfd = socket(AF_INET,SOCK_STREAM,0);
    bind(tcpfd,(struct sockaddr*)&address,sizeof(address));
    listen(tcpfd,3);

    int udpfd = socket(AF_INET,SOCK_DGRAM,0);
    bind(udpfd,(struct sockaddr*)&address,sizeof(address));

    epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    addfd(epollfd,tcpfd);
    addfd(epollfd,udpfd);

    char buf[BUFSIZ];
    while(1)
    {
        int ret = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
        if(ret < 0)
        {
            perror("epoll_wait error:");
            break;
        }

        for(int i = 0;i < ret;i++)
        {
            int sockfd = events[i].data.fd;
            if(sockfd == tcpfd)
            {
                while(1)
                {
                    int connfd = accept(sockfd,(struct
                                sockaddr*)&connaddr,&connaddrlen);
                    if(connfd < 0)
                    {
                        if((errno == EAGAIN) || (errno == EWOULDBLOCK))
                        {
                            break;
                        }
                    }
                    addfd(epollfd,connfd);
                }
            }
            else if(sockfd == udpfd)
            {
                while(1)
                {
                    memset(buf,'\0',sizeof(buf));
                    int n = recvfrom(sockfd,buf,sizeof(buf),0,(struct
                                sockaddr*)&connaddr,&connaddrlen);
                    if(n < 0)
                    {
                        if(errno == EAGAIN)
                            break;
                        perror("recvfrom error:");
                        break;
                    }
                    else if(n > 0)
                    {
                        sendto(sockfd,buf,strlen(buf),0,(struct
                                sockaddr*)&connaddr,connaddrlen);
                    }
                }
            }
            else if(events[i].events & EPOLLIN)
            {
                while(1)
                {
                    memset(buf,'\0',sizeof(buf));
                    int n = recv(sockfd,buf,sizeof(buf),0);
                    if(n < 0)
                    {
                        if(errno == EAGAIN)
                        {
                            break;
                        }
                        perror("recv error:");
                        epoll_ctl(epollfd,EPOLL_CTL_DEL,sockfd,NULL);
                        close(sockfd);
                        break;
                    }
                    else if(n == 0)
                    {
                        printf("client close the connection\n");
                        epoll_ctl(epollfd,EPOLL_CTL_DEL,sockfd,NULL);
                        close(sockfd);
                        break;
                    }
                    send(sockfd,buf,strlen(buf),0);
                }
            }
        }
    }
    close(epollfd);
    close(tcpfd);
    close(udpfd);
    return 0;
}


參考:Linux高性能服務器編程        遊雙
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章