簡單的Linux環境下多線程TCP服務程式架構

來源:互聯網
上載者:User

運行環境: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

#ifndef TCPUTIL_H#define TCPUTIL_H#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/types.h>ssize_t writen(int fd, void* buf, size_t n); ssize_t recvn(int fd, void* buf, size_t n); /*callback function called after received one message, 0-success, -1-error*/typedef int (*message_handler)(int socket, void * buf, uint32_t size);int start(uint32_t listenip, uint16_t listenport, message_handler handler);#endif

源碼:tcputil.c

/************************************************** * * $description: collection of functions  * $author: smstong * $date: Tue Apr 16 10:24:22 CST 2013 * * ************************************************/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/types.h>/************************************************** * func: receive n bytes from socket except an error * params: fd - socket handle *    buf - memory space to write *    n - size of buf  * return: -1 - error; *    >=0 - actually retceived bytes *************************************************/ssize_t recvn(int fd, void* buf, size_t n){char* ptr = (char*)buf; // position pointersize_t left = n;// bytes left to readwhile(left > 0) {size_t nread = read(fd, ptr, left);if(nread<0)  {if(errno==EINTR) { // an error occurednread = 0;} else {return -1;}} else if(nread==0) { //normally disconnect, FIN segment receivedbreak;} else {left -= nread;ptr += nread;}}return (n-left);}/******************************************************** * function: write n bytes to socket except error * params: fd - socket hanle  *    buf - src memory  *    n - bytes to write  * return: -1 - error *    >=0 - bytes actually written * ******************************************************/ssize_t writen(int fd, void* buf, size_t n){char* ptr = (char*)buf;size_t left = n;while(left > 0) {size_t nwrite = write(fd, ptr,left);    if(nwrite<0) {if(errno==EINTR) {nwrite = 0;} else {return -1;}} else if(nwrite==0) {break;} else {left -= nwrite;ptr += nwrite;}}return (n-left);}static void * thread_f(void *); //thread function typedef int (*message_handler)(int, void *, uint32_t); // callback function called after received one message/************************************************************* * * one thread per connection frameset * * ***********************************************************/// thread function's argsstruct thread_arg {int socket;message_handler msg_handler;};int start(uint32_t listenip, uint16_t listenport, message_handler handler){int    listenfd, connfd;struct sockaddr_in     servaddr;char    buff[4096];int     n;if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);exit(0);}memset(&servaddr, 0, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(listenip);servaddr.sin_port = htons(listenport);if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);return -1;}if( listen(listenfd, 10) == -1){printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);return -1;}printf("======waiting for client's request======\n");while(1){if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){printf("accept socket error: %s(errno: %d)",strerror(errno),errno);continue;}/* create a new thread to handle this connection */pthread_t tid = 0;int rc = 0;struct thread_arg *parg = malloc(sizeof(struct thread_arg));if(NULL==parg) {printf("error malloc: %s\n", strerror(errno));return -1;}parg->socket = connfd;parg->msg_handler = handler;if(0 != (rc=pthread_create(&tid, NULL, thread_f, parg))) {printf("%s: %s\n", __func__, strerror(rc));}printf(" create thread %u to handle connection %d \n", tid, connfd);}close(listenfd);return 0;}/*************************** * fun: receive one message * params: connfd - socket handle * return: 0 - success; *    -1 - error * * **************************/static int recv_one_message(int connfd, message_handler post_recv_one){uint32_t msg_len = 0; /* message length *//* recv length */if(4 != recvn(connfd, &msg_len, 4)) { // something wrongreturn -1;}msg_len = ntohl(msg_len);/* recv body */if(msg_len > 0x7FFFFFFF) {printf("message body to large\n");return -1;}char* buf = malloc(msg_len);/* allocate memory for message body*/if(NULL == buf) {printf("%s: malloc failed!\n", __func__);return -1;}if(msg_len != recvn(connfd, buf, msg_len)) {free(buf);return -1;}if(0!=post_recv_one(connfd, buf, msg_len)) { // callbackfree(buf);return -1;}free(buf);return 0;}/* thread to handle a connection */static void * thread_f(void * arg) {printf(" enter thread %u\n", pthread_self());struct thread_arg targ = *((struct thread_arg*)arg); int connfd = targ.socket;message_handler post_recv_one = targ.msg_handler;free(arg);int i = 0;while(1) {if(0 != recv_one_message(connfd, post_recv_one)) {break;}printf("message : %d\n", i++);}close(connfd);printf(" leave thread %u\n", pthread_self());}

源碼:測試例子 server.c,接收訊息寫入檔案data中。

#include "tcputil.h"#include <stdio.h>/* callback called after one message received. */int msg_handler(int fd, void* buf, uint32_t n){    char* msg = (char*)buf;    FILE* fp = fopen("data", "w");    if(NULL == fp) {        printf("%s\n", strerror(errno));        fclose(fp);        return -1;     }       if(n != fwrite(msg, 1, n, fp)) {        printf("write error:\n");        fclose(fp);        return -1;     }       fclose(fp);    return 0;}int main(int argc, char** argv){    start(0,6666, msg_handler);}             

源碼:用戶端程式 C#編寫

using System;using System.IO;using System.Net;using System.Net.Sockets;namespace ConsoleApplication1{    class Program    {        static void Main(string[] args)        {            SendTcpMsg(File.ReadAllBytes(@"F:\核心軟體備份\TomatoWin2k3.SP2.R2.iso"));        }        static void SendTcpMsg(Byte[] msgBody)         {            Socket sock = null;            try            {                sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                sock.Connect("172.16.35.135", 6666);                byte[] msgHead = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(msgBody.Length));                sock.Send(msgHead);                sock.Send(msgBody);            }            catch (Exception ex)            {                Console.Write(ex.Message);            }            finally            {                if(sock!=null)                    sock.Close();            }        }    }}

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.