亞寵展、全球?qū)櫸锂a(chǎn)業(yè)風(fēng)向標(biāo)——亞洲寵物展覽會(huì)深度解析
1652
2022-05-30
第一章 TCP網(wǎng)絡(luò)編程
1.1 socket創(chuàng)建套接字
#include
#include
int socket(int domain, int type, int protocol);
功能
創(chuàng)建網(wǎng)絡(luò)套接字,用于網(wǎng)絡(luò)通信使用,類似于文件操作的open函數(shù)。該函數(shù)在服務(wù)器和客戶端都會(huì)用到。
參數(shù)
int domain :網(wǎng)絡(luò)協(xié)議版本指定。
AF_INET??????? IPv4 Internet protocols
AF_INET6?????? IPv6 Internet protocols
int type:指定通信協(xié)議類型。
SOCK_STREAM 表明我們用的是TCP協(xié)議 (字節(jié)流)
SOCK_DGRAM 表明我們用的是UDP協(xié)議 (數(shù)據(jù)報(bào))
int protocol:指定通信協(xié)議類型。Type參數(shù)已經(jīng)指定了協(xié)議,該參數(shù)直接填0即可!
返回值
成功返回網(wǎng)絡(luò)套接字,與open函數(shù)返回值類似。
示例
Clientfd = socket(PF_INET,SOCK_STREAM,0);
1.2 bind綁定IP-端口
#include
#include
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能
創(chuàng)建服務(wù)器。該函數(shù)在服務(wù)器端使用。
參數(shù)
int sockfd : 網(wǎng)絡(luò)套接字
const struct sockaddr *addr? :填充創(chuàng)建服務(wù)器所需的地址信息,詳細(xì)的成員看1.3章節(jié)。
socklen_t addrlen :地址長(zhǎng)度,就是該結(jié)構(gòu)體的大小。使用sizeof函數(shù)進(jìn)行計(jì)算。
返回值
0表示成功,-1表示失敗!
1.3 struct sockaddr地址結(jié)構(gòu)體
1.3.1 結(jié)構(gòu)體成員解析
在實(shí)際填充參數(shù)的過(guò)程中,struct sockaddr結(jié)構(gòu)體被struct sockaddr_in結(jié)構(gòu)體代替。struct sockaddr_in結(jié)構(gòu)體比struct sockaddr可讀性強(qiáng)一些,填充參數(shù)比較好理解。
struct sockaddr_in和struct sockaddr大小相同。在填充結(jié)構(gòu)體的時(shí)候?yàn)榱朔奖闾畛鋮?shù),使用struct sockaddr_in結(jié)構(gòu)體,給函數(shù)賦值的時(shí)候需要強(qiáng)制轉(zhuǎn)換為struct sockaddr類型的結(jié)構(gòu)體。因?yàn)榈讓雍瘮?shù)最終還是使用struct sockaddr類型的結(jié)構(gòu)體。
struct sockaddr結(jié)構(gòu)體成員:
struct sockaddr {undefined
sa_family_t sa_family;????? //網(wǎng)絡(luò)協(xié)議版本。填寫:AF_INET 或者 AF_INET6。
char??????? sa_data[14];?? //IP地址和端口
}
struct sockaddr_in結(jié)構(gòu)體成員:
查看IPV4協(xié)議幫助文檔:# man? 7? ip
struct sockaddr_in {undefined
sa_family_t??? sin_family;? /* address family: AF_INET?? 協(xié)議類型*/
in_port_t????? sin_port;?? /* port in network byte order?? 端口號(hào)*/
struct in_addr sin_addr;??? /* internet address?? 存放IP地址的結(jié)構(gòu)體*/
};
/* Internet address. */
struct in_addr {undefined
uint32_t?????? s_addr;???? /* address in network byte order IP地址 */
};
1.3.2 端口號(hào)賦值
計(jì)算機(jī)數(shù)據(jù)存儲(chǔ)有兩種字節(jié)優(yōu)先順序: 高位字節(jié)優(yōu)先和低位字節(jié)優(yōu)先。 Internet 上數(shù)據(jù)以高位字節(jié)優(yōu)先順序在網(wǎng)絡(luò)上傳輸, 所以對(duì)于在內(nèi)部是以低位字節(jié)優(yōu)先方式存儲(chǔ)數(shù)據(jù)的機(jī)器, 在 Internet 上傳輸數(shù)據(jù)時(shí)就需要進(jìn)行轉(zhuǎn)換, 否則就會(huì)出現(xiàn)數(shù)據(jù)不一致。
普通人用的桌面電腦,只要是Intel或AMD的x86/x64架構(gòu)就一定是小端字節(jié)序。
外很多ARM CPU可以選擇數(shù)據(jù)指令字節(jié)序,不過(guò)通常也都是運(yùn)行小端字節(jié)序(比如我們的智能手機(jī))。
網(wǎng)絡(luò)設(shè)備,像PowerPC核心的一些路由器,默認(rèn)運(yùn)行大端字節(jié)序。
下面是幾個(gè)字節(jié)順序轉(zhuǎn)換函數(shù):
·htonl(): 把 32 位值從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
·htons(): 把 16 位值從主機(jī)字節(jié)序轉(zhuǎn)換成網(wǎng)絡(luò)字節(jié)序
·ntohl(): 把 32 位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序
·ntohs(): 把 16 位值從網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)換成主機(jī)字節(jié)序
函數(shù)原型
#include
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
網(wǎng)際協(xié)議在處理這些多字節(jié)整數(shù)時(shí),使用大端字節(jié)序。
在主機(jī)本身就使用大端字節(jié)序時(shí),這些函數(shù)通常被定義為空宏。
給struct sockaddr_in結(jié)構(gòu)體的端口成員賦值的時(shí)候就需要用到以上大端轉(zhuǎn)小端函數(shù)進(jìn)行轉(zhuǎn)換!
示例:
/*結(jié)構(gòu)體成員賦值*/
tcp_server.sin_family=AF_INET; //IPV4協(xié)議類型
tcp_server.sin_port=htons(tcp_server_port);//端口號(hào)賦值,將本地字節(jié)序轉(zhuǎn)為網(wǎng)絡(luò)字節(jié)序
tcp_server.sin_addr.s_addr=INADDR_ANY; //將本地IP地址賦值給結(jié)構(gòu)體成員
//inet_addr("192.168.18.3"); //IP地址賦值
1.3.3 IP地址賦值
struct sockaddr_in結(jié)構(gòu)體存放IP地址的成員是struct in_addr 結(jié)構(gòu)體類型,底層存放地址的成員是一個(gè)無(wú)符號(hào)int類型,而我們生活中的IP地址是使用xxx.xxx.xxx.xxx 這種格式表示的。比如:192.168.1.1。 在賦值的時(shí)候就需要進(jìn)行將”192.168.1.1”這種格式轉(zhuǎn)為無(wú)符號(hào)int類型才能進(jìn)行賦值。
以下是幾個(gè)IP格式轉(zhuǎn)換函數(shù):
將字符串類型IP轉(zhuǎn)為in_addr_t類型(unsigned int)返回。
in_addr_t inet_addr(const char *cp);
示例:
Serveraddr.sin_addr.s_addr = inet_addr("192.168.18.3");
使用字符串類型的IP直接給結(jié)構(gòu)體成員賦值
int inet_aton(const char *cp, struct in_addr *inp);
示例:
inet_aton(“192.168.18.3”,&Clientaddr.sin_addr);
將結(jié)構(gòu)體里的IP地址成員轉(zhuǎn)為字符串類型返回
char *inet_ntoa(struct in_addr in);
該函數(shù)與上面兩個(gè)函數(shù)功能剛好相反。是將整型的IP轉(zhuǎn)為字符串類型!
1.3.4 本地計(jì)算機(jī)大小端判斷
首先說(shuō)明,電腦大小端指的是一種存儲(chǔ)模式。
為什么有大小端:
在計(jì)算機(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個(gè)地址單元都對(duì)應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為 8bit。但是在C語(yǔ)言中除了8bit的char之外,還有16bit的short型,32bit的long型(要看具體的編譯器),另外,對(duì)于位數(shù)大于 8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如何將多個(gè)字節(jié)安排的問(wèn)題,因此就導(dǎo)致了大端存儲(chǔ)模式和小端存儲(chǔ)模式。
大小端定義:
大端模式(Big-endian),是指數(shù)據(jù)的高字節(jié),保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié),保存在內(nèi)存的高地址中。
小端模式(Little-endian),是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的低地址中。
直接來(lái)看一個(gè)圖,詳細(xì)說(shuō)明大小端:
例子:int i = 0x12345678 兩種模式存入內(nèi)存:
4. 判斷大小端的C語(yǔ)言代碼
#include
int CheckSystem()
{
union check
{
int i;
char ch;
}c;
c.i=1;
return (c.ch==1);
}
int main()
{
int check=CheckSystem();
if(check==1)
printf("當(dāng)前系統(tǒng)為小端\n");
else
printf("當(dāng)前系統(tǒng)為大端\n");
return 0;
}
///
// 公用的四個(gè)字節(jié)地址 :0x1001 -> 0x1002 -> 0x1003 -> 0x1004
//?? 小端來(lái)說(shuō) ?賦值 1 : 0x01????? 0x00????? 0x00????? 0x00
//?? 大端來(lái)說(shuō)? 賦值 1 : 0x00????? 0x00????? 0x00????? 0x01
//也就是說(shuō)存數(shù)據(jù)都是從低地址存放 一個(gè)char字節(jié),
//他和int開始的地址是一樣的 讀的話 還是從低字節(jié)向高字節(jié)完整的讀取
1.4 listen監(jiān)聽端口的數(shù)量
#include
#include
int listen(int sockfd, int backlog);
功能
設(shè)置服務(wù)器需要監(jiān)聽的端口數(shù)量。決定了能夠同時(shí)響應(yīng)連接的服務(wù)器數(shù)量。
返回值
成功返回0,失敗返回-1。
服務(wù)器創(chuàng)建,函數(shù)調(diào)用順序:
示例:listen(Serverfd,10)
1.5 accept 等待客戶端連接
#include
#include
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能
以阻塞的形式等待客戶端連接。
參數(shù)
struct sockaddr *addr? :存放已經(jīng)連接的客戶端信息。傳入一個(gè)結(jié)構(gòu)體地址。
socklen_t *addrlen??? :表示客戶端的結(jié)構(gòu)體大小。該大小需要我們指定,客戶端連接成功然后再判斷是否與填寫的大小一致。
返回值
成功將返回客戶端的網(wǎng)絡(luò)套接字。錯(cuò)誤返回-1。
示例:
struct sockaddr_in Clientaddr;
len = sizeof(struct sockaddr);
Clientfd = accept(Serverfd,(struct sockaddr *)&Clientaddr,&len);
1.6 connect連接服務(wù)器
#include
#include
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能
連接到指定服務(wù)器。該函數(shù)在客戶端使用。
參數(shù)
int sockfd :socket函數(shù)的網(wǎng)絡(luò)套接字。
const struct sockaddr *addr :服務(wù)器的IP地址信息。 參考:1.2節(jié)和1.3.節(jié)
socklen_t addrlen :結(jié)構(gòu)體的大小。
返回值
成功返回0,錯(cuò)誤返回-1。
示例
connect(Clientfd,(struct sockaddr *)&Clientaddr,sizeof(struct sockaddr));
1.7 send/ recv網(wǎng)絡(luò)數(shù)據(jù)收發(fā)
#include
#include
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv (int sockfd, void *buf, size_t len, int flags);
功能
客戶端與服務(wù)器之間的數(shù)據(jù)收發(fā)。
參數(shù)
const void *buf 、void *buf :讀寫的緩沖區(qū)。
int flags :填0。
以上兩個(gè)函數(shù)可以使用write和read函數(shù)替換。
1.8 shutdown關(guān)閉連接
#include
int shutdown(int sockfd, int how);
返回
0—成功,-1—失敗。
參數(shù)how的值:
SHUT_RD:關(guān)閉連接的讀這一半,不再接收套接口中的數(shù)據(jù)且留在套接口緩沖區(qū)中的數(shù)據(jù)都作廢。進(jìn)程不能再對(duì)套接口任何讀函數(shù)。調(diào)用此函數(shù)后,由TCP套接口接收的任何數(shù)據(jù)都被確認(rèn),但數(shù)據(jù)本身被扔掉。
SHUT_WR:關(guān)閉連接的寫這一半,在TCP場(chǎng)合下,這稱為半關(guān)閉。當(dāng)前留在套接口發(fā)送緩沖區(qū)中的數(shù)據(jù)都被發(fā)送,后跟正常的TCP連接終止序列。此半關(guān)閉不管套接口描述字的訪問(wèn)計(jì)數(shù)是否大于0。進(jìn)程不能再執(zhí)行對(duì)套接口的任何寫函數(shù)。
SHUT_RDWR:連接的讀這一半和寫這一半都關(guān)閉。這等效于調(diào)用shutdown兩次:第一次調(diào)用時(shí)用SHUT_RD,第二次調(diào)用時(shí)用SHUT_WR。
shutdown(tcp_client_fd,SHUT_WR);? //TCP半關(guān)閉,保證緩沖區(qū)內(nèi)的數(shù)據(jù)全部寫完
直接強(qiáng)制關(guān)閉連接示例:
int close(int fd);
1.9 查看Linux系統(tǒng)當(dāng)前的網(wǎng)絡(luò)連接
在/proc/net/tcp目錄下面保存了當(dāng)前系統(tǒng)所有TCP鏈接的狀態(tài)信息。
查看示例:
[root@wbyq FileSend2]# cat /proc/net/tcp
sl? local_address rem_address?? st tx_queue rx_queue tr tm->when retrnsmt?? uid? timeout inode
0: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000???? 0??????? 0 13264 1 c16ac5c0 99 0 0 10 -1
1: 00000000:DA10 00000000:0000 0A 00000000:00000000 00:00000000 00000000??? 29??????? 0 13592 1 c16ac0c0 99 0 0 10 -1
2: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000???? 0??????? 0 14400 1 c16acac0 99 0 0 10 -1
3: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000???? 0??????? 0 13851 1 c142f080 99 0 0 10 -1
4: 0100007F:0019 00000000:0000 0A 00000000:00000000 00:00000000 00000000???? 0??????? 0 14753 1 c142fa80 99 0 0 10 -1
5: 813DA8C0:A019 49AAC3CB:0522 01 00000000:00000000 00:00000000 00000000? ???0??????? 0 123641 1 c142f580 20 3 18 10 -1
說(shuō)明:? 這里的IP地址信息和端口號(hào)都是使用十六進(jìn)制保存的。
813DA8C0:A019 49AAC3CB:0522
查看網(wǎng)絡(luò)狀態(tài)連接:
[root@wbyq FileSend2]# netstat -ntp
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q?? Local Address??????? ?Foreign Address?????? State??????????? PID/Program name
tcp??????? 0? 0???? 192.168.61.129:40985???? 203.195.170.73:1314 ???ESTABLISHED?? 20955/./app_c
從上面可得到的信息:
連接類型: TCP協(xié)議
本地IP地址和端口號(hào): 192.168.61.129:40985
與其通信的遠(yuǎn)程IP地址和端口號(hào): 203.195.170.73:1314
狀態(tài): ESTABLISHED(已建立的連接)
進(jìn)程PID號(hào)與應(yīng)用程序名稱: 20955/./app_c
socket網(wǎng)絡(luò)連接的狀態(tài)如下
1、LISTENING狀態(tài)
FTP服務(wù)啟動(dòng)后首先處于偵聽(LISTENING)狀態(tài)。
2、ESTABLISHED狀態(tài)
ESTABLISHED的意思是建立連接。表示兩臺(tái)機(jī)器正在通信。
3、CLOSE_WAIT
對(duì)方主動(dòng)關(guān)閉連接或者網(wǎng)絡(luò)異常導(dǎo)致連接中斷,這時(shí)我方的狀態(tài)會(huì)變成CLOSE_WAIT 此時(shí)我方要調(diào)用close()來(lái)使得連接正確關(guān)閉
4、TIME_WAIT
我方主動(dòng)調(diào)用close()斷開連接,收到對(duì)方確認(rèn)后狀態(tài)變?yōu)門IME_WAIT。TCP協(xié)議規(guī)定TIME_WAIT狀態(tài)會(huì)一直持續(xù)2MSL(即兩倍的分 段最大生存期),以此來(lái)確保舊的連接狀態(tài)不會(huì)對(duì)新連接產(chǎn)生影響。處于TIME_WAIT狀態(tài)的連接占用的資源不會(huì)被內(nèi)核釋放,所以作為服務(wù)器,在可能的情 況下,盡量不要主動(dòng)斷開連接,以減少TIME_WAIT狀態(tài)造成的資源浪費(fèi)。
目前有一種避免TIME_WAIT資源浪費(fèi)的方法,就是關(guān)閉socket的LINGER選項(xiàng)。但這種做法是TCP協(xié)議不推薦使用的,在某些情況下這個(gè)操作可能會(huì)帶來(lái)錯(cuò)誤。
5、SYN_SENT狀態(tài)
SYN_SENT狀態(tài)表示請(qǐng)求連接,當(dāng)你要訪問(wèn)其它的計(jì)算機(jī)的服務(wù)時(shí)首先要發(fā)個(gè)同步信號(hào)給該端口,此時(shí)狀態(tài)為SYN_SENT,如果連接成功了就變?yōu)?ESTABLISHED,此時(shí)SYN_SENT狀態(tài)非常短暫。但如果發(fā)現(xiàn)SYN_SENT非常多且在向不同的機(jī)器發(fā)出,那你的機(jī)器可能中了沖擊波或震蕩波 之類的病毒了。這類病毒為了感染別的計(jì)算機(jī),它就要掃描別的計(jì)算機(jī),在掃描的過(guò)程中對(duì)每個(gè)要掃描的計(jì)算機(jī)都要發(fā)出了同步請(qǐng)求,這也是出現(xiàn)許多 SYN_SENT的原因。
根據(jù)TCP協(xié)議定義的3次握手?jǐn)嚅_連接規(guī)定,發(fā)起socket主動(dòng)關(guān)閉的一方 socket將進(jìn)入TIME_WAIT狀態(tài),TIME_WAIT狀態(tài)將持續(xù)2個(gè)MSL(Max Segment Lifetime),在Windows下默認(rèn)為4分鐘,即240秒,TIME_WAIT狀態(tài)下的socket不能被回收使用. 具體現(xiàn)象是對(duì)于一個(gè)處理大量短連接的服務(wù)器,如果是由服務(wù)器主動(dòng)關(guān)閉客戶端的連接,將導(dǎo)致服務(wù)器端存在大量的處于TIME_WAIT狀態(tài)的socket, 甚至比處于Established狀態(tài)下的socket多的多,嚴(yán)重影響服務(wù)器的處理能力,甚至耗盡可用的socket,停止服務(wù). TIME_WAIT是TCP協(xié)議用以保證被重新分配的socket不會(huì)受到之前殘留的延遲重發(fā)報(bào)文影響的機(jī)制,是必要的邏輯保證.
第二章 UDP網(wǎng)絡(luò)編程
2.1 UDP協(xié)議創(chuàng)建流程
2.2 數(shù)據(jù)報(bào)收發(fā)函數(shù)
2.2.1 recvfrom函數(shù)
UDP使用recvfrom()函數(shù)接收數(shù)據(jù),他類似于標(biāo)準(zhǔn)的read(),但是在recvfrom()函數(shù)中要指明數(shù)據(jù)的目的地址。
#include
#include
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr * from, size_t *addrlen);
返回值
成功返回接收到數(shù)據(jù)的長(zhǎng)度,負(fù)數(shù)失敗
前三個(gè)參數(shù)等同于函數(shù)read()的前三個(gè)參數(shù),flags參數(shù)是傳輸控制標(biāo)志。最后兩個(gè)參數(shù)類似于accept的最后兩個(gè)參數(shù)(接收客戶端的IP地址)。
示例:
/*阻塞方式接收數(shù)據(jù)*/
int len=0;
char buff[1024];
size_t addrlen=sizeof(struct sockaddr);
while(1)
{undefined
len=recvfrom(socketfd,buff,1024,0,(struct sockaddr *)&ClientSocket,&addrlen);
buff[len]='\0';
printf("Rx: %s,len=%d\n",buff,len);
printf("數(shù)據(jù)發(fā)送方IP地址:%s\n",inet_ntoa(ClientSocket.sin_addr));
printf("數(shù)據(jù)發(fā)送方端口號(hào):%d\n",ntohs(ClientSocket.sin_port));
}
2.2.2 sendto函數(shù)
UDP使用sendto()函數(shù)發(fā)送數(shù)據(jù),他類似于標(biāo)準(zhǔn)的write(),但是在sendto()函數(shù)中要指明目的地址。
#include
#include
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr * to, int addrlen);
返回值
成功返回發(fā)送數(shù)據(jù)的長(zhǎng)度,失敗返回-1
前三個(gè)參數(shù)等同于函數(shù)read()的前三個(gè)參數(shù),flags參數(shù)是傳輸控制標(biāo)志。參數(shù)to指明數(shù)據(jù)將發(fā)往的協(xié)議地址,他的大小由addrlen參數(shù)來(lái)指定。
示例:
/*向UDP協(xié)議服務(wù)器發(fā)送數(shù)據(jù)*/
ServerSocket.sin_family=PF_INET; //協(xié)議
ServerSocket.sin_port=htons(PROT); //端口
ServerSocket.sin_addr.s_addr=inet_addr(argv[1]);? //表示服務(wù)器的IP地址
bzero(ServerSocket.sin_zero,8); //初始化空間
char buff[]="1234567890";
int len=0;
while(1)
{undefined
len=sendto(socketfd,buff,strlen(buff),0,(const struct sockaddr*)&ServerSocket,sizeof(struct sockaddr));
printf("Tx: %d\n",len);
sleep(1);
}
第三章 設(shè)置Socket套接字屬性
3.1 函數(shù)原型介紹
#include
#include
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
參數(shù)
sockfd:標(biāo)識(shí)一個(gè)套接口的描述字。
level:選項(xiàng)定義的層次;目前僅支持SOL_SOCKET和IPPROTO_TCP層次。
optname:需設(shè)置的選項(xiàng)。
optval:指針,指向存放選項(xiàng)值的緩沖區(qū)。
optlen:optval緩沖區(qū)的長(zhǎng)度。
3.2 屬性功能注釋
setsockopt()函數(shù)用于任意類型、任意狀態(tài)套接口的設(shè)置選項(xiàng)值。盡管在不同協(xié)議層上存在選項(xiàng),但本函數(shù)僅定義了最高的“套接口”層次上的選項(xiàng)。選項(xiàng)影響套接口的操作,諸如加急數(shù)據(jù)是否在普通數(shù)據(jù)流中接收,廣播數(shù)據(jù)是否可以從套接口發(fā)送等等。
setsockopt()支持的選項(xiàng)定義位置:/usr/include/asm-generic/socket.h
#ifndef __ASM_GENERIC_SOCKET_H
#define __ASM_GENERIC_SOCKET_H
#include
/* For setsockopt(2) */
#define SOL_SOCKET? 1
#define SO_DEBUG????? 1
#define SO_REUSEADDR??? 2
#define SO_TYPE????????? 3
#define SO_ERROR?????? 4
#define SO_DONTROUTE??? 5
#define SO_BROADCAST??? 6
#define SO_SNDBUF??? 7
#define SO_RCVBUF??? 8
#define SO_SNDBUFFORCE?????? 32
#define SO_RCVBUFFORCE?????? 33
#define SO_KEEPALIVE????? 9
#define SO_OOBINLINE?????? 10
#define SO_NO_CHECK?????? 11
#define SO_PRIORITY 12
#define SO_LINGER????? 13
#define SO_BSDCOMPAT?? 14
/* To add :#define SO_REUSEPORT 15 */
#ifndef SO_PASSCRED /* powerpc only differs in these */
#define SO_PASSCRED??????? 16
#define SO_PEERCRED??????? 17
#define SO_RCVLOWAT????? 18
#define SO_SNDLOWAT????? 19
#define SO_RCVTIMEO?????? 20
#define SO_SNDTIMEO?????? 21
#endif
/* Security levels - as per NRL IPv6 - don't actually do anything */
#define SO_SECURITY_AUTHENTICATION?????????? 22
#define SO_SECURITY_ENCRYPTION_TRANSPORT?? 23
#define SO_SECURITY_ENCRYPTION_NETWORK?????????????? 24
#define SO_BINDTODEVICE????? 25
/* Socket filtering */
#define SO_ATTACH_FILTER??? 26
#define SO_DETACH_FILTER??? 27
#define SO_PEERNAME?????????????? 28
#define SO_TIMESTAMP???????????? 29
#define SCM_TIMESTAMP???????? SO_TIMESTAMP
#define SO_ACCEPTCONN????????? 30
#define SO_PEERSEC?????????? 31
#define SO_PASSSEC?? ??????? 34
#define SO_TIMESTAMPNS??????????????? 35
#define SCM_TIMESTAMPNS??????????? SO_TIMESTAMPNS
#define SO_MARK???????????????????????? 36
#define SO_TIMESTAMPING????????????? 37
#define SCM_TIMESTAMPING? SO_TIMESTAMPING
#define SO_PROTOCOL??????????????? 38
#define SO_DOMAIN??????????? 39
#define SO_RXQ_OVFL??????????????? 40
#endif /* __ASM_GENERIC_SOCKET_H */
setsockopt()支持下列選項(xiàng)。其中“類型”表明optval所指數(shù)據(jù)的類型。
選項(xiàng)
類型
意義
SO_BROADCAST
BOOL
允許套接口傳送廣播信息。
SO_DEBUG
BOOL
記錄調(diào)試信息。
SO_DONTLINER
BOOL
不要因?yàn)閿?shù)據(jù)未發(fā)送就阻塞關(guān)閉操作。設(shè)置本選項(xiàng)相當(dāng)于將SO_LINGER的l_onoff元素置為零。
SO_DONTROUTE
BOOL
禁止選徑;直接傳送。
SO_KEEPALIVE
BOOL
發(fā)送“保持活動(dòng)”包。
SO_LINGER
struct linger FAR*
如關(guān)閉時(shí)有未發(fā)送數(shù)據(jù),則逗留。
SO_OOBINLINE
BOOL
在常規(guī)數(shù)據(jù)流中接收帶外數(shù)據(jù)。
SO_RCVBUF
int
為接收確定緩沖區(qū)大小。
SO_REUSEADDR
BOOL
允許套接口和一個(gè)已在使用中的地址捆綁(參見bind())。
SO_SNDBUF
int
指定發(fā)送緩沖區(qū)大小。
TCP_NODELAY BOOL
禁止發(fā)送合并的Nagle算法。
3.3 設(shè)置socket具有廣播特性
發(fā)送UDP數(shù)據(jù)報(bào)的時(shí)候,設(shè)置socket具有廣播特性:(默認(rèn)情況下socket不支持廣播特性)
const int opt = 1;
//設(shè)置該套接字為廣播類型,
int nb = 0;
nb = setsockopt(client_fd, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
if(nb == -1)
{undefined
printf("設(shè)置廣播類型錯(cuò)誤.\n");
}
3.4 設(shè)置socket發(fā)送和接收的緩沖區(qū)大小。
系統(tǒng)默認(rèn)的狀態(tài)發(fā)送和接收一次為8688字節(jié)(約為8.5K);在實(shí)際的過(guò)程中發(fā)送數(shù)據(jù)和接收數(shù)據(jù)量比較大,可以設(shè)置socket緩沖區(qū)。
// 接收緩沖區(qū)
int nRecvBuf=20*1024;//設(shè)置為20K
setsockopt(socketfd,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//發(fā)送緩沖區(qū)
int nSendBuf=20*1024;//設(shè)置為20K
setsockopt(socketfd,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));
3.5 設(shè)置收發(fā)時(shí)限
在發(fā)送和接收過(guò)程中有時(shí)由于網(wǎng)絡(luò)狀況等原因,發(fā)收不能預(yù)期進(jìn)行,而設(shè)置收發(fā)時(shí)限:
int nNetTimeout=1000;? //1秒
//發(fā)送時(shí)限
setsockopt(socketfd,SOL_SOCKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收時(shí)限
setsockopt(socketfd,SOL_SOCKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));
3.6 允許套接字綁定已使用的端口
有時(shí)候?qū)⒎?wù)器關(guān)閉之后,端口的釋放需要時(shí)間,可以設(shè)置該數(shù)據(jù)允許套接字綁定正在被占用的端口。
int on = 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
3.7 忽略SIGPIPE信號(hào)
往一個(gè)已經(jīng)接收到FIN的套接中寫是允許的,接收到的FIN僅僅代表對(duì)方不再發(fā)送數(shù)據(jù)。并不能代表我不能發(fā)送數(shù)據(jù)給對(duì)方。
往一個(gè)FIN結(jié)束的進(jìn)程中寫(write),對(duì)方會(huì)發(fā)送一個(gè)RST字段過(guò)來(lái),TCP重置。
如果再調(diào)用write就會(huì)產(chǎn)生SIGPIPE信號(hào)。
(也就是當(dāng)服務(wù)器向客戶端發(fā)送數(shù)據(jù)時(shí),客戶端突然斷開連接,會(huì)導(dǎo)致SIGPIPE信號(hào)產(chǎn)生,如果不處理,系統(tǒng)默認(rèn)的處理方式就終止進(jìn)程)
signal(SIGPIPE,SIG_IGN); ?//忽略SIGPIPE信號(hào)
3.8 獲取網(wǎng)絡(luò)底層緩沖區(qū)發(fā)送剩余字節(jié)數(shù)
在網(wǎng)絡(luò)編程時(shí),發(fā)送方調(diào)用write(fd)將報(bào)文發(fā)送的時(shí)候?qū)嶋H上只是寫入了內(nèi)核的write buffer。接收方什么時(shí)候能收到報(bào)文是個(gè)未知數(shù)。
在某些需要同步狀態(tài)機(jī)的地方,發(fā)送方最好能夠確認(rèn)接收方收到報(bào)文后再進(jìn)行下一步動(dòng)作。
linux提供了ioctl(fd, SIOCOUTQ, &count)方法來(lái)查詢一個(gè)tcp socket的write buffer是否清空。發(fā)送方一般可以用這個(gè)方法來(lái)判斷對(duì)端是否收到報(bào)文。當(dāng)?shù)讓泳W(wǎng)卡將緩沖區(qū)的數(shù)據(jù)全部發(fā)送成功時(shí),獲取的count=0
#include?
#include?
int?value;
ioctl(client_fd,SIOCOUTQ,&value);
3.9 獲取當(dāng)前網(wǎng)絡(luò)協(xié)議底層發(fā)送與接收緩沖區(qū)大小
int?sockfd;
/*1.?創(chuàng)建socket套接字*/
sockfd=socket(AF_INET,SOCK_STREAM,0);
int?nRecvBuf;
socklen_t??len=4;
getsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&nRecvBuf,&len);
printf("接收緩沖區(qū)大小=%d\n",nRecvBuf);
//發(fā)送緩沖區(qū)
int?nSendBuf;
getsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&nSendBuf,&len);
printf("發(fā)送緩沖區(qū)大小=%d\n",nSendBuf);
Redhat6.3系統(tǒng)上輸出結(jié)果:
接收緩沖區(qū)大小=87380
發(fā)送緩沖區(qū)大小=16384
Linux 數(shù)據(jù)結(jié)構(gòu) 網(wǎng)絡(luò)
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。