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

Socket编程实践(4) --多进程并发server的更多相关文章

  1. Socket编程实践(10) --select的限制与poll的使用

    select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...

  2. C# socket编程实践

    C# socket编程实践——支持广播的简单socket服务器   在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...

  3. Socket编程实践(6) --TCP服务端注意事项

    僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...

  4. Socket编程实践(6) --TCPNotes服务器

    僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 sign ...

  5. Socket编程实践(12) --UDP编程基础

    UDP特点 无连接,面向数据报(基于消息,不会粘包)的传输数据服务; 不可靠(可能会丢包, 乱序, 反复), 但因此普通情况下UDP更加高效; UDP客户/服务器模型 UDP-API使用 #inclu ...

  6. C# socket编程实践——支持广播的简单socket服务器

    在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...

  7. Socket编程实践(2) Socket API 与 简单例程

    在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...

  8. Socket编程实践(1) 基本概念

    1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...

  9. Socket编程实践(4) --更复杂的过程server

    1.Socket地址复用 int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen); in ...

随机推荐

  1. Java 程序运行过程中的内存分析

    作为 java 程序员,都应该知道 Java 程序运行在 JVM(Java Virtual Machine,Java 虚拟机)上,可以把 JVM 理解成 Java 程序和操作系统之间的桥梁,JVM 实 ...

  2. 关于go语言的通道

    1.记一次gorountine导致的泄漏 在项目中使用https://github.com/deckarep/golang-set这个三方包造成了gorountine泄漏.先来看一下这个包的迭代器设置 ...

  3. 【移动开发】targetSdkVersion的作用

    在AndroidMenifest.xml中,常常会有下面的语句: <uses-sdk android:minSdkVersion="4" android:targetSdkV ...

  4. NLP系列(3)_用朴素贝叶斯进行文本分类(下)

    作者: 龙心尘 && 寒小阳 时间:2016年2月. 出处: http://blog.csdn.net/longxinchen_ml/article/details/50629110 ...

  5. Swift基础之如何使用iOS 9的Core Spotlight框架

    本文由CocoaChina译者KingOfOnePiece(博客)翻译 作者:GABRIEL THEODOROPOULOS?校对:hyhSuper 原文:How To Use Core Spotlig ...

  6. Bootstrap3 栅格系统-实例:流式布局容器

    如何让小屏幕设备上所有列不堆叠在一起,使用针对超小屏幕和中等屏幕设备所定义的类就可以做到,即 .col-xs-* 和 .col-md-*.举例: <!-- Stack the columns o ...

  7. Useful command for Docker

    Copy file from Container to Host: docker cp <containerId>:/file/path/within/container /host/pa ...

  8. 监听RecyclerView滑动到末端

    监听RecyclerView滑动到末端 效果图 实现 1. 添加滑动到末端的接口 package com.kongqw.recyclerviewdemo; /** * Created by kongq ...

  9. PHP Ajax JavaScript Json 实现天气信息获取

    使用第三方服务 间接方式 思路 使用到的服务 实现代码 前端完整代码 总结 要在自己的网站上添加一个天气预报功能,是一个很普通的需求,实现起来也不是很难.今天来介绍几个简单的方法. 使用第三方服务 有 ...

  10. ubuntu蓝牙音响配对成功但在声音设置中无法设置 解决

    ubuntu蓝牙音响配对成功但在声音设置中无法设置 解决 首先,连接蓝牙 但是,在声音设置中如下: 都没有发现设备??? 打开终端输入: ~$ pactl load-module module-blu ...