目錄
一、套接字
1、套接字的作用
2、關于TCP和UDP協議
1. TCP協議
2. UDP協議
3. 兩者的區別
2、套接字函數
1)函數 socket(創建套接字同文件描述符)
2)準備套接字用結構體
1. 套接字的結構體
2. 客戶端的套接字:
3. 服務器的套接字:
3)bind(以結構體向套接字中寫入 ip、port)
4)send / write(通過套接字發送數據)
5)recv / read(通過套接字接收數據)
6)fcntl(變更函數狀態:阻塞/非阻塞)
一、套接字
套接字在最初只是一種IPC通訊手段,在TCP協議出現,TCP協議使用套接字進行通信,因此我們常說的套接字都是應用于網絡通信,而早期只用于進程間通訊的則稱為域套接字
1、套接字的作用
專門用于網絡間通信的一種文件形式
該文件,包含了要交換的數據和通信雙方的 ip地址和 port端口號
ip地址和 port端口號需要我們自己添加到套接字中,并且不同的協議需要使用不同類型的套接字
2、關于TCP和UDP協議
TCP協議和UDP協議的套接字就是不同類型的
1. TCP協議
TCP協議是一個可靠的,連續的,基于字節流的協議,TCP具有流量控制功能,順序控制功能,應答重發功能,以確保在網絡不擁堵的時候,所有的數據都可以正確的發送
2. UDP協議
UDP協議由于非連續性,且沒有有效的應答手段,所以不可靠,很容易丟包
向前糾錯技術(后來各大廠商對UDP協議的更進,添加了校驗的方法)
udp協議在發送數據的時候,會選擇在發送 n 個數據的時候,匹配發送 m 個校驗包
比如說發送 3個數據的時候,匹配發送1個校驗包
最簡單的校驗包 = 1#數據報 + 2#數據報 + 3#數據報
比如說:發送了 1 ,2,3 這3個數據,此時,再發送一個校驗包,校驗包的值是 4
很明顯,在丟包率為 25% 的時候,3個包配一個校驗包,就能完全避免丟包
因為4個包里面丟一個,丟了校驗包完全無所謂
丟的是任意一個數據包,可以通過校驗包和其他數據包,恢復出丟失的數據是多少
依此類推:如果網絡波動變大,我們就需要配更多的校驗包,以保證丟包率減低,恢復率提高
3. 兩者的區別
TCP協議穩定性高,傳輸速度慢,UDP協議穩定性差,傳輸速度快
2、套接字函數
1)函數 socket(創建套接字同文件描述符)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:創建不同類型的套接字用文件描述符
參數 domain:套接字所依賴的網絡介質如果是 ipv4 就填入 AF_INET如果是 ipv6 就填入 AF_INET6如果是 域套接字 就填入 AF_LOCAL / AF_UNIX
參數 type:選擇套接字的類型SCOK_STREAM:字節流套接字傳輸數據,連續,可靠,雙向,數據量大SOCL_DGRAM:數據包套接字傳輸數據,不連續,不可靠,有長度要求,雙向
參數 protocol:選擇套接字所依賴的通信協議0:自動匹配,會根據參數 type 和參數 protocol 自動選擇合適的通信協議
返回值:返回創建套接字文件描述符一般來說:AF_INET + SOCK_STREAM + 0 ,最終創建的是 TCP 協議的套接字AF_INET + SOCK_DGRAM + 0 ,最終創建的是 UDP 協議的套接字
????????字節流的優點:允許發送無窮大的數據,會在內核中對數據做分割處理,但是由于連續發送,數據依然粘連在一起
? ? ? ? 字節流的缺點:由于數據是粘連的,因此接收端需要花費大量的時間處理數據
? ? ? ? 數據包的優點:數據不會粘連,發送幾次數據就是幾個數據包
? ? ? ? 數據包的缺點:發送的數據大小有上限,過大的數據需要手動分批次發送,還有丟包的風險
2)準備套接字用結構體
1. 套接字的結構體
struct sockaddr_in {__kernel_sa_family_t sin_family; /* 依賴的網絡介質 */__be16 sin_port; /* port端口號 */struct in_addr sin_addr; /* ip地址 */
};
? ? ? ? 127.0.0.1 是一個本地回環地址,不會經過網卡,專門用來在無網絡的情況下做本地測試,
抓包工具無法抓到這個 ip地址 的數據
2. 客戶端的套接字:
寫入 ip地址 的目的 是為了通過 ip地址,找到該客戶端想要連接的服務器
寫入 port端口號 的目的 是為了通過端口號,知道數據需要發送到服務器的哪個進程??
3. 服務器的套接字:
寫入 ip地址 的目的 是過濾掉不想接收的客戶端,選擇想要連接的客戶端
寫入 port端口號 的目的 是為了讓服務器知道要去哪個進程讀取接收到的數據
3)bind(以結構體向套接字中寫入 ip、port)
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:往套接字中寫入 ip地址 和 port端口號 (該操作被稱為為套接字命名)
參數 sockfd:填寫 套接字用文件描述法
參數 addr:通用套接字結構體指針,需要提前準備 struct sockaddr_in 類型的結構體
參數 addrlen:參數 addr 的字節長度
4)send / write(通過套接字發送數據)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:通過套接字,向套接字中指向的ip地址發送數據
參數 sockfd:填入 socket創建的套接字文件描述符
參數 buf:填入 要發送的數據的地址
參數 len:填入 要發送的字節的長度
參數 flag:設置函數的狀態 阻塞 / 非阻塞0 :默認阻塞,發送數據給目標,目標的接收區滿了,就會發送阻塞MSG_DONTWAIT:非阻塞,發送數據給目標,接收區滿了,丟棄新發送的數據
=======================================================================
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
用法基本一致,IO篇也有詳細介紹
5)recv / read(通過套接字接收數據)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能:通過套接字,讀取套接字中ip地址所發送的數據
參數 sockfd:由socket創建的套接字文件描述符
參數 buf:將讀取到的數據存入buf所指向的連續地址(所指向的數組)
參數 len:所讀取數據的字節長度
參數 flags:設置函數的狀態 阻塞 / 非阻塞0 :默認阻塞,沒有接收到數據就阻塞MSG_DONTWAIT:非阻塞,沒有讀取到數據直接返回 0
返回值:阻塞模式:返回接收到數據的字節數,若套接字損壞 返回 -1若服務器與客戶端連接斷開,則有阻塞函數變為非阻塞函數,并返回 0非阻塞模式:返回接收到數據的字節數,若服務器與客戶端斷開,返回 -1未接收到參數,則返回 0
=====================================================================
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
用法基本一致,IO篇也有詳細介紹
6)fcntl(變更函數狀態:阻塞/非阻塞)
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
功能:設置指定文件描述符的狀態,也就是flags屬性
參數 cmd:設置函數的功能 設置 flags 或 讀取 flagsF_SETFL:設置 fd 文件的 flags 屬性F_GETFL:獲取 fd 文件的 flags 屬性
不定參 ···:此處只有第一個數據是有效的僅當 cmd == F_SETFL 時,需要傳一個不定參作為 flags
返回值:當 cmd == F_SETFL 時,成功返回 0 ,失敗返回 -1當 cmd == F_GETFL 時,返回 flags ,失敗返回 -1
使用時要注意不要變更原有的狀態,因此盡可能追加flags值(若有需要替換原先的值,也可以換)
使用流程:
1、先獲取 fd 原有的 flags 屬性
2、對獲取到的 flags 屬性追加
3、將追加好的 flags 屬性,設置為 fd 的 flags 屬性
以標準輸入流為例