`

Unix 网络编程_阅读笔记四 线程、客户/服务器程序设计范式

 
阅读更多

Unix 网络编程_阅读笔记 四 (Socket高级篇之线程、客户/服务器程序设计范式)

--Unix Network Programming

 王宇 原创并发布


本文代码,在以下环境下编译通过

  • CentOS 6.4
  • Kernal version: 2.6.32
  • GCC version: 4.4.7

一、 线程

父进程accept一个连接,fork一个子进程,该子进程处理与该连接对端的客户之间的通信,这种范式多少年来一直用的挺好,fork调用却存在一些问题:

  • fork是昂贵的。
  • fork返回之后父子进程之间信息的传递需要进程间通信(IPC)机制。调用fork之前父进程向尚未存在的子进程传递信息相当容易,因为子进程将从父进程数据空间及所有描述符的一个副本开始运行。然而从子进程往父进程返回信息却比较费力。

线程有助于解决这两个问题。线程有时称为轻权进程(lightweight process)。同一进程内的所有线程共享相同的全局内存。这使得线程之间易于共享信息,然而伴随这种容易性而来的却是同步问题。

线程除了共享全局变量外还共享:

  • 进程指令
  • 大多数数据
  • 打开的文件(即描述符)
  • 信号处理函数和信号处置
  • 当前工作目录
  • 用户ID和组ID

不过每个线程有各自的:

  • 线程ID
  • 寄存器集合,包括程序计数器和栈指针
  • 栈(用于存放局部变量和返回地址)
  • errno
  • 信号掩码
  • 优先级

1、基本线程函数:创建和终止

  • pthread_create 函数 创建并启动一个线程
  • pthread_join 函数 等待给定线程终止
  • pthread_self 函数 获得线程自身线程ID
  • pthread_detach 函数 转变为脱离状态(detached)
  • pthread_exit 函数 终止线程

2、使用线程的str_cli函数

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<pthread.h>
#include<errno.h>

#define SERV_PORT 51000
#define MAXLINE 4096 
#define SA struct sockaddr

char* Ip_address = "192.168.153.130";

static int socket_fd;
static FILE *fp;


size_t readline(int fd, void *vptr, size_t maxlen)
{
    size_t n, rc;
    char c, *ptr;

    ptr = vptr;

    for(n = 1; n < maxlen; n++)
    {
again:
        if( (rc = read(fd, &c, 1)) == 1)
        {
            *ptr++ = c;     
            if(c == '\n')
            {
                break;
            }
        }
        else if(rc == 0)
        {
            return(n - 1);
        }
        else
        {
            if(errno == EINTR)
            {
                goto again; 
            }     
            return (-1);
        }

    }

    return(n);
}

ssize_t writen(int fd, const void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nwritten;
    const char *ptr;

    ptr = vptr;
    nleft = n;
    while(nleft > 0)
    {
        if((nwritten = write(fd, ptr, nleft)) <= 0)
        {
            if(nwritten < 0 && errno == EINTR)
            {
                nwritten = 0;
            }
            else
            {
                return(-1);
            }

        }

        nleft -= nwritten;
        ptr += nwritten;    
    }

    return(n);
}

void* copyto(void *arg)
{
    char sendline[MAXLINE];

    while(fgets(sendline, MAXLINE, fp) != NULL )
    {
        if(writen(socket_fd, sendline, strlen(sendline)) == -1)
        {
            perror("Error: write socket!\n");
        }

    }
    if(shutdown(socket_fd, SHUT_WR) == -1)
    {
        perror("Error: shutdown!\n");
    }

    return(NULL);
}


void str_cli(FILE *fp_arg, int socket_fd_arg)
{
    char recvline[MAXLINE];
    pthread_t tid;
    socket_fd = socket_fd_arg;
    fp = fp_arg;

    if(pthread_create(&tid, NULL, copyto, NULL) != 0)
    {
        perror("Error: pthread_create!\n");
    }

    while(readline(socket_fd, recvline, MAXLINE) > 0)
    {
        fputs(recvline, stdout);
    }
}


int main()
{
    int socket_fd, connect_rt;
    struct sockaddr_in servaddr;
    char err_message[MAXLINE];

    socket_fd = socket(AF_INET, SOCK_STREAM, 0);

    if(socket_fd == -1)
    {
        printf("Error: created socket!\n");
        exit(1);
    }

    bzero(&servaddr, sizeof(servaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, Ip_address, &servaddr.sin_addr);

    connect_rt = connect(socket_fd, (SA *)&servaddr, sizeof(servaddr));

    if(connect_rt != 0)
    {
        perror(err_message);    
        printf("Error: connect socket!\n");
        printf("%s\n", err_message);
        exit(1);
    }

    str_cli(stdin, socket_fd);


    exit(0);
}

 

 

3、使用线程的TCP回射服务程序

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<netinet/in.h>
#include<errno.h>
#include<pthread.h>

#define SA struct sockaddr
#define SERV_PORT 51000
#define MAXLINE 4096 
#define LISTENQ 1024


void str_echo(int socket_fd)
{
    ssize_t n;
    char buf[MAXLINE];

again:
    while( (n = read(socket_fd, buf, sizeof(buf))) > 0)
    {
        if(write(socket_fd, buf, n) == -1 )
        {
            perror("Error:write.\n");
        }
    }

    if( n < 0 && errno == EINTR)
    {
        goto again;
    }
    else if(n < 0)
    {
        perror("ERROR: str_echo\n");
    }

}


static void* doit(void *arg)
{
    int conn_fd;
    conn_fd = *((int *)arg);
    free(arg);

    pthread_detach(pthread_self());
    str_echo(conn_fd);

    close(conn_fd);
    return (NULL);
}


int main()
{
    int socket_fd, *connect_fd; 
    struct sockaddr_in servaddr, clientaddr;
    socklen_t client_len;
    pid_t child_pid;
    pthread_t tid;

    socket_fd = socket(AF_INET, SOCK_STREAM, 0);

    if(socket_fd == -1)
    {
        perror("Error: created socket!\n");
        exit(1);
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERV_PORT);

    if((bind(socket_fd, (SA *)&servaddr, sizeof(servaddr))) == -1)
    {
        perror("Error: Bind port!\n");
        exit(1);
    }

    if((listen(socket_fd, LISTENQ)) == -1)
    {
        perror("Error: Listen!\n");
        exit(1);
    }

    for(;;)
    {
        client_len = sizeof(clientaddr);
        connect_fd = (int*)malloc(sizeof(int));

        if(connect_fd == NULL)
        {
            perror("Error: malloc!\n");
            exit(1);
        }

        *connect_fd = accept(socket_fd, (SA *)&clientaddr, &client_len);

        if(pthread_create(&tid, NULL, &doit, connect_fd) == -1)
        {
            perror("Error: pthread!\n");
            exit(1);
        }

    }

    exit(0);
}

 

 

4、线程特定数据

把一个未线程化的程序转换成使用线程的版本时,有时会碰到因其中有函数使用静态变量而引起的一个常见编程错误。 有以下几种解决方案:

  • 使用线程特定数据。这个办法并不简单,而且转换成了只能在支持线程的系统上工作的函数。本办法的优点是调用顺序无需变动,所有变动都体现在库函数中而非调用这些函数的应用程序中。
  • 改变调用顺序
  • 改变接口的结构,避免使用静态变量,这样函数就可以是线程安全的。

每个系统支持有限数量的线程特定数据元素。POSIX要求这个限制不小于128(每个进程)

5、互斥锁

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mptr);
int pthread_mutex_unlock(pthread_mutex_t *mptr);
Both return: 0 if OK, positive Exxx value on error
 
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define NLOOP 200
int counter;
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;


void* doit(void *vptr)
{
    int i, value;

    for(i = 0; i < NLOOP; i++)
    {
        if(pthread_mutex_lock(&counter_mutex) !=0)  
        {
            perror("Error: lock\n");
        }

        value = counter;
        printf("%d: %d\n", pthread_self(), value + 1);
        counter = value + 1;

        if(pthread_mutex_unlock(&counter_mutex) !=0)    
        {
            perror("Error: unlock\n");
        }
    }

    return(NULL);
}


int main()
{

    pthread_t t_id_A, t_id_B;

    pthread_create(&t_id_A, NULL, &doit, NULL);
    pthread_create(&t_id_B, NULL, &doit, NULL);

    pthread_join(t_id_A, NULL);
    pthread_join(t_id_B, NULL);

    exit(0);
}
 

6、条件变量

修改以上代码,使counter变量每增加10,则打印一个分隔线:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>

#define NLOOP 100
int counter;
pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

void* doit(void *vptr)
{
    int i, value;

    for(i = 0; i < NLOOP; i++)
    {
        if(pthread_mutex_lock(&counter_mutex) !=0)  
        {
            perror("Error: lock\n");
        }

        value = counter;
        printf("%d: %d\n", pthread_self(), value + 1);
        counter = value + 1;

        if((counter%10) == 0)
        {
            pthread_cond_signal(&cond);
        }

        if(pthread_mutex_unlock(&counter_mutex) !=0)    
        {
            perror("Error: unlock\n");
        }

        sleep(1);
    }

    return(NULL);
}

void* do_split(void *vptr)
{
    for(;;)
    {
        if(pthread_mutex_lock(&counter_mutex) !=0)  
        {
            perror("Error: lock\n");
        }

        pthread_cond_wait(&cond, &counter_mutex);

        if(counter == (NLOOP * 2))
        {
            pthread_exit(0);
        }

        printf("%d: ----------\n", pthread_self());

        if(pthread_mutex_unlock(&counter_mutex) !=0)    
        {
            perror("Error: unlock\n");
        }
    }
}


int main()
{

    pthread_t t_id_A, t_id_B, t_id_C;

    pthread_create(&t_id_A, NULL, &do_split, NULL);
    pthread_create(&t_id_B, NULL, &doit, NULL);
    pthread_create(&t_id_C, NULL, &doit, NULL);

    pthread_join(t_id_A, NULL);
    pthread_join(t_id_B, NULL);
    pthread_join(t_id_C, NULL);

    exit(0);
}

 


二、客户/服务器程序设计范式

1、服务器程序设计范式:

  • 1、迭代服务器(无进程控制,用作测量基准)
  • 2、并发服务器,每个客户请求fork一个子进程
  • 3、预先派生子进程,每个子进程无保护地调用accept
  • 4、预先派生子进程,使用文件上锁保护accept
  • 5、预先派生子进程,使用线程互斥锁上锁保护accept
  • 6、预先派生子进程,父进程向子进程传递套接字描述符
  • 7、并发服务器,每个客户请求创建一个线程
  • 8、预先创建线程服务器,使用互斥锁保护accept
  • 9、预先创建线程服务器,由主线程调用accept

2、总结:

  • 当系统负载较轻时,每来一个客户请求现场派生一个子进程为之服务的传统并发服务器程序模型就足够了。这个模型甚至可以与inetd结合使用,也就是inetd处理每个连接的接受。我们的其他意见是就重负荷运行的服务器而言的,譬如Web服务器。
  • 相比传统的每个客户fork一次设计范式,预先创建一个子进程池或一个线程池的设计范式能够把进程控制CPU时间降低10倍或以上。编写这些范式的程序并不复杂,不过需超越本章所给例子的是:监视闲置子进程个数,随着所服务客户数的动态变化而增加或减少这个数目
  • 某些实现允许多个子进程或线程阻塞在同一个accept调用中,另一些实现却要求包绕accept调用安置某种类型的锁加以保护。文件锁或Pthread互斥锁都可以使用。
  • 让所有子进程或线程自行调用accept通常比让父进程或主线程独自调用accept并把描述符传递给子进程或线程来的简单而快速。
  • 由于潜在select冲突的原因,让所有子进程或线程阻塞在同一个accept调用中比让它们阻塞在同一个select调用中更可取。
  • 使用线程通常远快于使用进程。不过选择每个客户一个子进程还是每个客户一个线程取决于操作系统提供什么支持,还可能取决于为服务每个客户需激活其他什么程序(若有其他程序需激活的话)举例子说,如果accept客户连接的服务器调用fork和exec,那么fork一个单线程的进程可能快于fork一个多线程的进程
分享到:
评论

相关推荐

    《Python核心编程》读书笔记

    Python的设计简洁而强大,拥有丰富的标准库,支持正则表达式,提供系统调用接口,支持多线程,具有垃圾回收机制,支持交互式编程和多种编程范式。Python的语法允许程序员用更少的代码行表达概念,其代码密度可以是...

    关于Python的个人学习笔记

    随着技能的提升,可以涉及更高级的主题,如异常处理、多线程、网络编程和数据库操作。Python丰富的生态系统,如数据分析(NumPy、Pandas)、科学计算(SciPy)、Web开发(Django、Flask)和人工智能(TensorFlow、...

    网络流量采样在高吞吐量链路异常检测中的应用研究

    内容概要:本文探讨了高吞吐量网络链路异常检测中流量采样技术的应用及其效果。面对现代分布式信息系统频繁遭受的网络安全威胁,特别是互联网服务提供商(ISP)面临的威胁,作者提出一种通过减少数据采样频率以降低异常检测计算复杂度的方法。文中介绍了实验环境、系统架构、采用的数据聚合与采样方法以及用于检测异常的人工智能模型(基于自编码器神经网络)。通过对一个真实中型ISP生产环境中实际网络流量数据进行研究,该研究展示了即使在较低采样频率情况下仍能保持较高的异常检测准确性,尤其是针对持续时间较长的DDoS攻击更为显著。此外,论文还验证了所提系统的有效性和应用潜力,为构建高效的网络安全监控机制提供了新思路。 适用人群:对于计算机网络安全、数据分析或机器学习有兴趣的研究人员和从业人员,特别是那些专注于提高异常检测性能和应对高流量数据流的技术人员。 使用场景及目标:适用于希望在不影响业务操作的前提下引入额外层次防护措施的企业级网络管理员;研究者可参考本文中提出的流量预处理方式来探索不同的统计分布和采样间隔设置;企业可以通过部署该类系统快速响应潜在的安全事件并降低成本。

    unity ui画线插件

    unity ui画线插件

    比例公平性的下行链路资源分配在基于OFDMA的中继网络中的应用与优化(可复现,有问题请联系博主)

    内容概要:本文研究了在基于正交频分多址接入(OFDMA)的中继网络中进行带有比例公平性的下行链路资源分配问题。作者们通过联合优化中继选择、子载波分配和功率分配问题,并采用拉格朗日对偶分解方法求解这一复杂的NP完全问题。实验结果显示所提出的算法相较于启发式算法能显著提高系统吞吐量,并带来更好的用户间公平性。 适合人群:通信工程、无线网络优化、电信行业研发工程师和研究人员。 使用场景及目标:主要应用于提升4G移动通信系统的频谱效率及缓解频率选择衰落的问题,确保多用户之间的传输速率更加公平。同时适用于研究OFDMA技术及其相关领域的学者和技术专家。 其他说明:文中提供了详细的数学模型和模拟结果图表支持理论发现,并讨论了各种假设条件下的性能对比。此外还探讨了连续松弛技巧在解决NP完全问题时的应用价值以及通过调整算法参数来获得近似最优解的方法论意义。

    [程序系统设计]MATLAB打印纸缺陷检测GUI(不同缺陷类型,GUI界面).zip

    程序系统设计]MATLAB打印纸缺陷检测GUI(不同缺陷类型,GUI界面) [程序系统设计]MATLAB打印纸缺陷检测GUI(不同缺陷类型,GUI界面) [程序系统设计]MATLAB打印纸缺陷检测GUI(不同缺陷类型,GUI界面) [程序系统设计]MATLAB打印纸缺陷检测GUI(不同缺陷类型,GUI界面) [程序系统设计]MATLAB打印纸缺陷检测GUI(不同缺陷类型,GUI界面)

    邮件分拣组态王6.55和西门子S7-200plc联机程序2023,带io表,运行效果视频 ,邮件分拣; 组态王6.55; 西门子S7-200plc; 联机程序2023; IO表; 运行效果视频,邮件

    邮件分拣组态王6.55和西门子S7-200plc联机程序2023,带io表,运行效果视频 ,邮件分拣; 组态王6.55; 西门子S7-200plc; 联机程序2023; IO表; 运行效果视频,邮件分拣组态王6.55与S7-200PLC联机程序2023版:带IO表运行效果视频

    基于关系变化和跨时间差异注意力机制的遥感影像变化检测(可复现,有问题请联系博主)

    内容概要:本文提出了一种新的基于跨时间差异(CTD)注意力机制的变化检测方法(称为CTD-Former),用于高效地提取多时相遥感图像中的变化特征。作者重新审视了自注意力机制并深入挖掘多时间相位图像间的关系变化,构建CTD变压器编码器和解码器来增强这些特征。此外,还引入了一致性感知模块(CPB)以保护变化区域的空间结构。实验结果显示,在LEVIR-CD、WHU-CD和CLCD数据集上,该模型相比于当前最优的方法表现出更好的性能。 适合人群:对深度学习、遥感图像处理、尤其是变化检测感兴趣的研究人员和技术专家,特别是熟悉变换器网络架构的从业者。 使用场景及目标:此方法适用于需要从多时相对比遥感影像中识别变化情况的任务,如环境监测、灾害评估、城市规划等领域内的应用开发,能够帮助研究者和决策者更准确地了解地面物体随时间的变化趋势。 其他说明:源代码可在GitHub仓库中获取,这为未来的研究提供了一个重要的参考平台,有助于推动该领域的进一步发展。

    [matlab程序系统设计]MATLAB的视频图像去雾(处理视频,GUI界面).zip

    该项目是个人实践项目,答辩评审分达到90分,代码都经过调试测试,确保可以运行!,可用于小白学习、进阶。 该资源主要针对计算机、通信、人工智能、自动化等相关专业的学生、老师或从业者下载使用,亦可作为期末课程设计、课程大作业、毕业设计等。 项目整体具有较高的学习借鉴价值!基础能力强的可以在此基础上修改调整,以实现不同的功能。 欢迎下载,欢迎沟通,互相学习,共同进步!提供答疑!

    temp_sh.zip

    fajslghjlghg

    2008-2020年各省每十万人口高等学校平均在校生数数据

    2008-2020年各省每十万人口高等学校平均在校生数数据 1、时间:2008-2020年 2、来源:国家统计j、统计nj 3、指标:行政区划代码、地区名称、年份、每十万人口高等学校平均在校生数 4、范围:31省

    毕业设计&课程设计 基于STM32单片机基于RFID的电动车停车管理系统(软件源码+硬件资料+部署教程+功能说明+演示视频),高分项目,开箱即用

    毕业设计&课程设计 基于STM32单片机基于RFID的电动车停车管理系统(软件源码+硬件资料+部署教程+功能说明+演示视频),高分项目,开箱即用 用户 分为老师 及 学生 管理员 管理员 登录 用户管理 电动车管理 车卡rfid 电动车进出记录 挂失申请列表 解冻申请列表 补办列表申请 用户(只能管理自己的车) 注册(注册的时候选身份,选择学生或者老师) 登录 个人信息查看 电动车管理 进出校记录 挂失申请 解冻申请 补办申请

    无人机辅助旅行商问题的深度强化学习求解方法研究(可复现,有问题请联系博主)

    内容概要:本文探讨了一种新的基于深度强化学习的方法来解决旅行商问题与无人机组合优化(Traveling Salesman Problem with Drone, TSP-D),针对当前无人机辅助卡车配送中面临的协同调度难题进行了改进。研究者提出一种混合模型(HM),整合了注意力编码器和长短期记忆网络(LSTM)解码器的优势,从而有效地记录了多个车辆的动作序列并实现了协调路径规划。该方法在各种测试用例上展现了卓越性能,并能显著提高大型问题实例的计算效率,同时在实际应用场景如最后一步送货中有潜在的巨大价值。 适合人群:对物流系统优化和无人机应用有兴趣的专业人士,特别是从事最后一公里交付方案设计和技术实施的研究人员及工程师。 使用场景及目标:本研究所提出的深度学习框架主要适用于城市环境中复杂条件下的车辆和无人驾驶飞行系统的共同优化配置,目的是为了找到最优的货物递送方案,在最短的时间内完成所有的客户服务任务并返回起点。 其他说明:实验结果显示该算法在随机位置数据集和现实情况中的优越性超过了现有传统算法,表明它不仅能在简单理想情况下发挥良好效果,同样可以在更为复杂的条件下表现出稳定的性能。

    生活垃圾处理费征收管理系统产品介绍

    北京中启航向科技发展有限公司开发的城市生活垃圾处理费智慧征管系统,是一个全方位、一体化的解决方案,旨在协助城市管理部门高效、准确地收取生活垃圾处理费。该系统利用先进的人工智能和数据分析技术,实现垃圾分类、计量和收费的智能化管理,提升城市环境卫生质量,同时优化行政资源,提高征收效率。

    水测试试纸行业剖析:欧洲是全球最大的市场,占40%的份额.pdf

    水测试试纸行业剖析:欧洲是全球最大的市场,占40%的份额.pdf

    《电力电子技术(第5版)》王兆安-第2章-电力电子器件

    《电力电子技术(第5版)》王兆安_第2章_电力电子器件

    基于STM32的直流电机加减速正反转控制串口输出控制系统(P 1100009-基于STM32的直流电机加减速正反转控制串口输出控制系统(PCB 原理图 报告 源代码 proteus lcd1602)

    基于STM32的直流电机加减速正反转控制串口输出控制系统(P 1100009-基于STM32的直流电机加减速正反转控制串口输出控制系统(PCB 原理图 报告 源代码 proteus lcd1602) 功能描述:基于STM32平台 1、实现了电机控制正转、反转的功能 2、实现了电机控制加速、减速的功能 3、实现了串口输出控制信息的功能 4、串口可以模拟WIFI 蓝牙 RS232 等带有串口的功能。 资料包含: 1、源代码工程文件 2、仿真工程文件 3、lunwen报告1W字以上 4、原理图工程文件 5、PCB工程文件 ,核心关键词:STM32、直流电机、加减速、正反转控制、串口输出、控制信息、WIFI、蓝牙、RS232、源代码工程文件、仿真工程文件、原理图工程文件、PCB工程文件。,基于STM32的电机串口控制综合系统(含正反转、加减速及多种串口通信功能)

    ZYNQ7010采集AD7768

    ZYNQ7010采集AD7768

    apollo 泊车轨迹优化代码 hybridastar+iaps平滑优化+obca平滑优化 第一个图是matlab绘制 后面的图是程序用sdl库绘制 ,apollo;泊车轨迹优化;hybridas

    apollo 泊车轨迹优化代码 hybridastar+iaps平滑优化+obca平滑优化 第一个图是matlab绘制 后面的图是程序用sdl库绘制 ,apollo;泊车轨迹优化;hybridastar;iaps平滑优化;obca平滑优化;Matlab绘制;SDL库绘制,基于Apollo的泊车轨迹优化:HybridA*算法+平滑优化技术的实现与展示

Global site tag (gtag.js) - Google Analytics