一、TCP連接
1.TCP特點:
(1)面向鏈接
(2)面向字節流
(3)安全可靠的傳輸協議,因為會先建立連接
(4)占用資源開銷大,效率低,實時性不佳,機制復雜
2.安全可靠機制
(1)三次握手:【客戶端發起】
指建立tcp連接時,需要客戶端和服務端總共發送三次報文確認連接。(確保雙方都已經 做好收發數據的準備)
????????ACK:響應報文
????????STN:請求建立一個連接
(2)四次揮手:【兩端都可發起】
斷開一個tcp連接,需要客戶端和服務端發送四個報文以確認斷開。(確保斷開前雙方都已經收發完畢)
FIN:finish標志,表示釋放連接
(3) 應答機制
每一個tcp頭部有個序列號和確認應答號,TCP給每一抱會有一個序列號,發送方會把第一個編號給序號,在接受方應答的時候,會把確認序列號置最后一個編號的下一個編號回應
通過序列號和確定序列號來確定收發的
(4)超時重傳機制
3.編程
(1)socket?
(2)connect
(1)功能: 發送三次握手鏈接請求
(2)參數:
sockfd:套接字文件描述符
addr:存放目的地址空間首地址
addrlen:目的地址長度
(3)返回值:
成功返回0?
失敗返回-1
(3)send
(1)功能:發送數據
(2)參數:
sockfd:套接字文件描述符
buf:存放數據空間首地址
len:數據長度
(3)返回值:
成功返回發送字節數
失敗返回-1
(4)recv? ? ?
(1)功能:接收數據?
(2)參數:
sockfd:套接字文件描述符
buf:存放數據空間首地址?
len:最多接收數據長度?
flags:接收屬性默認為0?
(3)返回值:
成功返回實際接收字節數
失敗返回-1?
對方連接斷開,直接返回,不在阻塞,沒有數據返回0;?
(5)bind
(6)listen
(1)功能:監聽三次握手鏈接請求
(2)參數:
sockfd:套接字文件描述符
backlog:最多允許等待尚未處理的三次握手鏈接個數
(3)返回值:
成功返回0?
失敗返回-1?
(7)accept
? ? 
(1)功能::處理三次握手等待隊列中的第一個請求并建立一個用來通信的新套接字
(2)參數:
sockfd:套接字文件描述符
addr:存放發送端IP地址空間首地址?
addrlen:想要接收的IP地址的長度?
(3) 返回值:
成功返通訊套接字
失敗返回-1?
4.提高效率
(1)延遲應答
(2)捎帶應答
(3)流量控制機制
發送端根據窗口的數據大小,去動態控制發送端的數據;0--65535,接收端根據自己的能力,去調整窗口的大小,65535的時候,接受端處理能力最強,發送端可以發快一點。
(4)滑動窗口
? ? ? ? 滑動窗口大小:是TCP流量控制得一個手段。目的是告訴對方, 本端得TCP接受緩沖區還能容納多少字節得數據,這樣對方就可以控制發送數據的速度,從而達到流量控制,16bit,因而窗口最大65535.
? ? ? ? 本質是一段緩沖區:通過指針把緩沖區分為幾個部分:
? ? ? ? 已發送并且收到應答的數據、已發送未收到應答的數據、未發送但在對方處理能力內的數據;未發送但不在對方處理能力內的數據;
慢慢滑動,已發送并且收到的數據滑出。
二、?TCP粘包問題【高頻面試題】
? ? TCP協議是面向字節流的協議,接收方不知道消息的界限,不知道一次提取多少數據,這就造成了粘包問題。
1. 粘包問題出現的原因:?
(1)發送端:需要等緩沖區滿時才發送出去,造成粘包;發送數據太快
(2)接收端:不及時的接收緩沖區內的包,造成多個包接收。處理數據太慢導致數據在緩沖區緩存
2.避免粘包問題的方法:
(1)對于定長的包,保證每次都按固定大小發送和讀取即可;// ?結構體
? ? ? ? 1)問題:
? ? ? ? 結構體對齊問題:假設發送方和接收方分別32字節、64字節;在不同字節平臺結構體對齊長度不一樣;所以要確保雙方結構體對齊方式一樣;或者指定按照1個字節對齊,注意,結構體不可放指針;
在雙方通信時,發送方的發送數據類型不一樣,接收很難確認和區分接受的大小;所以不適用于數據類型多樣化;
(2) 對于變長的包,還可以在包和包之間使用明確的分隔符,這個分隔符是由程序員自己來定的,只要保證分隔符不和正文沖突即可。應用層根據分隔符進行解析
(3)自定義一個應用層的數據協議幀;
????????如果數據里面有幀頭或者幀尾 ====》在幀頭后面加入一個長度,這個長度規定,向后多少個
????????首先找幀頭和有效字節長度,向后讀有效字節長度并且讀完了看最后是不是幀尾
????????在后面加個校驗
????????所以包含:幀頭(AA)、幀尾(BB)、有效數據長度(len)、校驗(例如8位和校驗()、16位和校驗、CRC校驗、和其他復雜校驗算法)

三、TCP和UDP區別
????????都是網絡中傳輸層的傳輸協議
????????UDP叫做,無需建立鏈接、直接發送給接收方,需要對方的地址,面向數據包;存在丟包,盡最大努力叫覅,不安全不可靠;由于沒有很多機制,頭部小,資源開銷打;要求實時性、不要求數據;
????????TCP需要建立TXP鏈接,面向字節流,有一系列機制可以確保安全機制,三次握手四次揮手、應答機制、流量控制、流動窗口、超時重傳;由于很多機制。頭部大。資源開銷打,要求數據傳輸安全性高
四、代碼練習
????????(1)使用TCP實現全雙工(進程)
客戶端
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include<pthread.h>
#include <errno.h>
int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50001);seraddr.sin_addr.s_addr = inet_addr("192.168.245.128");char buf[1024] = {0};int ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("connect fail");return -1;}pid_t pid = fork();if(pid < 0){perror("fork fail");return -1;}if(pid > 0){while(1){memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), stdin);ssize_t size = send(sockfd, buf, strlen(buf)-1, 0);if(size < 0){perror("send fail");return -1;}}}if(pid == 0){while(1){size_t size = recv(sockfd, buf, sizeof(buf), 0);if(size < 0 ){perror("recv fail");return -1;}if(size == 0){break;}buf[strlen(buf)] = '\0';printf("ser buf = %s\n",buf);}}close(sockfd);
}
服務端
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include<pthread.h>
#include <errno.h>
int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50001);seraddr.sin_addr.s_addr = inet_addr("192.168.245.128");char buf[1024] = {0};int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("bind fail");return -1;}ret = listen(sockfd, 10);if(ret < 0){perror("listen fail");return -1;}int connfd = accept(sockfd, NULL, NULL);if(connfd < 0){perror("connfd fail");return -1;}pid_t pid = fork();if(pid < 0){perror("fork fail");return -1;}if(pid > 0){while(1){ssize_t size = recv(connfd, buf, sizeof(buf), 0);if(size < 0){perror("size fail");return -1;}if(size == 0){break;}buf[strlen(buf)] = '\0';printf("buf = %s\n",buf);}}if(pid == 0){while(1){memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), stdin);size_t size = send(connfd, buf, strlen(buf)-1, 0);if(size < 0){perror("send fail");return -1;}}}close(connfd);return 0;
}
? ? ? ? (2)使用TCP實現文件傳輸(傳輸過來的文件名不變)
客戶端
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include<pthread.h>
#include <errno.h>
int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50025);seraddr.sin_addr.s_addr = inet_addr("192.168.1.193");char buf[1024] = {0};char buffer[1024] = {0};int ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("connect fail");return -1;}int fd = open("../1.txt", O_RDWR);int fp = open("2.txt", O_RDWR);if(fd < 0){perror("open fail");return -1;}if(fp < 0){perror("open fail");return -1;}pid_t pid = fork();if(pid < 0){perror("fork fail");return -1;}if(pid > 0){while(1){memset(buf, 0, sizeof(buf));memset(buffer, 0, sizeof(buffer));fgets(buf, sizeof(buf), stdin);if(strncmp(buf, "1.txt", 5) == 0){int ret1 =read(fd, buffer, sizeof(buffer));printf("ret1 = %d\n",ret1);// ssize_t size = send(sockfd, buffer, strlen(buffer)-1, 0);ssize_t size = send(sockfd, buffer, ret1, 0);if(size < 0){perror("send fail");return -1;}memset(buffer, 0, sizeof(buffer));}}}if(pid == 0){while(1){memset(buffer, 0, sizeof(buffer));size_t size = recv(sockfd, buffer, sizeof(buffer), 0);if(size < 0 ){perror("recv fail");return -1;}buffer[strlen(buffer)] = '\0';write(fp, buffer,strlen(buffer));//buf[strlen(buf)] = '\0';printf("ser buffer = %s\n",buffer);memset(buffer, 0, sizeof(buffer));}}close(sockfd);
}
服務端
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include<unistd.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <sys/wait.h>
#include<pthread.h>
#include <errno.h>
int main()
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50025);seraddr.sin_addr.s_addr = inet_addr("192.168.1.193");char buf[1024] = {0};char buffer[1024] = {0};int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("bind fail");return -1;}ret = listen(sockfd, 10);if(ret < 0){perror("listen fail");return -1;}int connfd = accept(sockfd, NULL, NULL);if(connfd < 0){perror("connfd fail");return -1;}int fd = open("1.txt", O_RDWR);int fp = open("../2.txt", O_RDWR);pid_t pid = fork();if(pid < 0){perror("fork fail");return -1;}if(pid > 0){while(1){memset(buffer, 0, sizeof(buffer));ssize_t size = recv(connfd, buffer, sizeof(buffer), 0);if(size < 0){perror("size fail");return -1;}buffer[strlen(buffer)] = '\0';write(fd, buffer,strlen(buffer));//buf[strlen(buf)] = '\0';printf("buffer = %s\n",buffer);memset(buffer, 0, sizeof(buffer));}}if(pid == 0){while(1){memset(buffer, 0, sizeof(buffer));memset(buf, 0, sizeof(buf));fgets(buf, sizeof(buf), stdin);if(strncmp(buf, "2.txt", 5) == 0){int ret1 =read(fp, buffer, sizeof(buffer));printf("ret1 = %d\n",ret1);size_t size = send(connfd, buffer, strlen(buffer)-1, 0);if(size < 0){perror("send fail");return -1;}memset(buffer, 0, sizeof(buffer));}}}close(connfd);return 0;
}