Socket
socket是實現網絡主機進程間通信的一種機制。
從用戶空間來看,socket就是一個文件描述符,對socket的操作等同於對普通的文件描述符操作,可以使用read、write、close函數來操作,一旦針對該socket必要的初始化完成後,與對端的數據交互都是通過該socket來實現的
例如:
- 要向對方發送數據,只需要將數據write到該socket;
要接收數據,只要阻塞地在socket上讀數據即可;
從內核空間來看,socket不再指向一個磁盤文件,相應地讀寫指針指向的代碼也是網卡驅動程序提供的數據發送和接收函數。其主要的資源是一個內核空間的struct sk_buff結構體對象。在該對象中詳細的描述了通信雙方的基本信息,緩衝的數據等。
根據是否面向連接,可將socket通信分爲面向連接的數據流通信和面向無連接的數據流通信。這兩者在實現上有類似的地方,即都需要創建相應的socket對象,但是,這兩者也有明顯的區別
1、面向連接的TCP通信需要雙方建立可行的數據連接後才能通信
2、而面向無連接的UDP通信則只是簡單的將數據發送到對應的目的主機即可,而不管對方是否處於存活狀態,對方是否允許接收該數據包以及該數據包是否完整地被髮送到目標主機
面向來連接的數據流方式:此類型的套接字是可靠的,在這種套接字中,數據傳送和發送順序一致。在傳輸數據之前,通信雙方需要建立可靠的鏈路,在傳送過程中,數據作爲字節流傳輸。這種傳輸數據的方式可靠性高,如:TCP
面向無連接的數據報方式:此類型是不可靠的,這種套接字,數據傳送和發送順序可能不一樣。在發送和接收之間沒有邏輯連接,每個數據報的發送和處理都是獨立的,不同的數據報可以採用不同的路由路徑達到目的地。如:UDP
基於BSD的socket爲應用開發提供了統一的編程接口,只需要調用BSD socket所提供的編程函數即可
sockaddr結構
如果是IPV4網絡通信,
socket編程接口
socket常見API
//創建socket文件描述符(TCP/UDP,客戶端&&服務器)
int socket(int domain, //AF_INET(IPV4),AF_INET6(IPV6)
int type, //SOCK_STREAM(TCP),SOCK_DGRAM(UDP),SOCK_RAW(原始套接口)
int protocol) //0
- domain:用來指明此socket對象所使用的地址簇或者協議簇,就是此對象所使用的通信協議類型(地址簇是協議簇宏定義,實際上是一致的)
- type:socket類型
- protocol:標識採用協議簇中的哪一種協議,一般設置爲0,代表讓系統自動選擇默認協議,但原始套接口需要指定具體的協議
//綁定端口號(TCP/UDP,服務器)
int bind(int _fd,
const struct sockaddr *address,//sockaddr結構指針
socket_t address_len);//綁定的地址長度,一般用sizeof
//監聽網絡(socket)(TCP,服務器)
int listen(int _fd,
int _n);
//_fd:綁定了IP及端口信息的socket文件描述符
//_n:請求排隊的最大長度。當有多個客戶端和服務器相連時,這個值表示可以使用的處於等待的隊列長度
listen函數將綁定的socket文件描述符變爲監聽套接字,此時,服務器已經準備接收客戶端連接請求了
//客戶端發起連接
int connet(int _fd,
const struct sockaddr *address,
socket_t address_len);
//服務器接收數據
int accept(int _fd,
struct sockaddr *address,
socket_t address_len);
讀/寫socket對象
socket對象是一種特殊的文件,因此可以使用read函數來讀socket對象數據,write函數向socket對象寫入數據
TCP發送接收數據
size_t send(int _fd,
const void *buf,//要發送的數據
size_t n, //要發送數據的大小
int flags); //0
size_t recv(int _fd,const void *buf,size_t n,int flags);
//兩個函數的第四個參數flags用來說明數據處理的方式
關閉socket對象
在通信結束後,需要關閉socket對象,這裏有兩種方法
1、close函數
int close(int _fd);
2、 shutdown函數
int shutdown(int _fd,int _how);
shutdown函數靈活性更大,可以關閉全部,或者關閉socket的一端
TCP連接是雙向的(可讀寫的),當使用close()時,會把讀寫通道都關閉,有時希望只關閉一個方向,這時就要用shutdown。系統提供了以下3種關閉方式:
- how=0:系統關閉讀通道,但可以繼續往socket描述符中寫
- how=1:關閉寫通道,此時只能讀
- how=2:關閉讀寫通道,和close一樣,完全關閉
獲取socket本地的和對端的信息
1、getsockname函數
獲取一個套接字的本地信息,(這個套接字至少完成了綁定本地IP地址)。
int getsockname(int fd,_SOCKADDR _addr,socklen_t *_restrict len)
2、getpeername函數
獲取一個已經連接上的套接字的遠程信息,如IP地址和端口
int getpeername(int fd,_SOCKADDR _addr,socklen_t *_restrict len)
應用實例
struct sockaddr test;
getsockname(new_fd,(struct sockaddr *)&test,&size);
printf("ip=%s,port=%d\n",inet_ntoa(test.sin_addr),ntohs(test.sin_port));