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

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    解释机器学习模型的一些方法(一)——数据可视化
    机器学习模型解释工具-Lime
    Hive SQL 语法学习与实践
    LeetCode 198. 打家劫舍(House Robber)LeetCode 213. 打家劫舍 II(House Robber II)
    LeetCode 148. 排序链表(Sort List)
    LeetCode 18. 四数之和(4Sum)
    LeetCode 12. 整数转罗马数字(Integer to Roman)
    LeetCode 31. 下一个排列(Next Permutation)
    LeetCode 168. Excel表列名称(Excel Sheet Column Title)
    论FPGA建模,与面向对象编程的相似性
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4639205.html
Copyright © 2011-2022 走看看