網(wǎng)站首頁 編程語言 正文
TCP介紹與編程流程
1、面向連接的流式協(xié)議;可靠、出錯重傳、且每收到一個數(shù)據(jù)都要給出相應的確認
2、通信之前需要建立鏈接
3、服務器被動鏈接,客戶端是主動鏈接
TCP C/S架構
TCP客戶端編程流程
- socket 創(chuàng)建套接字
- connect 連接服務器
- send 發(fā)送請求
- recv 接收應答
- close
1. 創(chuàng)建TCP套接字
socket創(chuàng)建的套接字:沒有端口、主動連接別人
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
注:TCP是SOCK_STREAM, UDP為SOCK_DGRAM
2. connect連接服務器
如果sockfd沒有被bind綁定,第一次調(diào)用connect系統(tǒng)自動分配隨機端口,后續(xù)使用該端口
int connect(int socket, const struct sockaddr *address, socklen_t address_len)
/*
socket: 套接字
address: 連接的服務器地址結構
address_len: 地址結構長度
*/
注:如果客戶端和服務器通信,必須使用connect事先建立連接
3. send發(fā)送請求
ssize_t send(int socket, const void *buffer, size_t length, int flags)
/*
socket: 客戶端套接字
buffer:發(fā)送的消息
length:消息長度
flags: 0
返回值:
成功:返回發(fā)送的字節(jié)數(shù)
失?。悍祷?1
*/
4. recv接收應答(默認帶阻塞)
ssize_t recv(int socket, void *buffer, size_t length, int flags);
/*
socket: 客戶端套接字
buffer: 接收的消息
length:能接收的最大長度
flags:0
返回值:
成功:成功接收的字節(jié)數(shù)
失敗:-1
*/
5. close
#include <unistd.h>
int close(int fildes);
6. 客戶端編程流程代碼
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, char const *argv[])
{
//創(chuàng)建tcp套接字 SOCK_STREAN
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
//connect連接服務器(知道服務器地址信息)
struct sockaddr_in ser_addr;
bzero(&ser_addr, sizeof(ser_addr));
ser_addr.sin_family = AF_INET;
ser_addr.sin_port = htons(8000);
ser_addr.sin_addr.s_addr = inet_addr("10.9.21.211");
connect(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr));
//客戶端發(fā)送請求
send(sockfd, "hello tcp", strlen("hello tcp"), 0 );
//客戶端接收服務器的應答
unsigned char buf[1500] = "";
int len = recv(sockfd, buf, sizeof(buf), 0);
printf("服務器的應答:%s\n", buf);
//關閉套接字
close(sockfd);
return 0;
}
TCP服務端編程流程
- socket 創(chuàng)建套接字
- bind 綁定固定的port、ip地址信息
- listen 監(jiān)聽套接字 創(chuàng)建連接隊列
- accept
- send 發(fā)送請求
- recv 接收應答
- close
1. 創(chuàng)建TCP套接字
socket創(chuàng)建的套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
注:TCP是SOCK_STREAM, UDP為SOCK_DGRAM
2. bind給服務器綁定固定的port與IP地址信息
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr)):
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8000);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)):
3. listen監(jiān)聽并創(chuàng)建隊列
listen監(jiān)聽:等待客戶端的連接到來,經(jīng)過三次握手(底層自動),將客戶端放入入連接隊列
#include <sys/socket.h>
int listen(int socket, int backlog);
/*
功能:
1. 將監(jiān)聽套接字由主動變被動
2. 為該套接字創(chuàng)建連接隊列
參數(shù):
socket:變被動的套接字
backlog:連接隊列的大小
返回值:
成功:0
失?。?1
*/
4. accept提取客戶端的連接(阻塞)
#include <sys/socket.h>
int accept(int sockfd,struct sockaddr *cliaddr, socklen_t *addrlen);
/*
功能:
從已連接隊列中取出一個已經(jīng)建立的連接,如果沒有任何連接可用,則進入睡眠等待(阻塞)
參數(shù):
sockfd: socket監(jiān)聽套接字
cliaddr: 用于存放客戶端套接字地址結構
addrlen:套接字地址結構體長度的地址
返回值:
已連接套接字
*/
注:返回的是一個已連接套接字,服務器只能通過已連接套接字和客戶端進行數(shù)據(jù)通信
5. send 發(fā)送消息到客戶端
ssize_t send(int socket, const void *buffer, size_t length, int flags)
/*
socket: 客戶端套接字
buffer:發(fā)送的消息
length:消息長度
flags: 0
返回值:
成功:返回發(fā)送的字節(jié)數(shù)
失?。悍祷?1
*/
注:如果ssize_t>0,表示發(fā)送成功(實際發(fā)送的字節(jié)數(shù))
如果ssize_t為-1,表示發(fā)送是失敗
tcp不允許send發(fā)送0長度報文(服務端接收0長度報文則為客戶端斷開連接)
udp允許sendto發(fā)送0長度報文
6. recv 接收客戶端的消息
ssize_t recv(int socket, void *buffer, size_t length, int flags);
/*
socket: 客戶端套接字
buffer: 接收的消息
length:能接收的最大長度
flags:0
返回值:
成功:成功接收的字節(jié)數(shù)
失?。?1
*/
注:如果ssize_t為0,表示客戶端已經(jīng)斷開連接
如果ssize_t>0,表示recv收到的實際字節(jié)數(shù)
如果ssize_t為-1,表示recv讀取數(shù)據(jù)出錯
7. close關閉所有套接字
#include <unistd.h>
int close(套接字);
注:close后會導致對方recv收到0長度報文
8. TCP服務端編程流程代碼
#include <stdio.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //struct sockaddr_in
#include <string.h> //memset
#include <arpa/inet.h> //htos
#include <unistd.h> //close
int main()
{
//創(chuàng)建套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0)
{
perror("cockt");
return 0;
}
//bind綁定固定的port、ip地址信息
struct sockaddr_in my_addr;
bzero(&my_addr, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9000);
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int ret = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
if(ret == -1)
{
perror("bind");
return 0;
}
//監(jiān)聽套接字 創(chuàng)捷連接隊列
ret = listen(sockfd, 10);
if(ret == -1)
{
perror("listen");
return 0;
}
//提取客戶端的連接
while(1)
{
//一次只能提取一個客戶端
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int cfd = accept(sockfd, (struct sockaddr *)&cli_addr, &cli_len);
if(cfd < 0 ) //提取失敗
{
perrer("accept\n");
break;
}
else
{
char ip[16] = "";
unsigned short port = 0;
inet_ntop(AF_INET, &cli_addr.sin_addr.s_addr, ip, 16);
port = ntohs(cli_addr.sin_port);
//打印客戶端的信息
printf("客戶端:%s %d connected\n", ip, port);
while(1)
{
//獲取客戶端的請求
unsigned char buf[1500] = "";
int len = recv(cfd, buf, sizeof(buf), 0);
if(len == 0) //客戶端已經(jīng)關閉
{
//關閉與客戶端連接的套接字
close(cfd);
break;
}
//應答客戶端
send(cfd, buf, len, 0);
}
}
}
//關閉監(jiān)聽套接字
close(sockfd);
return 0;
}
原文鏈接:https://blog.csdn.net/K3169/article/details/126818323
相關推薦
- 2022-10-03 React中路由參數(shù)如何改變頁面不刷新數(shù)據(jù)的情況_React
- 2022-08-03 使用Apache?Camel表達REST服務的方法_Linux
- 2023-10-16 使用elemnt移動端字體大小會自動變小
- 2022-12-26 C++內(nèi)存分區(qū)模型超詳細講解_C 語言
- 2022-04-29 C#實現(xiàn)實體類和XML的相互轉換_C#教程
- 2022-05-29 ASP.NET?Core在WebApi項目中使用Cookie_實用技巧
- 2022-11-25 Centos?8.2?升級內(nèi)核通過elrepo源的方法_云其它
- 2024-03-20 SpringBoot使用dynamic-datasource實現(xiàn)多數(shù)據(jù)源方案
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支