基於libuv的TCP設計(二)

1、本人設想的TCP服務器有以下特性:git

1.啓動服務,一直監聽端口。github

2.有新鏈接(客戶端)就通知用戶。並把鏈接接收到的數據回調給用戶。安全

3.客戶端鏈接上後用戶可在任意時間發送數據給它。服務器

4.客戶端斷開時關閉或用戶可手動關掉。網絡

以上操做均可以不一樣線程在完成。多線程

 

2、使用libuv遇到的問題async

因爲對libuv不熟悉+其文檔,調用其函數時吃了很多苦頭。tcp

1.libuv的特性函數

libuv是基於event驅動的,當調用uv_run後就會一直啓動event循環,阻塞其線程(event loop thread)直到沒有事件了uv_run返回。除了uv_async_send函數外,其餘函數都是非線程安全的。即其餘函數只能在event loop thread裏調用,在其餘線程調用libuv不保證其正確性。oop

libuv到處回調,多數有回調的函數都是直到回調函數被觸發時纔算調用完成,而非該函數返回就算調用完成。

 

2.遇到的問題

libuv的這點特性對於我想經過多線程調用tcp sever中的不一樣操做是一大麻煩事。

2.1.我在另外一線程裏調用了uv_write發送數據,結果總提示Assertion failed: handle->write_queue_size >= req->queued_bytes, file src/win/tcp.c

最後在google group( https://groups.google.com/forum/#!msg/libuv/iHzv3x-VOr4/KzhJymI6lRkJ )中找到方法:

內部開闢一線程用於發送數據。用戶調用發送函數時把數據壓入隊列,發送線程從隊列中循環取數據,而後調用uv_async_send觸發真正的發送數據函數。數據參數能夠經過uv_handle_t.data傳輸。可用uv_sem_wait/ uv_sem_post來控制數據發送前後。

 

2.2.對於想要關閉一個客戶端,可以使用uv_close關閉其所關聯的uv_handle_t,而後把客戶端參數從客戶端隊列中刪除。

可是libuv這種到處回調的函數,調用uv_close返回後並不意味着真正close成功了,此時若把客戶端刪除,則會調用客戶端的析構函數,客戶端的全部變量地址都是未知的了。由於uv_close後對客戶端繼續操做,因此訪問這些未知變量地址會出錯。真正close成功是在uv_close_cb被觸發時。

因此想要delete掉一個客戶端,得調用uv_close,而後在uv_close_cb等待並判斷是哪一個客戶端,再把客戶端刪除。

 

2.3 uv_close, uv_tcp_connect也同樣,不能uv_tcp_connect就想發送數據,得等其回調函數觸發後才能進行發送數據操做。uv_write也同樣。

 

總結:libuv好很差,會用纔好,不會用坑一大堆。

 

3、傳輸規則定義

網絡傳輸中不能只接受裸流,必須對數據進行卦包與拆包,一來可防止數據被篡改與丟失,二來方便數據解析。

CSDN上對網絡數據如何定義有討論過:http://bbs.csdn.net/topics/380167545

 

本人定義的包結構以下:

// 一個數據包的內存結構

//增長包頭與包尾數據,用於檢測包的完整性。檢驗值用於檢測包的徹底性。

//|-----head----|--------------------------pack header-------------------|--------------------pack data------------|-----tail----|

//|--包頭1字節--|--[version][head][tail][check][type][datalen][reserve]--|--datalen長度的內存數據(根據type去解析)--|--包尾1字節--|

#pragma pack(1)//將當前字節對齊值設爲1

 

#define NET_PACKAGE_VERSION 0x01

typedef struct _NetPacket{//傳輸自定義數據包頭結構

    int32_t version; //封包的版本號,不一樣版本包的定義可能不一樣 :0-3

    unsigned char header; //包頭-可自定義,例如0x02 :4

    unsigned char tail; //包尾-可自定義,例如0x03 :5

    unsigned char check[16];//pack data校驗值-16字節的md5二進制數據 :6-21

    int32_t type; //包數據的類型 :22-25

    int32_t datalen; //包數據的內容長度-不包括此包結構和包頭尾 :26-29

    int32_t reserve; //包數據保留字段-暫時不使用 :30-33

}NetPacket;

#define NET_PACKAGE_HEADLEN sizeof(NetPacket)//包頭長度,爲固定大小34字節

 

同時進行了封包與折包工做,詳見packet.h

 

——————————————————————————————————————————————————————————————————————

代碼已上傳到git: https://github.com/wqvbjhc/libuv_tcp

客戶端的測試例子有缺陷,但服務器徹底正常。

服務器能夠接收上百路鏈接。

相關文章
相關標籤/搜索