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...\nserver 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...\nclient 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

转载于:https://www.cnblogs.com/itrena/p/5926938.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
如果`connect`函数返回-1,表示连接失败。然而,有时候即使`connect`返回-1,仍然可以进行正常通信。这可能是因为网络连接过程中存在一些延迟或其他因素导致连接建立时出现错误。在此情况下,你可以尝试发送和接收数据来验证连接是否确实建立成功。 然而,不建议依赖连接失败的返回值来判断连接状态。正确的做法是检查`connect`函数的返回值,并根据返回值进行适当的错误处理。例如,可以使用`perror`函数打印错误信息,并根据错误类型采取相应的措施。 以下是一个示例代码,展示如何处理`connect`函数的返回值: ```c #include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { // 创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 设置目标主机的IP地址和端口号 struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 连接到目标主机 int connect_result = connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 判断连接状态 if (connect_result == -1) { perror("连接失败"); // 进行错误处理 close(sockfd); return 1; } // 连接成功,进行通信 printf("连接成功\n"); // 进行数据发送和接收操作 // 关闭套接字 close(sockfd); return 0; } ``` 在这个示例中,当`connect`函数返回-1时,会打印连接失败的错误信息,并进行相应的错误处理。如果连接成功,则会打印连接成功的消息,并继续进行后续的通信操作。 注意,虽然在某些情况下连接失败的返回值可能不会影响后续的通信,但仍然建议正确地处理连接失败的情况,以确保代码的健壮性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值