简单的Linux环境下多线程TCP服务程序框架

运行环境:Linux2.6以上

文件说明:tcputil.c --------------- TCP多线程服务框架实现

                    tcputil.h --------------- 公开函数声明

使用说明:

                  发送消息必须采用固定的(消息大小,消息体)这种流边界方式,其中消息大小是uint32_t类型,并且是网络字节序。

                  直接调用start(监听IP, 监听端口,自定义消息处理函数)即可;主要是提供自定义的消息处理函数,原型为:

                    int msg_handler(int socket, void* buf, uint32_t n),其中: socket-接收消息的socket,buf-消息体内存,n-消息体长度。

几个关键点:

(1)发送和接收n个字节的方法,在readn()和writen()函数实现;

(2)向派生线程传递参数时,注意并发导致的同步问题,参见start()函数中的传参实现;

(3)遵循“malloc和free要成对存在于同一个函数中”,但是(2)违反了这个原则,是否有更好的解决方案?

(4)采用了回调函数机制(类似C#中的事件)来让库使用着自定义消息处理函数;(这也是为了遵循(3)采取的策略);

(5)TCP流边界,采取了(消息大小,消息体)的方式,其中消息大小为4字节无符号整数。

存在问题:

(1)性能问题,目前是直接分配与消息体大小同样的内存来接收消息体;

(2)大消息问题,目前消息不能大于int32_t的最大值,对于大数据量传送,请在消息体内实现自定义的用户消息格式来把大数据分块传送;



源码:tcputil.h

[cpp] view plaincopy
  1. #ifndef TCPUTIL_H  
  2. #define TCPUTIL_H  
  3.   
  4. #include <stdio.h>  
  5. #include <stdlib.h>  
  6. #include <string.h>  
  7. #include <errno.h>  
  8. #include <sys/types.h>  
  9. #include <sys/socket.h>  
  10. #include <netinet/in.h>  
  11. #include <sys/types.h>  
  12.   
  13. ssize_t writen(int fd, void* buf, size_t n);   
  14. ssize_t recvn(int fd, void* buf, size_t n);   
  15.   
  16. /*callback function called after received one message, 0-success, -1-error*/  
  17. typedef int (*message_handler)(int socket, void * buf, uint32_t size);  
  18.   
  19. int start(uint32_t listenip, uint16_t listenport, message_handler handler);  
  20. #endif  

源码:tcputil.c

[cpp] view plaincopy
  1. /************************************************** 
  2.  * 
  3.  * $description: collection of functions  
  4.  * $author: smstong 
  5.  * $date: Tue Apr 16 10:24:22 CST 2013 
  6.  * 
  7.  * ************************************************/  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <errno.h>  
  12. #include <sys/types.h>  
  13. #include <sys/socket.h>  
  14. #include <netinet/in.h>  
  15. #include <sys/types.h>  
  16. /************************************************** 
  17.  * func: receive n bytes from socket except an error 
  18.  * params: fd - socket handle 
  19.  *         buf - memory space to write 
  20.  *         n - size of buf  
  21.  * return: -1 - error; 
  22.  *         >=0 - actually retceived bytes 
  23.  *************************************************/  
  24. ssize_t recvn(int fd, void* buf, size_t n)  
  25. {  
  26.     char* ptr = (char*)buf; // position pointer  
  27.     size_t left = n;        // bytes left to read  
  28.     while(left > 0) {  
  29.         size_t nread = read(fd, ptr, left);  
  30.         if(nread<0)  {  
  31.             if(errno==EINTR) { // an error occured  
  32.                 nread = 0;  
  33.             } else {  
  34.                 return -1;  
  35.             }  
  36.         } else if(nread==0) { //normally disconnect, FIN segment received  
  37.             break;  
  38.         } else {  
  39.             left -= nread;  
  40.             ptr += nread;  
  41.         }  
  42.     }  
  43.     return (n-left);  
  44. }  
  45.   
  46. /******************************************************** 
  47.  * function: write n bytes to socket except error 
  48.  * params: fd - socket hanle  
  49.  *         buf - src memory  
  50.  *         n - bytes to write  
  51.  * return: -1 - error 
  52.  *         >=0 - bytes actually written 
  53.  * ******************************************************/  
  54. ssize_t writen(int fd, void* buf, size_t n)  
  55. {  
  56.     char* ptr = (char*)buf;  
  57.     size_t left = n;  
  58.     while(left > 0) {  
  59.         size_t nwrite = write(fd, ptr,left);  
  60.         if(nwrite<0) {  
  61.             if(errno==EINTR) {  
  62.                 nwrite = 0;  
  63.             } else {  
  64.                 return -1;  
  65.             }  
  66.         } else if(nwrite==0) {  
  67.             break;  
  68.         } else {  
  69.             left -= nwrite;  
  70.             ptr += nwrite;  
  71.         }     
  72.     }  
  73.     return (n-left);  
  74. }  
  75.   
  76. static void * thread_f(void *); //thread function   
  77. typedef int (*message_handler)(intvoid *, uint32_t); // callback function called after received one message  
  78.   
  79. /************************************************************* 
  80.  * 
  81.  * one thread per connection frameset 
  82.  * 
  83.  * ***********************************************************/  
  84.   
  85. // thread function's args  
  86. struct thread_arg {  
  87.     int socket;  
  88.     message_handler msg_handler;  
  89. };  
  90.   
  91. int start(uint32_t listenip, uint16_t listenport, message_handler handler)  
  92. {     
  93.     int    listenfd, connfd;  
  94.     struct sockaddr_in     servaddr;  
  95.     char    buff[4096];  
  96.     int     n;  
  97.   
  98.     if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
  99.         printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);  
  100.         exit(0);  
  101.     }  
  102.   
  103.     memset(&servaddr, 0, sizeof(servaddr));  
  104.     servaddr.sin_family = AF_INET;  
  105.     servaddr.sin_addr.s_addr = htonl(listenip);  
  106.     servaddr.sin_port = htons(listenport);  
  107.   
  108.     if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  
  109.         printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
  110.         return -1;  
  111.     }  
  112.   
  113.     if( listen(listenfd, 10) == -1){  
  114.         printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  
  115.         return -1;  
  116.     }  
  117.   
  118.     printf("======waiting for client's request======\n");  
  119.     while(1){  
  120.         if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){  
  121.             printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
  122.             continue;  
  123.         }  
  124.         /* create a new thread to handle this connection */  
  125.         pthread_t tid = 0;  
  126.         int rc = 0;  
  127.         struct thread_arg *parg = malloc(sizeof(struct thread_arg));  
  128.         if(NULL==parg) {  
  129.             printf("error malloc: %s\n", strerror(errno));  
  130.             return -1;  
  131.         }  
  132.         parg->socket = connfd;  
  133.         parg->msg_handler = handler;  
  134.         if(0 != (rc=pthread_create(&tid, NULL, thread_f, parg))) {  
  135.             printf("%s: %s\n", __func__, strerror(rc));  
  136.         }  
  137.         printf(" create thread %u to handle connection %d \n", tid, connfd);      
  138.     }  
  139.     close(listenfd);  
  140.     return 0;  
  141. }  
  142. /*************************** 
  143.  * fun: receive one message 
  144.  * params: connfd - socket handle 
  145.  * return: 0 - success; 
  146.  *         -1 - error 
  147.  * 
  148.  * **************************/  
  149. static int recv_one_message(int connfd, message_handler post_recv_one)  
  150. {  
  151.     uint32_t msg_len = 0;        /* message length */  
  152.   
  153.     /* recv length */  
  154.     if(4 != recvn(connfd, &msg_len, 4)) { // something wrong  
  155.         return -1;  
  156.     }  
  157.     msg_len = ntohl(msg_len);  
  158.   
  159.     /* recv body */  
  160.     if(msg_len > 0x7FFFFFFF) {  
  161.         printf("message body to large\n");  
  162.         return -1;  
  163.     }  
  164.     char* buf = malloc(msg_len);/* allocate memory for message body*/  
  165.     if(NULL == buf) {  
  166.         printf("%s: malloc failed!\n", __func__);  
  167.         return -1;  
  168.     }  
  169.     if(msg_len != recvn(connfd, buf, msg_len)) {  
  170.         free(buf);  
  171.         return -1;  
  172.     }  
  173.     if(0!=post_recv_one(connfd, buf, msg_len)) { // callback  
  174.         free(buf);  
  175.         return -1;  
  176.     }  
  177.     free(buf);  
  178.     return 0;  
  179. }  
  180. /* thread to handle a connection */  
  181. static void * thread_f(void * arg)   
  182. {  
  183.     printf(" enter thread %u\n", pthread_self());  
  184.     struct thread_arg targ = *((struct thread_arg*)arg);   
  185.     int connfd = targ.socket;  
  186.     message_handler post_recv_one = targ.msg_handler;  
  187.     free(arg);  
  188.   
  189.     int i = 0;  
  190.     while(1) {  
  191.         if(0 != recv_one_message(connfd, post_recv_one)) {  
  192.             break;  
  193.         }  
  194.         printf("message : %d\n", i++);  
  195.     }  
  196.     close(connfd);  
  197.     printf(" leave thread %u\n", pthread_self());  
  198. }  

源码:测试例子 server.c,接收消息写入文件data中。

[cpp] view plaincopy
  1. #include "tcputil.h"  
  2. #include <stdio.h>  
  3.   
  4. /* callback called after one message received. */  
  5. int msg_handler(int fd, void* buf, uint32_t n)  
  6. {  
  7.     char* msg = (char*)buf;  
  8.     FILE* fp = fopen("data""w");  
  9.     if(NULL == fp) {  
  10.         printf("%s\n", strerror(errno));  
  11.         fclose(fp);  
  12.         return -1;   
  13.     }     
  14.     if(n != fwrite(msg, 1, n, fp)) {  
  15.         printf("write error:\n");  
  16.         fclose(fp);  
  17.         return -1;   
  18.     }     
  19.     fclose(fp);  
  20.     return 0;  
  21. }  
  22.   
  23. int main(int argc, char** argv)  
  24. {  
  25.     start(0,6666, msg_handler);  
  26. }  
  27.                
源码:客户端程序 C#编写

[csharp] view plaincopy
  1. using System;  
  2. using System.IO;  
  3. using System.Net;  
  4. using System.Net.Sockets;  
  5.   
  6. namespace ConsoleApplication1  
  7. {  
  8.     class Program  
  9.     {  
  10.         static void Main(string[] args)  
  11.         {  
  12.             SendTcpMsg(File.ReadAllBytes(@"F:\核心软件备份\TomatoWin2k3.SP2.R2.iso"));  
  13.         }  
  14.         static void SendTcpMsg(Byte[] msgBody)   
  15.         {  
  16.             Socket sock = null;  
  17.             try  
  18.             {  
  19.                 sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
  20.                 sock.Connect("172.16.35.135", 6666);  
  21.                 byte[] msgHead = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(msgBody.Length));  
  22.                 sock.Send(msgHead);  
  23.                 sock.Send(msgBody);  
  24.             }  
  25.             catch (Exception ex)  
  26.             {  
  27.                 Console.Write(ex.Message);  
  28.             }  
  29.             finally  
  30.             {  
  31.                 if(sock!=null)  
  32.                     sock.Close();  
  33.             }  
  34.         }  
  35.     }  
  36. }  

你可能感兴趣的:(简单的Linux环境下多线程TCP服务程序框架)