1. 程式人生 > >Linux網路程式設計 -- poll實現多路IO轉接伺服器

Linux網路程式設計 -- poll實現多路IO轉接伺服器

server.c

#include <poll.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#include "wrap.h"

#define OPEN_MAX 1024
#define SERV_PORT 8888

int main()
{
    ssize_t n;
    int
opt = 1; int i, j, maxi; int nready; //接收poll返回值, 記錄滿足監聽事件的fd個數 int listenfd, connfd, sockfd; char buf[BUFSIZ], clie_IP[INET_ADDRSTRLEN]; struct sockaddr_in clie_addr, serv_addr; socklen_t clie_addr_len; struct pollfd client[OPEN_MAX]; listenfd = Socket(AF_INET, SOCK_STREAM, 0
); setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); bzero(&serv_addr, sizeof(serv_addr)); serv_addr.sin_family= AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port= htons(SERV_PORT); Bind(listenfd, (struct sockaddr *)&serv_addr, sizeof
(serv_addr)); Listen(listenfd, 128); client[0].fd = listenfd; //要監聽的第一個檔案描述符listenfd 存入client[0] client[0].events = POLLIN; //listenfd監聽讀事件 maxi = 0; //client[]陣列有效元素中最大元素下標 for (i = 1; i < OPEN_MAX; ++i) { client[i].fd = -1; //將存放客戶端的檔案描述符的陣列初始化為 -1 } while (1) { nready = poll(client, maxi+1, -1); //阻塞監聽是否有客戶端連線請求 //listenfd有讀事件就緒->有新的客戶端需要連線 if (client[0].revents & POLLIN) { clie_addr_len = sizeof(clie_addr); //接收客戶端請求 此時Accept 不會阻塞 connfd = Accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len); printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &clie_addr.sin_addr, clie_IP, sizeof(clie_IP)), ntohs(clie_addr.sin_port)); for (i = 1; i < OPEN_MAX; ++i) { if (client[i].fd < 0) { client[i].fd = connfd; //找到client[]中空閒的位置,存放accept返回的connfd break; } } if (OPEN_MAX == i) { perr_exit("使用者連線量過大"); } client[i].events = POLLIN; //設定剛剛返回的connfd,監聽讀事件 if (i > maxi) { maxi = i; //更新client[]中最大元素下標 } if (--nready <= 0) { continue; //沒有更多就緒事件時,繼續回到poll阻塞 } } //前面的if沒滿足,說明沒有listenfd滿足. 檢測client[] 看是哪個connfd就緒 for (i = 1; i <= maxi; ++i) //注意等號 臨界條件 { sockfd = client[i].fd; if (sockfd < 0) { continue; } if (client[i].revents & POLLIN) { n = Read(sockfd, buf, BUFSIZ); if (n < 0) { if (errno == ECONNRESET) //收到RST標誌 { printf("client[%d] aborted connection\n", i); Close(sockfd); client[i].fd = -1; /* poll中不監控該檔案描述符,直接置為-1即可,不用像select中那樣移除 */ } else { perr_exit("read error"); } } else if (0 == n) //說明客戶端先關閉連結 { printf("client[%d] closed connection\n", i); Close(sockfd); client[i].fd = -1; } else { for (j = 0; j < n; j++) { buf[j] = toupper(buf[j]); } Write(sockfd, buf, n); } if (--nready <= 0) { break; } } } } Close(listenfd); return 0; }

wrap.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>

//輸出錯誤資訊並退出
void perr_exit(const char *str)
{
    perror(str);
    exit(-1);
}    

//建立套接字 帶出錯處理
int Socket(int family, int type, int protocol)
{
    int n;

    n = socket(family, type, protocol);
    if (n < 0)
    {
        perr_exit("socket error");
    }

    return n;
}


int Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
    int n;

    n = bind(fd, sa, salen);
    if (n < 0)
    {
        perr_exit("bing error");
    }

    return n;
}

int Listen(int fd, int backlog)
{
    int n;

    n = listen(fd, backlog);
    if (n < 0)
    {
        perr_exit("listen error");
    }

    return n;
}

int Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
    int n;

    n = connect(fd, sa, salen);
    if (n < 0)
    {
        perr_exit("connect error");
    }

    return n;
}

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
    int n;

again:
    n = accept(fd, sa, salenptr);
    if (n < 0)
    {
        if (errno == ECONNABORTED || errno == EINTR)
        {
            goto again;
        }
        else
        {
            perr_exit("accept error");
        }
    }

    return n;
}

ssize_t Read(int fd, void *ptr, size_t nbytes)
{
    ssize_t n;

again:
    n = read(fd, ptr, nbytes);
    if (-1 == n)
    {
        if (EINTR == errno)
        {
            goto again;
        }
        else
        {
            return -1;
        }
    }

    return n;
}

ssize_t Write(int fd, const void *ptr, size_t nbytes)
{
    ssize_t n;

again:
    n = write(fd, ptr, nbytes);
    if (-1 == n)
    {
        if (EINTR == errno)
        {
            goto again;
        }
        else
        {
            return -1;
        }
    }

    return n;
}

int Close(int fd)
{
    int n;

    n = close(fd);
    if (-1 == n)
    {
        perr_exit("close error");
    }

    return n;
}