`
836811384
  • 浏览: 542327 次
文章分类
社区版块
存档分类
最新评论

最简单的socket套接字编程(2)–poll()和epoll()

 
阅读更多

最简单的socket套接字编程(2)–poll()和epoll()

作者:gaopenghigh,转载请注明出处。(原文地址)


本文主要介绍了使用poll()epoll()在UNIX环境下socket网络编程的主要步骤,实现 了一个简单的 服务器和客户端代码实例,实现了一个网络服务,该服务接受一个字符串的命令,执行该命 令,并且把结 果返回给客户端。

关于socket网络编程的基本概念以及多进程、多线程的网络服务器的原理和实例,参考最简单的socket套接字编程

关于poll()epoll()的介绍和用法,参考一步步理解Linux之IO(2)–高级IO

Client

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define BUFLEN 256

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;

    char buffer[BUFLEN];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);

    if ((server = gethostbyname(argv[1])) == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr,
          (char *)&serv_addr.sin_addr.s_addr,
          server->h_length);
    serv_addr.sin_port = htons(portno);

    while (1) {
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            error("ERROR opening socket");

        if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
            error("ERROR connecting");
        printf(">>> ");
        bzero(buffer, BUFLEN);
        fgets(buffer, BUFLEN, stdin);
        if((n = send(sockfd, buffer, strlen(buffer), 0)) <= 0)
             error("ERROR writing to socket");
        bzero(buffer, BUFLEN);
        while ((n = recv(sockfd, buffer, BUFLEN, 0)) > 0) {
            printf("%s",buffer);
        }
    }
    return 0;
}

poll()实现的server

/* A simple server to run system commands use poll */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <poll.h>
#include <fcntl.h>

#define PORTNO 4444
#define BACKLOG 10
#define BUFLEN 256
#define MAXCONN 100
#define TRUE 1
#define FALSE 0

void error(const char *msg) {
    perror(msg);
}

/*
 * function really do the service
 * run cmd geted from client and return the output to client
 */
void serve(struct pollfd *pfd) {
    int n;
    char buffer[BUFLEN];
    FILE *fp;

    bzero(buffer, BUFLEN);
    printf("in serve ,fd=%d\n", pfd->fd);
    if (pfd->revents & POLLIN) {                /* read */
        if ((n = read(pfd->fd, buffer, BUFLEN)) < 0)
            printf("ERROR reading from socket : %d", n);
        printf("CMD : %s\n",buffer);
        if ((fp = popen(buffer, "r")) == NULL)
            error("ERROR when popen");
        while (fgets(buffer, BUFLEN, fp) != NULL) {
            if (send(pfd->fd, buffer, BUFLEN, 0) == -1)
                error("send ERROR");
        }
        printf("serve end, closing %d\n", pfd->fd);
        close(pfd->fd);
        pfd->fd = -1;
        pclose(fp);
    }
}

/*
 * Init listen socket and bind it to addr, return the listen socket
 */
int init_server() {
    int sockfd;
    struct sockaddr_in serv_addr;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        error("ERROR opening socket");
        exit(1);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORTNO);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        error("ERROR on binding");
        exit(1);
    }

    return sockfd;
}

/*
 * set fd nonblocking, success return 0, fail return -1
 */
int setnonblocking(int fd) {
    if (fd > 0) {
        int flags = fcntl(fd, F_GETFL, 0);
        flags = flags|O_NONBLOCK;
        if (fcntl(fd, F_SETFL, flags) == 0) {
            printf("setnonblocking success!\n");
            return 0;
        }
    }
    return -1;
}

void printfd(struct pollfd array[], int n) {
    int i;
    printf("array = ");
    for(i = 0; i < n; i++)
        printf("[%d]:%d ", i, array[i].fd);
    printf("\n");
}

/*
 * Use poll() to serve for every connection
 */
int main(int argc, char *argv[]) {
    int endserver = FALSE;
    int listen_sock, conn_sock, pos, i, j;
    struct sockaddr_in cli_addr;
    socklen_t clilen = sizeof(cli_addr);

    /* INIT pollfd structures */
    struct pollfd allfds[MAXCONN];
    printf("sizeof(allfds)=%d\n", (int)sizeof(allfds));
    memset(allfds, 0, sizeof(allfds));
    int nfds = 0;    /* number of fds in allfds */
    int currentsize; /* current size of allfds */
    int poll_result;

    /* init listen socket, bind, listen */
    listen_sock = init_server();
    setnonblocking(listen_sock);
    listen(listen_sock, BACKLOG);

    /* add listen_socket to allfds array */
    allfds[0].fd = listen_sock;
    allfds[0].events = POLLIN;
    nfds++;


    while (endserver == FALSE) {
        printfd(allfds, nfds);
        printf("nfds=%d\n", nfds);
        /* wait for events on sockets, timeout = -1 means waite forever */
        poll_result = poll(allfds, nfds, -1);
        if (poll_result == -1) {
            error("poll ERROR");
            break;
        } else if (poll_result == 0) {
            printf("poll round timeout, enther another poll...\n");
            continue;
        }
        /*******************************************************************/
        /* One or more descriptors are readable                            */
        /*******************************************************************/
        currentsize = nfds;
        printf("correntsize=%d\n", currentsize);
        for (pos = 0; pos < currentsize; pos++) {
            if (allfds[pos].revents & POLLIN &&
                    allfds[pos].fd == listen_sock) {  /* listen socket */
                printf("event on listen sock\n");
                /* Accept all incomming connections */
                do {
                    printf("listen_sock=%d\n", listen_sock);
                    conn_sock = accept(listen_sock,
                                   (struct sockaddr *)&cli_addr, &clilen);
                    if (conn_sock < 0)
                        continue;
                    setnonblocking(conn_sock);
                    if (nfds >= MAXCONN) {
                        error("nfds >= MAXCONN");
                        break;
                    }
                    allfds[nfds].fd = conn_sock;
                    allfds[nfds].events = POLLIN;
                    nfds++;
                } while(conn_sock != -1);

            /* regular socket */
            } else {
                serve(&allfds[pos]);
            }

            /* comparess allfds, delete the items which fd = -1 */
            for (i = 0; i < nfds; i++) {
                if (allfds[i].fd == -1) {
                    for (j = i; j < nfds; j++) {
                        allfds[j] = allfds[j+1];
                    }
                    nfds--;
                    i--;
                }
            }
        }

    }

    /* end server */
    for (i = 0; i < nfds; i++) {
        if (allfds[i].fd >= 0)
            close(allfds[i].fd);
    }

    return 0;

}

epoll()实现的server

/* A simple server to run system commands use poll */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define PORTNO 4444
#define BACKLOG 10
#define BUFLEN 256
#define MAXCONN 100
#define TRUE 1
#define FALSE 0

void error(const char *msg) {
    perror(msg);
}

/*
 * function really do the service
 * run cmd geted from client and return the output to client
 */
void serve(int fd) {
    int n;
    char buffer[BUFLEN];
    FILE *fp;

    bzero(buffer, BUFLEN);
    printf("in serve ,fd=%d\n", fd);
    if ((n = read(fd, buffer, BUFLEN)) < 0)
        printf("ERROR reading from socket : %d", n);
    printf("CMD : %s\n",buffer);
    if ((fp = popen(buffer, "r")) == NULL)
        error("ERROR when popen");
    while (fgets(buffer, BUFLEN, fp) != NULL) {
        if (send(fd, buffer, BUFLEN, 0) == -1)
            error("send ERROR");
    }
    printf("serve end, closing %d\n", fd);
    pclose(fp);
}

/*
 * Init listen socket and bind it to addr, return the listen socket
 */
int init_server() {
    int sockfd;
    struct sockaddr_in serv_addr;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        error("ERROR opening socket");
        exit(1);
    }

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(PORTNO);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        error("ERROR on binding");
        exit(1);
    }

    return sockfd;
}

/*
 * set fd nonblocking, success return 0, fail return -1
 */
int setnonblocking(int fd) {
    if (fd > 0) {
        int flags = fcntl(fd, F_GETFL, 0);
        flags = flags|O_NONBLOCK;
        if (fcntl(fd, F_SETFL, flags) == 0) {
            printf("setnonblocking success!\n");
            return 0;
        }
    }
    return -1;
}


/*
 * Use poll() to serve for every connection
 */
int main(int argc, char *argv[]) {
    int endserver = FALSE;
    int listen_sock, conn_sock, n;
    struct sockaddr_in cli_addr;
    socklen_t clilen = sizeof(cli_addr);

    /* INIT pollfd structures */
    struct epoll_event ev, events[MAXCONN];
    int nfds;
    int epollfd = epoll_create(10);
    if (epollfd == -1) {
        printf("epoll_create error\n");
        exit(1);
    }

    /* init listen socket, bind, listen */
    listen_sock = init_server();
    setnonblocking(listen_sock);
    listen(listen_sock, BACKLOG);

    /* register listen_sock to epollfd */
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
        printf("ERROR: epoll_ctl\n");
        exit(1);
    }

    while (endserver == FALSE) {
        nfds = epoll_wait(epollfd, events, MAXCONN, -1);
        if (nfds == -1) {
            printf("epoll_wait ERROR\n");
            exit(1);
        }

        for (n = 0; n < nfds; n++) {
            if (events[n].data.fd == listen_sock) { /* listen socket */
                do {
                    printf("listen_sock=%d\n", listen_sock);
                    conn_sock = accept(listen_sock,
                                   (struct sockaddr *)&cli_addr, &clilen);
                    if (conn_sock < 0)
                        continue;
                    setnonblocking(conn_sock);
                    ev.events = EPOLLIN|EPOLLET;
                    ev.data.fd = conn_sock;
                    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock, &ev)
                            == -1) {
                        printf("epoll_ctl for conn_sock ERROR\n");
                        exit(1);
                    }
                } while(conn_sock != -1);
            } else { /* regular connection */
                serve(events[n].data.fd);
                ev.events = EPOLLIN|EPOLLET;
                ev.data.fd = conn_sock;
                if (epoll_ctl(epollfd, EPOLL_CTL_DEL, events[n].data.fd, &ev)
                        == -1) {
                    printf("epoll_ctl DEL ERROR\n");
                    exit(1);
                }
                close(events[n].data.fd);
            }
        }
    }

    return 0;
}

参考资料:



分享到:
评论

相关推荐

    linux网络编程

    udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程...

    Linux网络编程socket编程学习

    很详细的介绍了网络套接字socket的C/S模型TCP协议的服务器端和客户端的程序函数以及编写过程;重点介绍多路I/O转接服务器的实现,包括select函数poll函数epoll函数;最后介绍了UDP协议的服务器编写和本地套接字的...

    Linux网络编程 视频 教程

    UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信...

    C++教程网《Linux网络编程》视频百度云地址

    UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信...

    [免费]2018年C++教程网的linux网络编程视频百度云下载链接.rar

    UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信...

    c++教程网的linux网络编程视频下载

    UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信...

    C++教程网视频:linux网络编程

    UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信...

    2018年C++教程网的linux网络编程视频共41集百度云下载链接.rar

    UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信发展 ...

    Python313道企业面试题集锦(附答案).pdf

    简述基于 tcp 协议的套接字通信流程。 87.什么是粘包? socket 中造成粘包的原因是什什么? 哪些情况会发生粘包现 象?. 88.IO 多路复的作用? 89.select、poll、epoll 模型的区别?(属于多路复用 IO 的模型). ...

    Zeus:高性能,跨平台的Internet通信引擎。 使用本机套接字API开发。 旨在处理数百万个并发连接

    Zeus:高性能,跨平台的Internet通信引擎。 使用本机套接字API开发。 旨在处理数百万个并发连接

    python 并发编程 多路复用IO模型详解

    它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。它的流程如图: select是多路复用的一种 当用户进程调用了select,那么整个进程会被block...

    详解socket阻塞与非阻塞,同步与异步、I/O模型

    socket阻塞与非阻塞,同步与异步 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步: 所谓同步,就是在c端发出一个...

    reactor-io-pattern

    Reactor 是事件驱动的,并使用 os api(select、poll、epoll、kqueue、iocp 等)来调度 socket 事件。 编程只需要向Reactor注册事件源socket发生的读、写或except以及具体的事件处理程序。 Reactor 自动将套接字...

Global site tag (gtag.js) - Google Analytics