libuv學(xué)習(xí)筆記5------TCP客戶端的實(shí)現(xiàn)

前一節(jié)我們講到了如何用libuv實(shí)現(xiàn)一個TCP服務(wù)器壁公,用libuv實(shí)現(xiàn)一個客戶端與用libuv實(shí)現(xiàn)一個TCP服務(wù)器極為類似感论。不同的地方在于不需要進(jìn)行uv_tcp_bind操作,將uv_listen改為uv_tcp_connect。

實(shí)現(xiàn)一個TCP客戶端的基本步驟為:

1.uv_tcp_init建立tcp句柄

2.uv_tcp_connect建立tcp連接

3.使用stream操作來和客戶端通信

所用到的API為:

1.uv_tcp_init

2.uv_ip4_addr

3.uv_tcp_connect

4.uv_write/uv_read_start

下面講只針對TCP客戶端的實(shí)現(xiàn)介紹一個新的API函數(shù)uv_tcp_connect紊册。

1.uv_tcp_connect函數(shù)講解

int uv_tcp_connect(uv_connect_t* req,uv_tcp_t* handle,const struct sockaddr* addr,uv_connect_cb cb);

參數(shù)1:連接請求對象

參數(shù)2:TCP客戶端對象

參數(shù)3:填充好的struct sockaddr_in結(jié)構(gòu)體

參數(shù)4:回調(diào)函數(shù)


struct sockaddr_in addr;
 
uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));
 
 
uv_ip4_addr("192.168.65.205",DEFAULT_PORT,&addr);
 
int r = uv_tcp_connect(connect,&mysocket,(const struct sockaddr*)&addr,on_connect);
 
if(r)
{
    fprintf(stderr, "connect error %s\n", uv_strerror(r));
    return 1;
}

2.uv_tcp_connect回調(diào)函數(shù)介紹

void (uv_connect_cb)(uv_connect_t req, int status);

在連接成功/失敗后調(diào)用此函數(shù)比肄。

status:返回的狀態(tài),小于零代表出錯囊陡。

req:連接請求對象芳绩,req->handle指向TCP客戶端對象(mysocket),可直接將req->handle用于uv_write,uv_read_start等流操作中。

3.傳輸指定文件給服務(wù)器的代碼實(shí)現(xiàn)

#include <stdio.h>
#include <uv.h>
#include <stdlib.h>
 
uv_loop_t *loop;
#define DEFAULT_PORT 7000
 
uv_tcp_t mysocket;
 
char *path = NULL;
uv_buf_t iov;
char buffer[128];
 
uv_fs_t read_req;
uv_fs_t open_req;
void on_read(uv_fs_t *req);
void on_write(uv_write_t* req, int status)
{
    if (status < 0) 
    {
        fprintf(stderr, "Write error: %s\n", uv_strerror(status));
        uv_fs_t close_req;
        uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
        uv_close((uv_handle_t *)&mysocket,NULL);
        exit(-1);
    }
    else 
    {
        uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read);
    }
}
 
void on_read(uv_fs_t *req)
{
    if (req->result < 0) 
    {
        fprintf(stderr, "Read error: %s\n", uv_strerror(req->result));
    }
    else if (req->result == 0) 
    {
        uv_fs_t close_req;
        // synchronous
        uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
        uv_close((uv_handle_t *)&mysocket,NULL);
    }
    else
    {
        iov.len = req->result;
        uv_write((uv_write_t *)req,(uv_stream_t *)&mysocket,&iov,1,on_write);
    }
}
 
void on_open(uv_fs_t *req)
{
    if (req->result >= 0) 
    {
        iov = uv_buf_init(buffer, sizeof(buffer));
        uv_fs_read(uv_default_loop(), &read_req, req->result,&iov, 1, -1, on_read);
    }
    else 
    {
        fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result));
        uv_close((uv_handle_t *)&mysocket,NULL);
        exit(-1);
    }
}
 
void on_connect(uv_connect_t* req, int status)
{
    if(status < 0)
    {
        fprintf(stderr,"Connection error %s\n",uv_strerror(status));
 
        return;
    }
 
    fprintf(stdout,"Connect ok\n");
 
    uv_fs_open(loop,&open_req,path,O_RDONLY,-1,on_open);
 
 
}
 
int main(int argc,char **argv)
{
    if(argc < 2)
    {
        fprintf(stderr,"Invaild argument!\n");
 
        exit(1);
    }
    loop = uv_default_loop();
 
    path = argv[1];
 
    uv_tcp_init(loop,&mysocket);
 
    struct sockaddr_in addr;
 
    uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t));
 
 
    uv_ip4_addr("127.0.0.1",DEFAULT_PORT,&addr);
 
    int r = uv_tcp_connect(connect,&mysocket,(const struct sockaddr *)&addr,on_connect);
 
    if(r)
    {
        fprintf(stderr, "connect error %s\n", uv_strerror(r));
        return 1;
    }
 
    return uv_run(loop,UV_RUN_DEFAULT);
}

這段代碼利用了libuv文件操作撞反、流操作妥色、TCP實(shí)現(xiàn)等知識。代碼的基本邏輯如下:

1.創(chuàng)建TCP客戶端對象痢畜,和TCP連接請求對象垛膝,并與服務(wù)器建立連接。

2.連接成功后打開要傳輸?shù)奈募?/p>

3.打開文件成功后讀取一定的文件內(nèi)容丁稀,并在讀取成功后將文件內(nèi)容利用stream操作發(fā)送給服務(wù)器吼拥。

4.發(fā)送成功后再次讀取文件內(nèi)容,并在讀取成功后發(fā)送給服務(wù)器线衫。

5.循環(huán)進(jìn)行第4步操作凿可,直到讀到文件末尾。

6.關(guān)閉文件授账、關(guān)閉流枯跑。

測試:

1.打開服務(wù)器,監(jiān)聽7000端口白热,并將輸出重定向到文件:

nc -l 7000 > test_recv.jpg

2.編譯并運(yùn)行程序:

gcc main.c -luv

./a.out test.jpg

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敛助,一起剝皮案震驚了整個濱河市屋确,隨后出現(xiàn)的幾起案子续扔,更是在濱河造成了極大的恐慌焕数,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件识脆,死亡現(xiàn)場離奇詭異,居然都是意外死亡灼捂,警方通過查閱死者的電腦和手機(jī)雕拼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門粘招,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人洒扎,你說我怎么就攤上這事×状祝” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵邓线,是天一觀的道長煌恢。 經(jīng)常有香客問我,道長你雌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任婿崭,我火速辦了婚禮肴颊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捕虽。我一直安慰自己,他們只是感情好奥务,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挡篓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪官研。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天戏羽,我揣著相機(jī)與錄音楼吃,去河邊找鬼。 笑死酷宵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浇垦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼男韧,長吁一口氣:“原來是場噩夢啊……” “哼默垄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起寡壮,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤讹弯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后组民,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡莫其,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乱陡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡胳徽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出养盗,到底是詐尸還是另有隱情,我是刑警寧澤往核,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布嚷节,位于F島的核電站,受9級特大地震影響丹喻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一柄慰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坐搔,春花似錦、人聲如沸概行。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至勤家,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伐脖,已是汗流浹背热幔。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工绎巨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蠕啄,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓介汹,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嘹承。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容