主要函數(shù):
------------------------------------------
//服務器端---服務器是一個被動的角色
1.socket //買一個手機
2.bind //SIM卡 綁定一個手機號(ip+port)
3.listen //待機(等待電話打入)
4.accept //接聽電話
6.close //掛機
//客戶端---客戶端是一個主動發(fā)起請求的一端
1.socket //買一個手機
2.bind(可選的) //SIM卡(綁定號碼)
3.connect //撥打電話
4.read/write //通話
5.close //掛機
//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é)議
返回值:
成功 對應的socket文件描述符
失敗 返回-1
注意:
文件描述符:
實際上就是 創(chuàng)建好的 socket的一個標識符
//2.bind
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:
給指定的 socket 綁定地址信息
參數(shù):
@sockfd //表示操作的socket
@addr //填充的地址信息(ip + port)
@addrlen //地址信息結構體的大小
返回值:
成功 0
失敗 -1
//通用的地址結構
struct sockaddr {
sa_family_t sa_family; //AF_INET //IPV4的協(xié)議
char sa_data[14];//(ip+port)
}
//網絡通信的地址結構(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.定義一個 地址結構體變量
struct sockaddr_in addr;
bzero(&addr,sizeof(addr)); //清0的函數(shù)
//2.之后進行信息填充
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)測試的地址
//3.進行綁定
if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind fail");
return 0;
}
//3.listen --- 設置監(jiān)聽 ---作用:讓操作系統(tǒng)監(jiān)控是否有客戶端發(fā)起連接
int listen(int sockfd, int backlog);
功能:
設置監(jiān)聽
參數(shù):
@sockfd //監(jiān)聽套接字
@backlog //監(jiān)聽隊列的大小
返回值
成功 0
失敗 -1
listenfd
--------------監(jiān)聽隊列------------------
fd1 fd2 fd3 fd4
|
-|--------------------------------------
|
\---->建立好連接的套接字
accept函數(shù)獲取已連接的套接字 返回對應
的標識符
|--->后面的讀寫操作 都是通過這個標識符
進行的
-----------------------------------------------
accept(); //accept 從監(jiān)聽隊列中獲得已連接的的socket,返回一個標示符來表示已連接的socket
//后續(xù)通過已連接的socket進行通信
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能: 獲取連接
參數(shù):
@sockfd //監(jiān)聽套接字的fd(標識符)
@addr //來電顯示(保存對端的地址信息)(ip+port)
@addrlen //表示 addr 參數(shù)對應類型的大小,值結果參數(shù) --- 就是在用的時候,必須先賦一個初值,最后函數(shù)調用完成
//通過該參數(shù),返回一個結果值
返回值:
成功 已連接的socket的標識符
失敗 -1
//connect ---發(fā)起連接
int connect(
int sockfd, //表示 進行通信的 socket的標識符
const struct sockaddr *addr, //對端的地址信息(ip+port)
socklen_t addrlen); //表示的是 addr 參數(shù)類型的長度
參數(shù):
@sockfd //通過socket函數(shù)獲得的fd
@addr //服務器端的地址
@addrlen //參數(shù)addr類型的大小
//數(shù)據(jù)流向 fd --> buf (count 表示一次讀取多少個字節(jié))
ssize_t read(int fd, void *buf, size_t count);
//數(shù)據(jù)流向 buf--> fd (count 表示一次寫多少個字節(jié))
ssize_t write(int fd, const void *buf, size_t count);
參數(shù):
@fd 就是要操作的 socket對應的 標示符
@buf 保存數(shù)據(jù)的一塊內存首地址
@count 一次操作的字節(jié)數(shù)
confd
char buf[] = "hello QCXY\n";
write(confd,buf,strlen(buf)); //寫 數(shù)據(jù)到socket中
//讀數(shù)據(jù)出來
char rbuf[1024] = {0}; //表示申請了一塊1024個字節(jié)大小
//的內存空間
read(confd,rbuf,sizeof(rbuf)); //讀取數(shù)據(jù)
//練習:
實現(xiàn) 客戶端 向服務器發(fā)送數(shù)據(jù)
服務器回發(fā)數(shù)據(jù)的功能
client ----- server
scanf(); ---(1)----> read 之后printf
read <--(2)---- ? ? ? ? write
printf
循環(huán)做,結束條件
當客戶端輸入 "quit"字符串時 客戶端結束
怎么判斷客戶端讀到的是"quit"
c語言中
"quit" == buf; (X) //不能這么寫
//字符串的比較函數(shù)
strcmp("quit",buf);
strncmp("quit",buf,4);
客戶端的程序:
#include
#include
#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(手機)
//2.bind(電話卡)
//3.connect (撥打電話)
//處理命令行參數(shù)
if(argc != 3)
{
printf("Usage: %s
return -1;
}
//1.socket(手機)
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0) //出錯處理
{
perror("socket fail");
return -1;
}
printf("fd = %d\n",fd);
//2.bind(電話卡)---綁定的是客戶端自己的地址信息
//客戶端地址信息
//1.定義一個 地址結構體變量
struct sockaddr_in cli_addr;
bzero(&cli_addr,sizeof(cli_addr)); //清0的函數(shù)
//2.之后進行信息填充
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;
}
//服務器端的地址信息
//1.定義一個 地址結構體變量
struct sockaddr_in addr;
bzero(&addr,sizeof(addr)); //清0的函數(shù)
//2.之后進行信息填充
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ā)送給服務器
write(fd,buf,strlen(buf));
//接受服務器回發(fā)的消息
ret = read(fd,rbuf,sizeof(rbuf));
//如果回發(fā)的消息是
//quit
//則結束
rbuf[ret] = '\0';
printf("rbuf = %s\n",rbuf);
if(strncmp("quit",buf,4) == 0)
{
close(fd);
break;
}
}
return 0;
}
服務端
#include
#include
#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
return -1;
}
//1.socket 創(chuàng)建套接字
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0) //出錯處理
{
perror("socket fail");
return -1;
}
printf("fd = %d\n",fd);
//2.綁定
//1.準備地址信息
//2.綁定
//
//1.定義一個 地址結構體變量
struct sockaddr_in addr;
bzero(&addr,sizeof(addr)); //清0的函數(shù)
//2.之后進行信息填充
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)測試的地址
//3.進行綁定
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
{
perror("bind fail");
return 0;
}
printf("bind success\n");
//4.設置監(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) //可以不斷接受客戶端的請求
{
//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");
//通信過程
//效果實現(xiàn)數(shù)據(jù)回發(fā)
while(1)
{
//read 與 write 的返回值 大于0 時表示的是
//一次成功操作到的字節(jié)數(shù)
ret = read(connfd,buf,sizeof(buf));
//hello
buf[ret] = '\0'; //添加'\0'--轉換成字符串
printf("buf = %s\n",buf);//字符串打印 需要
//結束標志 '\0'
if(ret == 0 || strncmp(buf,"quit",4) == 0)
{
close(connfd);
break;
}
write(connfd,buf,ret);
}
} //telnet
return 0;
}
補充:
可以用如下函數(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 //進行操作的socket的文件描述符
@buf //保存數(shù)據(jù)的首地址
@len //一次操作的字節(jié)數(shù)
@flags //標志0--默認的操作方式(阻塞)
返回值:
成功 成功操作的字節(jié)數(shù)
失敗 -1&errno
-
TCP
+關注
關注
8文章
1349瀏覽量
78985 -
客戶端
+關注
關注
1文章
290瀏覽量
16661 -
通信編程
+關注
關注
0文章
5瀏覽量
2027
原文標題:C語言中如何實現(xiàn)網絡通信(流程實例)
文章出處:【微信號:cyuyanxuexi,微信公眾號:C語言編程學習基地】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論