基於libuv的TCP設計(二)


一、本人設想的TCP服務器有如下特性:

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

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

3.客戶端連接上后用戶可在任意時間發送數據給它。

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

以上操作都可以不同線程在完成。

 

二、使用libuv遇到的問題

由於對libuv不熟悉+其文檔,調用其函數時吃了不少苦頭。

1.libuv的特性

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

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好不好,會用才好,不會用坑一大堆。

 

三、傳輸規則定義

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

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

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

服務器可以接收上百路連接。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM