zoukankan      html  css  js  c++  java
  • Socket编程实践(4) --多进程并发server

    1.Socket地址复用

    int getsockopt(int sockfd, int level, int optname,
                   void *optval, socklen_t *optlen);
    int setsockopt(int sockfd, int level, int optname,
                   const void *optval, socklen_t optlen);

    服务端尽可能使用SO_REUSEADDR,在绑定之前尽可能调用setsockopt来设置SO_REUSEADDR套接字选项。该选项可以使得server不必等待TIME_WAIT状态消失就可以重启服务器(对于TIME_WAIT状态会在后面续有叙述).

    可以在bind之前添加代码(完整代码请参照博文最后):

        int on = 1;
        if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,
                       &on,sizeof(on)) == -1)
            err_exit("setsockopt SO_REUSEADDR error");

    用以支持地址复用.

     

    2.process-per-connecton

    我们的echo服务器最大的缺点就是无法支持多客户连接,即使客户端能够连接到服务器上(client端connect时并没有出错返回), 服务器也不为该客户做服务,(直接没什么反应),虽然链接是有的(也就是说,客户端是已经连接到服务器上的了,但是服务器就是不搭理你....), 我们提出的改进方案是process-per-connection(一条连接一个进程, 我们在多线程那一章中曾经提出过一条连接一个线程, 这种方案相比较而言能够比多进程拥有更高的并发量);

    /** 示例:echo server改进, 多进程模型(client并未更改)**/
    void echo(int clientfd);
    int main()
    {
        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (listenfd == -1)
            err_exit("socket error");
        int on = 1;
        if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,
                       &on,sizeof(on)) == -1)
            err_exit("setsockopt SO_REUSEADDR error");
    
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(8001);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
            err_exit("bind error");
        if (listen(listenfd, SOMAXCONN) == -1)
            err_exit("listen error");
    
        struct sockaddr_in clientAddr;
        //谨记: 此处一定要初始化
        socklen_t addrLen = sizeof(clientAddr);
        while (true)
        {
            int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);
            if (clientfd == -1)
                err_exit("accept error");
            //打印客户IP地址与端口号
            cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)
                 << ", " << ntohs(clientAddr.sin_port) << endl;
    
            pid_t pid = fork();
            if (pid == -1)
                err_exit("fork error");
            else if (pid > 0)
                close(clientfd);
            //子进程处理链接
            else if (pid == 0)
            {
                close(listenfd);
                echo(clientfd);
                //子进程一定要exit, 否则的话, 该子进程也会回到accept处
                exit(EXIT_SUCCESS);
            }
        }
        close(listenfd);
    }
    void echo(int clientfd)
    {
        char buf[512] = {0};
        int readBytes;
        while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)
        {
            cout << buf;
            if (write(clientfd, buf, readBytes) == -1)
                err_exit("write socket error");
            memset(buf, 0, sizeof(buf));
        }
        if (readBytes == 0)
        {
            cerr << "client connect closed..." << endl;
            close(clientfd);
        }
        else if (readBytes == -1)
            err_exit("read socket error");
    }

    完整代码实现:

    http://download.csdn.net/detail/hanqing280441589/8458053

     

    3. P2P聊天程序设计与实现

    server端与client都有两个进程:

        (1)父进程负责从socket中读取数据将其写至终端, 由于父进程使用的是read系统调用的阻塞版本, 因此如果socket中没有数据的话, 父进程会一直阻塞; 如果read返回0, 表示对端连接关闭, 则父进程会发送SIGUSR1信号给子进程, 通知其退出;

        (2)子进程负责从键盘读取数据将其写入socket, 如果键盘没有数据的话, 则fgets调用会一直阻塞;

    //serever端代码与说明
    int main()
    {
        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if (listenfd == -1)
            err_exit("socket error");
        int on = 1;
        if (setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,
                       &on,sizeof(on)) == -1)
            err_exit("setsockopt SO_REUSEADDR error");
    
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(8001);
        addr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(listenfd, (const struct sockaddr *)&addr, sizeof(addr)) == -1)
            err_exit("bind error");
        if (listen(listenfd, SOMAXCONN) == -1)
            err_exit("listen error");
    
        struct sockaddr_in clientAddr;
        socklen_t addrLen = sizeof(clientAddr);
        int clientfd = accept(listenfd, (struct sockaddr *)&clientAddr, &addrLen);
        if (clientfd == -1)
            err_exit("accept error");
        close(listenfd);
        //打印客户IP地址与端口号
        cout << "Client information: " << inet_ntoa(clientAddr.sin_addr)
             << ", " << ntohs(clientAddr.sin_port) << endl;
    
        char buf[512] = {0};
        pid_t pid = fork();
        if (pid == -1)
            err_exit("fork error");
        //父进程: socket -> terminal
        else if (pid > 0)
        {
            int readBytes;
            while ((readBytes = read(clientfd, buf, sizeof(buf))) > 0)
            {
                cout << buf;
                memset(buf, 0, sizeof(buf));
            }
            if (readBytes == 0)
                cout << "client connect closed...
    server exiting..." << endl;
            else if (readBytes == -1)
                err_exit("read socket error");
            //通知子进程退出
            kill(pid, SIGUSR1);
        }
        //子进程: keyboard -> socket
        else if (pid == 0)
        {
            signal(SIGUSR1, sigHandler);
            while (fgets(buf, sizeof(buf), stdin) != NULL)
            {
                if (write(clientfd, buf, strlen(buf)) == -1)
                    err_exit("write socket error");
                memset(buf, 0, sizeof(buf));
            }
        }
        close(clientfd);
        exit(EXIT_SUCCESS);
    }
    //client端代码与说明
    int main()
    {
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
            err_exit("socket error");
    
        //填写服务器端口号与IP地址
        struct sockaddr_in serverAddr;
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port = htons(8001);
        serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        if (connect(sockfd, (const struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)
            err_exit("connect error");
    
        char buf[512] = {0};
        pid_t pid = fork();
        if (pid == -1)
            err_exit("fork error");
        //父进程: socket -> terminal
        else if (pid > 0)
        {
            int readBytes;
            while ((readBytes = read(sockfd, buf, sizeof(buf))) > 0)
            {
                cout << buf;
                memset(buf, 0, sizeof(buf));
            }
            if (readBytes == 0)
                cout << "server connect closed...
    client exiting..." << endl;
            else if (readBytes == -1)
                err_exit("read socket error");
            kill(pid, SIGUSR1);
        }
        //子进程: keyboard -> socket
        else if (pid == 0)
        {
            signal(SIGUSR1, sigHandler);
            while (fgets(buf, sizeof(buf), stdin) != NULL)
            {
                if (write(sockfd, buf, strlen(buf)) == -1)
                    err_exit("write socket error");
                memset(buf, 0, sizeof(buf));
            }
        }
        close(sockfd);
        exit(EXIT_SUCCESS);
    }

    完整代码实现:

    http://download.csdn.net/detail/hanqing280441589/8460013

  • 相关阅读:
    4. Git撤销修改
    3. Git如何管理修改
    2. Git的工作区、暂存区和版本库
    1. Git创建仓库、查看工作区状态
    微信平台接入Web页面功能接口(C#)
    Python之路【第六篇】:Python运算符
    Python之路【第五篇】:Python基本数据类型
    Python之路【第四篇】:Pycharm集成开发环境
    Python之路【第二篇】:Python简介、解释器与编码
    Python之路【第七篇】:Python流程控制
  • 原文地址:https://www.cnblogs.com/itrena/p/5926938.html
Copyright © 2011-2022 走看看