主要函數(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 //接聽電話
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)
@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)監(jiān)控是否有客戶端發(fā)起連接
int listen(int sockfd, int backlog);
功能:
設(shè)置監(jiān)聽
參數(shù):
@sockfd //監(jiān)聽套接字
@backlog //監(jiān)聽隊(duì)列的大小
返回值
成功 0
失敗 -1
listenfd
--------------監(jiān)聽隊(duì)列------------------
fd1 fd2 fd3 fd4
|
-|--------------------------------------
|
\---->建立好連接的套接字
accept函數(shù)獲取已連接的套接字 返回對(duì)應(yīng)
的標(biāo)識(shí)符
|--->后面的讀寫操作 都是通過這個(gè)標(biāo)識(shí)符
進(jìn)行的
-----------------------------------------------
accept(); //accept 從監(jiān)聽隊(duì)列中獲得已連接的的socket,返回一個(gè)標(biāo)示符來表示已連接的socket
//后續(xù)通過已連接的socket進(jìn)行通信
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能: 獲取連接
參數(shù):
@sockfd //監(jiān)聽套接字的fd(標(biāo)識(shí)符)
@addr //來電顯示(保存對(duì)端的地址信息)(ip+port)
@addrlen //表示 addr 參數(shù)對(duì)應(yīng)類型的大小,值結(jié)果參數(shù) --- 就是在用的時(shí)候,必須先賦一個(gè)初值,最后函數(shù)調(diào)用完成
//通過該參數(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 //通過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ù)出來
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"
"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");
//通信過程
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)聽
if(listen(fd,5) < 0)
{
perror("listen fail");
return -1;
}
struct sockaddr_in peer_addr;
socklen_t addrlen = sizeof(peer_addr);
//5.獲取連接 -- 接聽電話
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");
//通信過程
//效果實(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
-
通信
+關(guān)注
關(guān)注
18文章
5926瀏覽量
135703 -
TCP
+關(guān)注
關(guān)注
8文章
1337瀏覽量
78865
原文標(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)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論