socket基礎詳解 小白白

qq_45743563發表於2020-10-30
\#include <arpa/inet.h>

uint16_t htons(uint16_t hostshort);(將主機位元組序轉換為網路位元組序162個位元組) 

uint32_t htonl(uint32_t hostlong);(將主機位元組序轉換為網路位元組序324個位元組)

uint32_t ntohl(uint32_t netlong);  將網洛位元組序轉換為主機位元組序 (324位元組)

uint16_t ntohs(uint16_t netshort);  將網路位元組序轉換為主機位元組序(162位元組)

inet_pton() 將字串型別的位元組序轉換為網路位元組序

inet_ntop()網路位元組序的地址轉換位字串型別的地址

其中inet_pton和inet_ntop不僅可以轉換IPv4的in_addr,還可以轉換IPv6的in6_addr。

int inet_pton(int af, const char *src, void *dst);const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

af 取值可選為 AF_INET 和 AF_INET6 ,即和 ipv4 和ipv6對應

支援IPv4和IPv6
在這裡插入圖片描述

    *sockaddr資料結構*

很多網路程式設計函式誕生早於IPv4協議,那時候都使用的是sockaddr結構體,為了向前相容,現在sockaddr(以前的)退化成了(void *)的作用,傳遞一個地址給函式,至於這個函式是sockaddr_in(現在的需轉換成以前的結構指標保證相容)(可放更多的ip 佔更多的空間)還是其他的,由地址族確定,然後函式內部再強制型別轉化為所需的地址型別。


#include <sys/types.h> 

#include <sys/socket.h>

socket函式//建立信箱

int socket(int domain, int type, int protocol);

domain:

​	AF_INET 這是大多數用來產生socket的協議,使用TCP或UDP來傳輸,用IPv4的地址

    AF_INET6 與上面類似,不過是來用IPv6的地址

​	AF_UNIX 本地協議,使用在Unix和Linux系統上,一般都是當客戶端和伺服器在同一臺及其上的時候  使用同一臺主機沒必要經過網路

type:

​	SOCK_STREAM 這個協議是按照順序的、可靠的、資料完整的基於位元組流的連線。這是一個使用最多的socket型別,這個socket是使用TCP來進行傳輸。

​	SOCK_DGRAM 這個協議是無連線的、固定長度的傳輸呼叫。該協議是不可靠的,使用UDP來進行它的連線。

​	SOCK_SEQPACKET該協議是雙線路的、可靠的連線,傳送固定長度的資料包進行傳輸。必須把這個包完整的接受才能進行讀取。

​	SOCK_RAW socket型別提供單一的網路訪問,這個socket型別使用ICMP公共協議。(ping、traceroute使用該協議)

​	SOCK_RDM 這個型別是很少使用的,在大部分的作業系統上沒有實現,它是提供給資料鏈路層使用,不保證資料包的順序

protocol:

​	傳0 表示使用預設協議。

返回值:

​	成功:返回指向新建立的socket的檔案描述符,失敗:返回-1,設定errno
*bind 函式*//繫結
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd:

​	socket檔案描述符

addr:

​	構造出IP地址加埠號

addrlen:sizeof(addr)長度

返回值:

​	成功返回0,失敗返回-1, 設定errno
struct sockaddr_in servaddr;

bzero(&servaddr, sizeof(servaddr));//使用之前必須清空

servaddr.sin_family = AF_INET;//協議

servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//監聽的地址

servaddr.sin_port = htons(6666);//埠
*listen 函式*//監聽
int listen(int sockfd, int backlog);

sockfd:

​	socket檔案描述符

backlog:

​	在Linux 系統中,它是指排隊等待建立3次握手佇列長度 如果這個佇列滿了再有客戶端來進行三次握手是失敗的,直到完成3次握手之後的客戶端連結被清出佇列,就空出了位置 ,就可以繼續連結,併發越大就設定大一點,如果這個佇列滿了,多餘的客戶端連結就會被忽略掉

檢視系統預設backlog

cat /proc/sys/net/ipv4/tcp_max_syn_backlog

`` 

改變 系統限制的backlog 大小

vim /etc/sysctl.conf

最後新增
net.core.somaxconn = 1024

net.ipv4.tcp_max_syn_backlog = 1024


儲存,然後執行
sysctl -p

檢視是否生效

sysctl -a | grep 1024

//相當於可以同時等待監聽最多有多少個連結 返回int型別 listen()成功返回0,失敗返回-1。 錯誤都會在errno中 這個佇列存放還沒有進行三次握手的連結 當完成三次握手之後 就執行accept(接受連結)就可以接收資料

//三次握手概念:(客戶端commect 之後傳送訊息給服務端 這是第一次握手,服務端收到回一個資訊給客戶端,這是第二次握手,客戶端收到再發信給服務端 這是第3次握手 完成之後就可以進行讀寫操作

在這裡插入圖片描述

****accept 函式****//接收客戶端的連結//接收信箱 返回跟客戶端得sock
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockdf:

​	socket檔案描述符

addr:

​	傳出引數,返回連結客戶端地址資訊,含IP地址和埠號

addrlen:

​	傳入傳出引數(值-結果),傳入sizeof(addr)大小,函式返回時返回真正接收到地址結構體的大小

返回值:

​	成功返回一個新的socket檔案描述符,用於和客戶端通訊,失敗返回-1,設定errno

//伺服器呼叫accept()接受連線,如果伺服器呼叫accept()時還沒有客戶端的連線請求,就阻塞等待直到有客戶端連線上來
****connect函式****客戶端連結函式
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockdf:

​	socket檔案描述符

addr:

​	傳入引數,指定伺服器端地址資訊,含IP地址和埠號

addrlen:

​	傳入引數,傳入sizeof(addr)大小

返回值:

​	成功返回0,失敗返回-1,設定errno

客戶端需要呼叫connect()連線伺服器,connect和bind的引數形式一致,區別在於bind的引數是自己的地址,而connect的引數是對方的地址。connect()成功返回0,出錯返回-1。

*出錯處理函式*

我們知道,系統函式呼叫不能保證每次都成功,必須進行出錯處理,這樣一方面可以保證程式邏輯正常,另一方面可以迅速得到故障資訊。

出錯處理函式

#include <errno.h>

#include <string.h>

char *strerror(int errnum);//此函式可寫入到日誌

errnum:

//比如

perror_exit(const char * des){

  //fprintf(stderr, "%s error, reason: %s\n", des, strerror(errno));//如果出錯錯誤資訊就會在全域性的errno,stderr表示列印到標準出錯des表示哪個地方出錯就傳字串進去就可以列印出錯誤

  perror(des);//此函式只是列印到標誌出錯資訊,不寫入日誌

  exit(1);

}

相關文章