0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

TCP實(shí)現(xiàn)服務(wù)器與客戶端的通信流程

C語(yǔ)言編程基礎(chǔ) ? 來(lái)源:未知 ? 作者:胡薇 ? 2018-05-04 17:52 ? 次閱讀

主要函數(shù):

TCP實(shí)現(xiàn)服務(wù)器與客戶端的通信流程

//服務(wù)器端---服務(wù)器是一個(gè)被動(dòng)的角色

1.socket //買一個(gè)手機(jī)

2.bind //SIM卡 綁定一個(gè)手機(jī)號(hào)(ip+port)

3.listen //待機(jī)(等待電話打入)

4.accept //接聽(tīng)電話

5.read/write //通話

6.close //掛機(jī)

//客戶端---客戶端是一個(gè)主動(dòng)發(fā)起請(qǐng)求的一端

1.socket //買一個(gè)手機(jī)

2.bind(可選的) //SIM卡(綁定號(hào)碼)

3.connect //撥打電話

4.read/write //通話

5.close //掛機(jī)

//1.socket ---- 插口

int socket(int domain, int type, int protocol);

功能: 創(chuàng)建通信的一端 (socket)

參數(shù):

@domain //"域" --范圍

AF_INET //IPV4 協(xié)議的通信

@type SOCK_STREAM //TCP (流式套接字)

@protocol 0 //LINUX下 流式套接字 ==>TCP

//協(xié)議

返回值:

成功 對(duì)應(yīng)的socket文件描述符

失敗 返回-1

注意:

文件描述符:

實(shí)際上就是 創(chuàng)建好的 socket的一個(gè)標(biāo)識(shí)符

//2.bind

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

功能:

給指定的 socket 綁定地址信息

參數(shù):

@sockfd //表示操作的socket

@addr //填充的地址信息(ip + port)

@addrlen //地址信息結(jié)構(gòu)體的大小

返回值:

成功 0

失敗 -1

//通用的地址結(jié)構(gòu)

struct sockaddr {

sa_family_t sa_family; //AF_INET //IPV4的協(xié)議

char sa_data[14];//(ip+port)

}

//網(wǎng)絡(luò)通信的地址結(jié)構(gòu)(internet)

struct sockaddr_in {

sa_family_t sin_family; /* address family: AF_INET */

in_port_t sin_port; /* port in network byte order */

struct in_addr sin_addr; /* internet address */

};

/* Internet address. */

struct in_addr {

uint32_t s_addr; /* address in network byte order */

};

//1.定義一個(gè) 地址結(jié)構(gòu)體變量

struct sockaddr_in addr;

bzero(&addr,sizeof(addr)); //清0的函數(shù)

//2.之后進(jìn)行信息填充

addr.sin_family = AF_INET;

addr.sin_port = htons(8888);

addr.sin_addr.s_addr = inet_addr("127.0.0.1");

//127.0.0.1 是回環(huán)測(cè)試的地址

//3.進(jìn)行綁定

if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)

{

perror("bind fail");

return 0;

}

//3.listen --- 設(shè)置監(jiān)聽(tīng) ---作用:讓操作系統(tǒng)監(jiān)控是否有客戶端發(fā)起連接

int listen(int sockfd, int backlog);

功能:

設(shè)置監(jiān)聽(tīng)

參數(shù):

@sockfd //監(jiān)聽(tīng)套接字

@backlog //監(jiān)聽(tīng)隊(duì)列的大小

返回值

成功 0

失敗 -1

listenfd

--------------監(jiān)聽(tīng)隊(duì)列------------------

fd1 fd2 fd3 fd4

|

-|--------------------------------------

|

\---->建立好連接的套接字

accept函數(shù)獲取已連接的套接字 返回對(duì)應(yīng)

的標(biāo)識(shí)符

|--->后面的讀寫操作 都是通過(guò)這個(gè)標(biāo)識(shí)符

進(jìn)行的

-----------------------------------------------

accept(); //accept 從監(jiān)聽(tīng)隊(duì)列中獲得已連接的的socket,返回一個(gè)標(biāo)示符來(lái)表示已連接的socket

//后續(xù)通過(guò)已連接的socket進(jìn)行通信

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能: 獲取連接

參數(shù):

@sockfd //監(jiān)聽(tīng)套接字的fd(標(biāo)識(shí)符)

@addr //來(lái)電顯示(保存對(duì)端的地址信息)(ip+port)

@addrlen //表示 addr 參數(shù)對(duì)應(yīng)類型的大小,值結(jié)果參數(shù) --- 就是在用的時(shí)候,必須先賦一個(gè)初值,最后函數(shù)調(diào)用完成

//通過(guò)該參數(shù),返回一個(gè)結(jié)果值

返回值:

成功 已連接的socket的標(biāo)識(shí)符

失敗 -1

//connect ---發(fā)起連接

int connect(

int sockfd, //表示 進(jìn)行通信的 socket的標(biāo)識(shí)符

const struct sockaddr *addr, //對(duì)端的地址信息(ip+port)

socklen_t addrlen); //表示的是 addr 參數(shù)類型的長(zhǎng)度

參數(shù):

@sockfd //通過(guò)socket函數(shù)獲得的fd

@addr //服務(wù)器端的地址

@addrlen //參數(shù)addr類型的大小

//數(shù)據(jù)流向 fd --> buf (count 表示一次讀取多少個(gè)字節(jié))

ssize_t read(int fd, void *buf, size_t count);

//數(shù)據(jù)流向 buf--> fd (count 表示一次寫多少個(gè)字節(jié))

ssize_t write(int fd, const void *buf, size_t count);

參數(shù):

@fd 就是要操作的 socket對(duì)應(yīng)的 標(biāo)示符

@buf 保存數(shù)據(jù)的一塊內(nèi)存首地址

@count 一次操作的字節(jié)數(shù)

confd

char buf[] = "hello QCXY\n";

write(confd,buf,strlen(buf)); //寫 數(shù)據(jù)到socket中

//讀數(shù)據(jù)出來(lái)

char rbuf[1024] = {0}; //表示申請(qǐng)了一塊1024個(gè)字節(jié)大小

//的內(nèi)存空間

read(confd,rbuf,sizeof(rbuf)); //讀取數(shù)據(jù)

//練習(xí):

實(shí)現(xiàn) 客戶端 向服務(wù)器發(fā)送數(shù)據(jù)

服務(wù)器回發(fā)數(shù)據(jù)的功能

client ----- server

scanf(); ---(1)----> read 之后printf

read <--(2)---- ? ? ? ? write

printf

循環(huán)做,結(jié)束條件

當(dāng)客戶端輸入 "quit"字符串時(shí) 客戶端結(jié)束

怎么判斷客戶端讀到的是"quit"

c語(yǔ)言

"quit" == buf; (X) //不能這么寫

//字符串的比較函數(shù)

strcmp("quit",buf);

strncmp("quit",buf,4);

客戶端的程序:

#include

#include /* See NOTES */

#include

#include

#include

#include

#include

//./client 127.0.0.1 8888

int main(int argc, const char *argv[])

{

int fd;

int ret = 0;

char buf[1024] = {0};

char rbuf[1024] = {0};

//處理命令行參數(shù)

//1.socket(手機(jī))

//2.bind(電話卡)

//3.connect (撥打電話)

//處理命令行參數(shù)

if(argc != 3)

{

printf("Usage: %s\n",argv[0]);

return -1;

}

//1.socket(手機(jī))

fd = socket(AF_INET,SOCK_STREAM,0);

if(fd < 0) //出錯(cuò)處理

{

perror("socket fail");

return -1;

}

printf("fd = %d\n",fd);

//2.bind(電話卡)---綁定的是客戶端自己的地址信息

//客戶端地址信息

//1.定義一個(gè) 地址結(jié)構(gòu)體變量

struct sockaddr_in cli_addr;

bzero(&cli_addr,sizeof(cli_addr)); //清0的函數(shù)

//2.之后進(jìn)行信息填充

cli_addr.sin_family = AF_INET;

cli_addr.sin_port = htons(7777);

cli_addr.sin_addr.s_addr = inet_addr(argv[1]);

if(bind(fd,(struct sockaddr*)&cli_addr,sizeof(cli_addr)) < 0)

{

perror("bind fail");

return -1;

}

//服務(wù)器端的地址信息

//1.定義一個(gè) 地址結(jié)構(gòu)體變量

struct sockaddr_in addr;

bzero(&addr,sizeof(addr)); //清0的函數(shù)

//2.之后進(jìn)行信息填充

addr.sin_family = AF_INET;

addr.sin_port = htons(atoi(argv[2]));

addr.sin_addr.s_addr = inet_addr(argv[1]);

//3.connect (撥打電話)

if(connect(fd,(struct sockaddr*)&addr,sizeof(addr))<0)

{

perror("connect fail");

return -1;

}

printf("connect success\n");

//通信過(guò)程

while(1)

{

//客戶端從鍵盤獲得數(shù)據(jù)

//數(shù)據(jù)流向stdin --> buf

fgets(buf,sizeof(buf),stdin); //stdin表示是從鍵盤獲得數(shù)據(jù)

//發(fā)送給服務(wù)器

write(fd,buf,strlen(buf));

//接受服務(wù)器回發(fā)的消息

ret = read(fd,rbuf,sizeof(rbuf));

//如果回發(fā)的消息是

//quit

//則結(jié)束

rbuf[ret] = '\0';

printf("rbuf = %s\n",rbuf);

if(strncmp("quit",buf,4) == 0)

{

close(fd);

break;

}

}

return 0;

}

服務(wù)端

#include

#include /* See NOTES */

#include

#include

#include

#include

//./server 127.0.0.1 8888

int main(int argc, const char *argv[])

{

int fd = 0;

int connfd = 0;

int ret = 0;

char buf[1024] = {0};

//處理命令行參數(shù)

if(argc != 3)

{

printf("Usage: %s\n",argv[0]);

return -1;

}

//1.socket 創(chuàng)建套接字

fd = socket(AF_INET,SOCK_STREAM,0);

if(fd < 0) //出錯(cuò)處理

{

perror("socket fail");

return -1;

}

printf("fd = %d\n",fd);

//2.綁定

//1.準(zhǔn)備地址信息

//2.綁定

//

//1.定義一個(gè) 地址結(jié)構(gòu)體變量

struct sockaddr_in addr;

bzero(&addr,sizeof(addr)); //清0的函數(shù)

//2.之后進(jìn)行信息填充

addr.sin_family = AF_INET;

addr.sin_port = htons(atoi(argv[2]));

addr.sin_addr.s_addr = inet_addr(argv[1]);

//127.0.0.1 是回環(huán)測(cè)試的地址

//3.進(jìn)行綁定

if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)

{

perror("bind fail");

return 0;

}

printf("bind success\n");

//4.設(shè)置監(jiān)聽(tīng)

if(listen(fd,5) < 0)

{

perror("listen fail");

return -1;

}

struct sockaddr_in peer_addr;

socklen_t addrlen = sizeof(peer_addr);

//5.獲取連接 -- 接聽(tīng)電話

while(1) //可以不斷接受客戶端的請(qǐng)求

{

//connfd = accept(fd,NULL,NULL);

connfd = accept(fd,(struct sockaddr*)&peer_addr,&addrlen);

if(connfd < 0)

{

perror("accept fail");

return -1;

}

printf("connfd = %d\n",connfd);

printf("-----------------------\n");

printf("ip = %s\n",inet_ntoa(peer_addr.sin_addr));

printf("port = %d\n",ntohs(peer_addr.sin_port));

printf("-----------------------\n");

//通信過(guò)程

//效果實(shí)現(xiàn)數(shù)據(jù)回發(fā)

while(1)

{

//read 與 write 的返回值 大于0 時(shí)表示的是

//一次成功操作到的字節(jié)數(shù)

ret = read(connfd,buf,sizeof(buf));

//hello

buf[ret] = '\0'; //添加'\0'--轉(zhuǎn)換成字符串

printf("buf = %s\n",buf);//字符串打印 需要

//結(jié)束標(biāo)志 '\0'

if(ret == 0 || strncmp(buf,"quit",4) == 0)

{

close(connfd);

break;

}

write(connfd,buf,ret);

}

} //telnet

return 0;

}

補(bǔ)充:

可以用如下函數(shù)替代read,write

ssize_t recv(int sockfd, void* buf,size_t len,int flags);

ssize_t send(int sockfd,const void *buf,size_t len,int flags);

@sockfd //進(jìn)行操作的socket的文件描述符

@buf //保存數(shù)據(jù)的首地址

@len //一次操作的字節(jié)數(shù)

@flags //標(biāo)志0--默認(rèn)的操作方式(阻塞)

返回值:

成功 成功操作的字節(jié)數(shù)

失敗 -1&errno

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 通信
    +關(guān)注

    關(guān)注

    18

    文章

    5880

    瀏覽量

    135328
  • TCP
    TCP
    +關(guān)注

    關(guān)注

    8

    文章

    1324

    瀏覽量

    78759

原文標(biāo)題:C語(yǔ)言中如何實(shí)現(xiàn)網(wǎng)絡(luò)通信(流程實(shí)例)

文章出處:【微信號(hào):xx-cyy,微信公眾號(hào):C語(yǔ)言編程基礎(chǔ)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    用labview做個(gè)服務(wù)器,和若干個(gè)客戶端同時(shí)進(jìn)行TCP通信

    用labview做個(gè)服務(wù)器,和若干個(gè)客戶端同時(shí)進(jìn)行TCP通信,有哪位高手做過(guò)的指點(diǎn)下小弟吧,不勝感激!
    發(fā)表于 05-21 19:01

    labview TCP客戶端

    最近在做一個(gè)labview 客戶端測(cè)試小程序,服務(wù)器采用MFC編寫,客戶端采用TCP偵聽(tīng)函數(shù),通信可以連接,數(shù)據(jù)也正確,但是
    發(fā)表于 06-30 23:15

    【NanoPi NEO試用體驗(yàn)】TCP通信客戶端程序

    不適合傳輸實(shí)時(shí)視頻這種數(shù)據(jù)量比較大的。通信雙方是基于C/S架構(gòu),這里使用電腦TCP助手作為服務(wù)器,NEO寫了客戶端的程序。雙方大致
    發(fā)表于 12-28 23:40

    labview-TCP客戶端服務(wù)器

    labview-TCP客戶端服務(wù)器一個(gè)服務(wù)器上位機(jī),多個(gè)下位機(jī)客戶端
    發(fā)表于 03-26 16:58

    如何使用Socket實(shí)現(xiàn)UDP客戶端?

    本教程介紹了如何利用socket 編程來(lái)實(shí)現(xiàn)一個(gè) UDP 客戶端,與服務(wù)器進(jìn)行通信。與開(kāi)發(fā) TCP 客戶
    發(fā)表于 03-30 07:39

    Labview TCP服務(wù)器和多個(gè)客戶端問(wèn)題

    我開(kāi)啟一個(gè)Labview TCP服務(wù)器,與外部?jī)蓚€(gè)TCP客戶端連接,兩個(gè)客戶端會(huì)定時(shí)給服務(wù)端發(fā)送
    發(fā)表于 04-13 18:43

    當(dāng)WiFi信號(hào)變低時(shí),服務(wù)器客戶端之間的TCP通信丟失,如何使客戶端重新連接?

    大家好, 當(dāng) WiFi 信號(hào)變低時(shí),服務(wù)器客戶端之間的 TCP 通信丟失,比如超過(guò) -80dBm。一旦客戶端斷開(kāi)連接,它就無(wú)法重新連接并正
    發(fā)表于 05-15 07:31

    服務(wù)器客戶端之間的TCP通信丟失怎么處理?

    嗨, 當(dāng) WiFi 信號(hào)變低時(shí),比如超過(guò) -80dBm,我面臨服務(wù)器客戶端之間的 TCP 通信丟失。一旦客戶端斷開(kāi)連接,它就無(wú)法重新連接并
    發(fā)表于 05-16 08:19

    TCP實(shí)現(xiàn)服務(wù)器客戶端通信流程實(shí)例

    //服務(wù)器端---服務(wù)器是一個(gè)被動(dòng)的角色 1.socket //買一個(gè)手機(jī) 2.bind //SIM卡 綁定一個(gè)手機(jī)號(hào)(ip+port) 3.listen //待機(jī)(等待電話打入)
    的頭像 發(fā)表于 04-06 11:49 ?1.2w次閱讀

    網(wǎng)絡(luò)調(diào)試和串口調(diào)試集合UDP TCP客戶端TCP服務(wù)器端應(yīng)用程序免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是網(wǎng)絡(luò)調(diào)試和串口調(diào)試集合UDP TCP客戶端TCP服務(wù)器端應(yīng)用程序免費(fèi)下載。
    發(fā)表于 08-30 08:00 ?16次下載
    網(wǎng)絡(luò)調(diào)試和串口調(diào)試集合UDP <b class='flag-5'>TCP</b><b class='flag-5'>客戶端</b>和<b class='flag-5'>TCP</b><b class='flag-5'>服務(wù)器端</b>應(yīng)用程序免費(fèi)下載

    Linux下網(wǎng)絡(luò)編程TCP并發(fā)服務(wù)器TCP客戶端程序免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是Linux下網(wǎng)絡(luò)編程TCP并發(fā)服務(wù)器TCP客戶端程序免費(fèi)下載
    發(fā)表于 01-08 15:12 ?9次下載
    Linux下網(wǎng)絡(luò)編程<b class='flag-5'>TCP</b>并發(fā)<b class='flag-5'>服務(wù)器</b>和<b class='flag-5'>TCP</b><b class='flag-5'>客戶端</b>程序免費(fèi)下載

    STM32+LWIP服務(wù)器實(shí)現(xiàn)客戶端連接

    (UCOSIII版本) 的基礎(chǔ)上進(jìn)行修改,實(shí)現(xiàn)客戶端連接的一個(gè)方法。1、TCP服務(wù)器創(chuàng)建過(guò)程建立一個(gè)TCP
    發(fā)表于 12-23 19:59 ?59次下載
    STM32+LWIP<b class='flag-5'>服務(wù)器</b><b class='flag-5'>實(shí)現(xiàn)</b>多<b class='flag-5'>客戶端</b>連接

    Linux下TCP網(wǎng)絡(luò)編程-創(chuàng)建服務(wù)器客戶端

    這篇文章介紹在Linux下的socket編程,完成TCP服務(wù)器、客戶端的創(chuàng)建,實(shí)現(xiàn)數(shù)據(jù)通信。
    的頭像 發(fā)表于 08-14 09:26 ?2292次閱讀
    Linux下<b class='flag-5'>TCP</b>網(wǎng)絡(luò)編程-創(chuàng)建<b class='flag-5'>服務(wù)器</b>與<b class='flag-5'>客戶端</b>

    基于LwIP的TCP客戶端設(shè)計(jì)

    上一篇我們基于LwIP協(xié)議棧的RAW API實(shí)現(xiàn)了一個(gè)TCP服務(wù)器的簡(jiǎn)單應(yīng)用,接下來(lái)一節(jié)我們來(lái)實(shí)現(xiàn)一個(gè)TCP
    的頭像 發(fā)表于 12-14 15:12 ?2136次閱讀
    基于LwIP的<b class='flag-5'>TCP</b><b class='flag-5'>客戶端</b>設(shè)計(jì)

    服務(wù)器Server和客戶端Client的區(qū)別

    例如在使用TCP通訊建立連接時(shí)采用客戶端服務(wù)器模式,這種模式又常常被稱為主從式架構(gòu),簡(jiǎn)稱為C/S結(jié)構(gòu),屬于一種網(wǎng)絡(luò)通訊架構(gòu),將通訊的雙方以客戶端(Client )與
    的頭像 發(fā)表于 09-06 16:13 ?1181次閱讀
    <b class='flag-5'>服務(wù)器</b>Server和<b class='flag-5'>客戶端</b>Client的區(qū)別