Linux 網絡編程八(epoll應用--大並發處理)


//頭文件 pub.h
#ifndef _vsucess

#define _vsucess

#ifdef __cplusplus
extern "C"
{

#endif
//服務器創建socket
int server_socket(int port);

//設置非阻塞
int setnonblock(int st);

//接收客戶端socket
int server_accept(int st);

//關閉socket
int close_socket(int st);

//接收消息
int socket_recv(int st);

//連接服務器
int connect_server(char *ipaddr,int port);

//發送消息
int socket_send(int st);

//將sockaddr_in轉化成IP地址
int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr);

#ifdef __cplusplus
}
#endif

#endif
//輔助方法--pub.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//htons()函數頭文件
#include <netinet/in.h>//inet_addr()頭文件
#include <fcntl.h>
#include "pub.h"

#define MAXBUF 1024

//創建socket
int socket_create()
{
    int st = socket(AF_INET, SOCK_STREAM, 0);
    if (st == -1)
    {
        printf("create socket failed ! error message :%s\n", strerror(errno));
        return -1;
    }
    return st;
}

//設置服務端socket地址重用
int socket_reuseaddr(int st)
{
    int on = 1;
    if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        printf("setsockopt reuseaddr failed ! error message :%s\n",
                strerror(errno));
        //close socket
        close_socket(st);
        return -1;
    }
    return 0;
}

//服務器綁定--監聽端口號
int socket_bind(int st, int port)
{
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    //type
    addr.sin_family = AF_INET;
    //port
    addr.sin_port = htons(port);
    //ip
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    //bind ip address
    if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
    {
        printf("bind failed ! error message :%s\n", strerror(errno));
        //close socket
        close_socket(st);
        return -1;
    }
    //listen
    if (listen(st, 20) == -1)
    {
        printf("listen failed ! error message :%s\n", strerror(errno));
        //close socket
        close_socket(st);
        return -1;
    }
    return 0;
}

//服務器創建socket
int server_socket(int port)
{
    if (port < 0)
    {
        printf("function server_socket param not correct !\n");
        return -1;
    }
    //create socket
    int st = socket_create();
    if (st < 0)
    {
        return -1;
    }
    //reuseaddr
    if (socket_reuseaddr(st) < 0)
    {
        return -1;
    }
    //bind and listen
    if (socket_bind(st, port) < 0)
    {
        return -1;
    }
    return st;
}

//連接服務器
int connect_server(char *ipaddr,int port)
{
    if(port<0||ipaddr==NULL)
    {
        printf("function connect_server param not correct !\n");
        return -1;
    }
    int st=socket_create();
    if(st<0)
    {
        return -1;
    }
    //conect server
    struct sockaddr_in addr;
    memset(&addr,0,sizeof(addr));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(port);
    addr.sin_addr.s_addr=inet_addr(ipaddr);
    if(connect(st,(struct sockaddr *)&addr,sizeof(addr))==-1)
    {
        printf("connect failed ! error message :%s\n",strerror(errno));
        return -1;
    }
    return st;
}

//設置非阻塞
int setnonblock(int st)
{
    if (st < 0)
    {
        printf("function setnonblock param not correct !\n");
        //close socket
        close_socket(st);
        return -1;
    }
    int opts = fcntl(st, F_GETFL);
    if (opts < 0)
    {
        printf("func fcntl failed ! error message :%s\n", strerror(errno));
        return -1;
    }
    opts = opts | O_NONBLOCK;
    if (fcntl(st, F_SETFL, opts) < 0)
    {
        printf("func fcntl failed ! error message :%s\n", strerror(errno));
        return -1;
    }
    return opts;
}

//接收客戶端socket
int server_accept(int st)
{
    if (st < 0)
    {
        printf("function accept_clientsocket param not correct !\n");
        return -1;
    }
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    socklen_t len = sizeof(addr);
    int client_st = accept(st, (struct sockaddr *) &addr, &len);
    if (client_st < 0)
    {
        printf("accept client failed ! error message :%s\n", strerror(errno));
        return -1;
    } else
    {
        char ipaddr[20] = { 0 };
        sockaddr_toa(&addr, ipaddr);
        printf("accept by %s\n", ipaddr);
    }
    return client_st;
}

//關閉socket
int close_socket(int st)
{
    if (st < 0)
    {
        printf("function close_socket param not correct !\n");
        return -1;
    }
    close(st);
    return 0;
}

//將sockaddr_in轉化成IP地址
int sockaddr_toa(const struct sockaddr_in * addr, char * ipaddr)
{
    if (addr == NULL || ipaddr == NULL)
    {
        return -1;
    }
    unsigned char *p = (unsigned char *) &(addr->sin_addr.s_addr);
    sprintf(ipaddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
    return 0;
}

//接收消息
int socket_recv(int st)
{
    if (st < 0)
    {
        printf("function socket_recv param not correct !\n");
        return -1;
    }
    char buf[MAXBUF] = { 0 };
    int rc=0;
    rc=recv(st,buf,sizeof(buf),0);
    if(rc==0)
    {
        printf("client is close ! \n");
        return -1;
    }else if(rc<0)
    {
        /*
         * recv錯誤信息:Connection reset by peer * 錯誤原因:服務端給客戶端發送數據,但是客戶端沒有接收,直接關閉,那么就會報錯 * 如果客戶端接受了數據,再關閉,也不會報錯,rc==0. */
        printf("recv failed ! error message :%s \n",strerror(errno));
        return -1;
    }
    printf("%s",buf);
    //send message
    /*
    memset(buf,0,sizeof(buf));
    strcpy(buf,"i am server , i have recved !\n");
    if(send(st,buf,strlen(buf),0)<0)
    {
        printf("send failed ! error message :%s \n",strerror(errno));
        return -1;
    }
    */
    return 0;
}

//發送消息
int socket_send(int st)
{
    char buf[MAXBUF]={0};
    while(1)
    {
        //read from keyboard
        read(STDIN_FILENO,buf,sizeof(buf));
        if(buf[0]=='0')
        {
            break;
        }
        if(send(st,buf,strlen(buf),0)<0)
        {
            printf("send failed ! error message :%s \n",strerror(errno));
            return -1;
        }
        memset(buf,0,sizeof(buf));
    }
    return 0;
}
//網絡編程服務端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//htons()函數頭文件
#include <netinet/in.h>//inet_addr()頭文件
#include <fcntl.h>
#include <sys/epoll.h>
#include "pub.h"

#define MAXSOCKET 20

int main(int arg, char *args[])
{
    if (arg < 2)
    {
        printf("please print one param!\n");
        return -1;
    }
    //create server socket
    int listen_st = server_socket(atoi(args[1]));
    if (listen_st < 0)
    {
        return -1;
    }
    /*
     * 聲明epoll_event結構體變量ev,變量ev用於注冊事件,
     * 數組events用於回傳需要處理的事件
     */
    struct epoll_event ev, events[100];
    //生成用於處理accept的epoll專用文件描述符
    int epfd = epoll_create(MAXSOCKET);
    //把socket設置成非阻塞方式
    setnonblock(listen_st);
    //設置需要放到epoll池里的文件描述符
    ev.data.fd = listen_st;
    //設置這個文件描述符需要epoll監控的事件
    /*
     * EPOLLIN代表文件描述符讀事件
     *accept,recv都是讀事件
     */
    ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
    /*
     * 注冊epoll事件
     * 函數epoll_ctl中&ev參數表示需要epoll監視的listen_st這個socket中的一些事件
     */
    epoll_ctl(epfd, EPOLL_CTL_ADD, listen_st, &ev);

    while (1)
    {
        /*
         * 等待epoll池中的socket發生事件,這里一般設置為阻塞的 * events這個參數的類型是epoll_event類型的數組 * 如果epoll池中的一個或者多個socket發生事件, * epoll_wait就會返回,參數events中存放了發生事件的socket和這個socket所發生的事件 * 這里強調一點,epoll池存放的是一個個socket,不是一個個socket事件 * 一個socket可能有多個事件,epoll_wait返回的是有消息的socket的數目 * 如果epoll_wait返回事件數組后,下面的程序代碼卻沒有處理當前socket發生的事件 * 那么epoll_wait將不會再次阻塞,而是直接返回,參數events里面的就是剛才那個socket沒有被處理的事件 */
        int nfds = epoll_wait(epfd, events, MAXSOCKET, -1);
        if (nfds == -1)
        {
            printf("epoll_wait failed ! error message :%s \n", strerror(errno));
            break;
        }
        int i = 0;
        for (; i < nfds; i++)
        {
            if (events[i].data.fd < 0)
                continue;
            if (events[i].data.fd == listen_st)
            {
                //接收客戶端socket
                int client_st = server_accept(listen_st);
                /*
                 * 監測到一個用戶的socket連接到服務器listen_st綁定的端口
                 *
                 */
                if (client_st < 0)
                {
                    continue;
                }
                //設置客戶端socket非阻塞
                setnonblock(client_st);
                //將客戶端socket加入到epoll池中
                struct epoll_event client_ev;
                client_ev.data.fd = client_st;
                client_ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
                epoll_ctl(epfd, EPOLL_CTL_ADD, client_st, &client_ev);
                /*
                 * 注釋:當epoll池中listen_st這個服務器socket有消息的時候
                 * 只可能是來自客戶端的連接消息
                 * recv,send使用的都是客戶端的socket,不會向listen_st發送消息的
                 */
                continue;
            }
            //客戶端有事件到達
            if (events[i].events & EPOLLIN)
            {
                //表示服務器這邊的client_st接收到消息
                if (socket_recv(events[i].data.fd) < 0)
                {
                    close_socket(events[i].data.fd);
                    //接收數據出錯或者客戶端已經關閉
                    events[i].data.fd = -1;
                    /*這里continue是因為客戶端socket已經被關閉了,
                     * 但是這個socket可能還有其他的事件,會繼續執行其他的事件,
                     * 但是這個socket已經被設置成-1
                     * 所以后面的close_socket()函數都會報錯
                     */
                    continue;
                }
                /*
                 * 此處不能continue,因為每個socket都可能有多個事件同時發送到服務器端
                 * 這也是下面語句用if而不是if-else的原因,
                 */

            }
            //客戶端有事件到達
            if (events[i].events & EPOLLERR)
            {
                printf("EPOLLERR\n");
                //返回出錯事件,關閉socket,清理epoll池,當關閉socket並且events[i].data.fd=-1,epoll會自動將該socket從池中清除
                close_socket(events[i].data.fd);
                events[i].data.fd = -1;
                continue;
            }
            //客戶端有事件到達
            if (events[i].events & EPOLLHUP)
            {
                printf("EPOLLHUP\n");
                //返回掛起事件,關閉socket,清理epoll池
                close_socket(events[i].data.fd);
                events[i].data.fd = -1;
                continue;
            }
        }
    }
    //close epoll
    close(epfd);
    //close server socket
    close_socket(listen_st);
    return 0;
}
//網絡編程客戶端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//htons()函數頭文件
#include <netinet/in.h>//inet_addr()頭文件
#include <fcntl.h>
#include <sys/epoll.h>
#include "pub.h"

int main(int arg,char *args[])
{
    if(arg<2)
    {
        printf("please print two param !\n");
    }
    //端口號
    int port=atoi(args[2]);
    //服務端IP地址
    char ipaddr[30]={0};
    strcpy(ipaddr,args[1]);
    //connect server
    int st=connect_server(ipaddr,port);
    //send message
    //發送消息--
    socket_send(st);
    //close socket
    close(st);
    return 0;
}
.SUFFIXES:.c .o
CC=gcc
SRCS1=epoll_client.c\
    pub.c
SRCS2=epoll_server.c\
    pub.c
OBJS1=$(SRCS1:.c=.o)
OBJS2=$(SRCS2:.c=.o)
EXEC1=mclient
EXEC2=mserver

start:$(OBJS1) $(OBJS2)
    $(CC) -o $(EXEC1) $(OBJS1)
    $(CC) -o $(EXEC2) $(OBJS2)
    @echo "-------ok-----------"
.c.o:
    $(CC) -Wall -g -o $@ -c $<
clean:
    rm -f $(OBJS1)
    rm -f $(EXEC1)
    rm -f $(OBJS2)
    rm -f $(EXEC2)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM