SOCKET通信中多线程编程的性能优化问题

原创 2017年04月12日 16:19:08

背景:本文作为本人的一点心得,同时也供遇到类似问题的读者以供参考。
所需知识:简单的SOCKET编程、多线程编程知识

SOCKET编程作为当前网络编程的主要方式,在各种项目、科研、实验中多有使用,其通过套接字的方式完成端到端通信,大大方便了编程人员的网络搭建。

网络建立的模式多种多样,client-sever(用户-服务器)模式、peer-2-peer(对等)模式等等。而无论哪一种模式,在点到点链路建立后,不可避免的可能会出现多应用同时通信的情景。所以在SOCKET通信中,自然地,在多数情况下都会采用多线程通信。例如,在C-V(用户-服务器模式缩写)模式中,服务器大多不可能单一地为一个用户提供服务,所以在用户A连接建立时,服务器会自动的分配一个线程给该用户,而预留自身的监听状态,以保证后续的用户可以正常建立连接。

然而,只要实现过任何一个最基本的C-V通信的读者都会知道,SOCKET编程中有许多函数都有决定函数自身的执行状态的参数,比如接收数据函数:

ssize_t recvfrom(int sockfd,void *buf,size_t len,unsigned int flags, struct sockaddr *from,socket_t *fromlen);

中的flag字段可以是MSG_NOWAIT、0等情况。再比如

int sendto ( socket s , const void * msg, int len, unsigned int flags, conststruct sockaddr * to , int tolen ) ;

中的flag字段。

flag字段的含义定义如下:
MSG_DONTWAIT:操作不会被阻塞。
MSG_ERRQUEUE: 指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过msg_iovec作为一般的数据来传递。导致错误的数据报原目标地址作为msg_name被提供。
MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。
MSG_TRUNC:返回封包的实际长度,即使它比所提供的缓冲区更长, 只对packet套接字有效。
MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被接收的数据类型不同,仍会返回少于请求量的数据。
MSG_EOR:指示记录的结束,返回的数据完成一个记录。
MSG_TRUNC:指明数据报尾部数据已被丢弃,因为它比所提供的缓冲区需要更多的空间。
MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。
MSG_OOB:指示接收到out-of-band数据(即需要优先处理的数据)。
MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据。

而如何选择合适的flag直接影响了程序的性能。

比如在代码段中:

int UDPRcv(struct tunnel_T *tunnel)
{
    int BufLength;  
    unsigned char msg[PLMsgMaxLength];
    struct sockaddr_in clientaddr;
    socklen_t len = (socklen_t) sizeof(clientaddr);
    struct tunnel_T tunnelcsma;
    int i =0,num;

    while(1)
    {           
#ifndef DISABLE_ALL     



----------


if((BufLength = recvfrom(tunnel->socket_descriptor, msg, PLMsgMaxLength, 0, (struct sockaddr *)&clientaddr, &len)) > 0)


----------


#else
        if((BufLength = uart_recv_frame((u8 *)msg, PLMsgMaxLength)) >0)
#endif
        {           
            full_ip_r += BufLength + 1;
#ifdef DEBUG        
                printf("udp received!\n");  
                for(;i<BufLength;i++)
                printf(" %x",msg[i]);
                i=0;
                printf("\n");           
                printf("length is %d\n",BufLength);         
#endif                  
                switch((int)msg[0])
                {           
                    case NOMAC:
                        ...
                    case FDMAC:
                        ...
                    case CSMA:
                        ...
                }           
        }   
        else 
            fprintf(stderr, "udpRcv :%s\a\n", strerror(errno));
    }
    return 0;
}

这段代码一直在不停的接收数据,如果flag参数为0,则代码运行到这里时会阻塞,直到有数据包到达时才会继续运行;而如果参数flag是MSG_NOWAIT的话,代码运行到recvfrom处发现没有数据包执行,则会打印else的语句,并继续执行。如果该UDPRcv函数是一个接收线程的话,那么flag为MSG_NOWAIT时,该线程则会不停地无效的占用资源,而如果flag为0时,该线程就会阻塞,知道有数据包到达为止。

与此类似的,在多线程编程中,不同线程间进行数据交换时所用的消息队列也有类似的机制。例如函数:

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

中都有msgflg参数,该参数同样有阻塞、非阻塞等不同的情况,其功能与recvfrom和sendto类似,在此不赘述了。

下图展示了本人仅修改了多线程SOCKET通信代码中*一处***flag参数后,端到端ping业务时延的结果。
这里写图片描述


从图中可以看到,性能表现是十分明显的。

总结一下:参数选择是阻塞,还是不阻塞,需要根据代码实现的功能需求进行调整,合理设计才行。


Socket层实现系列 — I/O事件及其处理函数

主要内容:Socket I/O事件的定义、I/O处理函数的实现。 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd   I/O事件定义   sock中定义了...
  • zhangskd
  • zhangskd
  • 2015年05月23日 22:25
  • 6208

sendmsg recvmsg函数

sendmsg(2)与recvmsg(2)函数 这些函数为程序提供了一些其他的套接口I/O接口所不具备的高级特性。下面的内容我们将会先来看一下sendmsg来介绍这些主题。然后将会完整的介绍recv...
  • shisiye15
  • shisiye15
  • 2012年07月19日 12:05
  • 18399

基于Linux的Socket网络编程的性能优化

基于Linux的Socket网络编程的性能优化2009-10-23      嵌入式在线      收藏 | 打印1 引言    随着Intenet的日益发展和普及,网络在嵌入式系统中应用非常广泛,越...
  • tinnycloud
  • tinnycloud
  • 2009年12月23日 02:57
  • 2618

高并发的socket的高性能设计

高性能的高并发socket设计 本文是从网络上搜集的几篇较好的文章: 主要转载自: http://blog.csdn.net/qifengzou/article/details/23912267 ht...
  • tlaff
  • tlaff
  • 2015年03月26日 19:46
  • 10154

SOCKET通信中多线程编程的性能优化问题

SOCKET网络编程,多线程场景下的性能表现与参数选择
  • success_by_choice
  • success_by_choice
  • 2017年04月12日 16:19
  • 419

Socket编程(四)---使用线程池优化服务端

在服务端使用线程池,来优化服务端处理的能力。
  • oDeviloo
  • oDeviloo
  • 2016年05月23日 09:19
  • 1875

Android中的性能优化问题

2015年,Google发布了关于Android性能优化典范的专题, 一共16个短视频,每个3-5分钟,帮助开发者创建更快更优秀的Android App。课程专题不仅仅介绍了Android系统中有...
  • linergou
  • linergou
  • 2016年09月29日 17:07
  • 497

性能优化问题?

代码层面:避免使用css表达式,避免使用高级选择器,通配选择器。 缓存利用:缓存Ajax,使用CDN,使用外部js和css文件以便缓存,添加Expires头,服务端配置Etag,减少DNS查找等 ...
  • qq_36648555
  • qq_36648555
  • 2017年10月18日 09:49
  • 28

socket 多线程写法

参考headfirst java 中 服务器代码如下: import java.io.BufferedReader; import java.io.IOException; import java...
  • liang_henry
  • liang_henry
  • 2016年07月05日 17:16
  • 835

Socket通讯解决并发采用线程池

  • 2016年02月29日 20:56
  • 157KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:SOCKET通信中多线程编程的性能优化问题
举报原因:
原因补充:

(最多只允许输入30个字)