c 語言實現 tcp/udp 伺服器功能

勺顛顛發表於2019-09-21

提示:程式碼基於epoll的ET模式 【當然它預設是LT模式,還有EPOLLONESHOT】 epoll是最高效的一種模式,內部採用了複雜的二叉樹【最頭暈的AVL樹和紅黑TREE,特別是插入,刪除,查詢比線性表高效,作業系統核心使用了大量的資料結構和演算法,自己去看系統導論就清楚了】
讀者務必瞭解網路卡工作原理【做過網路卡晶片與常規的嵌入式晶片通訊驅動最好容易理解,藉助電子廠商的datasheet來擼理解會深刻一些】,硬體中斷原理,TCP/IP協議,TCP/UDP的區別,連線與關閉狀態轉移,TCP/UDP API【socket】,瞭解常規的線性表實現和樹的資料結構。
理解linux裝置檔案和硬體的關係,知道程式的常規排程演算法【以便於理解阻塞,非阻塞,非同步,輪詢】
理解IO流和緩衝IO【比如對檔案描述符的操作】
IO模型知識
以上知識理論便以理解以下程式碼【不然真跟碼農一樣抄著程式碼嘍^_^】

c 語言實現 tcp/udp 伺服器功能

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <sys/epoll.h>
#include <libgen.h>

#define MAX_EVENT_NUMBER 1024
#define TCP_BUFFER_SIZE 512
#define UDP_BUFFER_SIZE 1024

int setnonblock(int fd)
{
        int old_option = fcntl(fd,F_GETFL);
        int new_option = old_option|O_NONBLOCK;
        fcntl(fd,F_SETFL,new_option);
        return old_option;
}
void addfd(int epollfd,int fd)
{
        struct epoll_event event;
        event.data.fd = fd;
        event.events = EPOLLIN|EPOLLET;
        epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
        setnonblock(fd);

}
int main(int argc,char *argv[])
{
        if(argc<2){
                printf("useage:%s ip and port\n",basename(argv[0]));
                return 0;
        }

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

        struct sockaddr_in address;
        bzero(&address,sizeof(&address));
        address.sin_family = AF_INET;
        address.sin_port = htons(port);

        inet_pton(AF_INET,ip,&address.sin_addr);

        int sockfd = socket(PF_INET,SOCK_STREAM,0);

        assert(sockfd>0);

        int ret = bind(sockfd,(struct sockaddr*)&address,sizeof(address));
        assert(ret!=-1);

        ret = listen(sockfd,1024);

        bzero(&address,sizeof(address));
        address.sin_family = AF_INET;
        address.sin_port = htons(port);
        inet_pton(AF_INET,ip,&address.sin_addr);
         int udpfd = socket(PF_INET,SOCK_DGRAM,0);
        assert(udpfd>0);

        ret = bind(udpfd,(struct sockaddr*)&address,sizeof(address));
        assert(ret!=-1);

        struct epoll_event events[MAX_EVENT_NUMBER];

        int epollfd = epoll_create(1024);

        addfd(epollfd,sockfd);
        addfd(epollfd,udpfd);

        while(1){
                int number = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
                if(number <0){
                        printf("epoll fail\n");
                        break;
                }

                int i;
                for(i=0;i<number;i++){
                        int fd = events[i].data.fd;
                        if(fd == sockfd){
                                struct sockaddr_in client;
                                socklen_t client_len = sizeof(client);
                                int connfd = accept(sockfd,(struct sockaddr*)&client,&client_len);
                                printf("client come in\n");
                                addfd(epollfd,connfd);
                        }
                        else if(fd == udpfd){
                                char buf[UDP_BUFFER_SIZE];
                                memset(buf,'\0',UDP_BUFFER_SIZE);
                                struct sockaddr_in client;
                                socklen_t client_len = sizeof(client);
                                ret = recvfrom(udpfd,buf,UDP_BUFFER_SIZE,0,(struct sockaddr*)&client,&client_len);
                                printf("收到udp客戶端的資料:%s\n",buf);
                                if(ret >0){
                                        sendto(udpfd,buf,UDP_BUFFER_SIZE,0,(struct sockaddr*)&client,client_len);
                                }
                        }
                        else if(events[i].events & EPOLLIN){
                                char buf[TCP_BUFFER_SIZE];
                                while(1){
                                        memset(buf,'\0',TCP_BUFFER_SIZE);
                                        ret = recv(fd,buf,TCP_BUFFER_SIZE-1,0);
                                        if(ret < 0){
                                                if((errno == EAGAIN)||(errno == EWOULDBLOCK)){
                                                        break;
                                                }
                                                close(fd);

                                                break;
                                        }
                                        else if(ret ==0){
                                                close(fd);
                                        }else{
                                                printf("收到tcp客戶端的資料:%s\n",buf);
                                                send(fd,buf,TCP_BUFFER_SIZE,0);

                                                struct sockaddr_in client;
                                                socklen_t client_len = sizeof(client);
                                                getpeername(fd,(struct sockaddr*)&client,&client_len);
                                                char ip[INET_ADDRSTRLEN];
                                                printf("your ip is %s,port is %d\n",inet_ntop(AF_INET,&client.sin_addr,ip,INET_ADDRSTRLEN),ntohs(client.sin_port));

                                                //sendto(udpfd,buf,TCP_BUFFER_SIZE,0,(struct sockaddr*)&client,client_len);

                                        }
                                }
                        }else{
                                printf("其它事件\n");
                        }
                }
        }

        close(sockfd);
        return 0;
}

相關文章