Linux c 網絡編程--中篇

1.字節順序與轉換函數

1.1字節順序

大端模式:高字節數據存放在低地址,低字節數據存放在高地址。
小端模式:低字節數據存放在低地址,高字節數據存放在高地址。

數據0x04030201存放如下
在這裏插入圖片描述
注意
1.對於char類型數據由於只佔一個字節,因此不存在大小端的問題
2.對於IP地址、端口等非char類型數據,必須在數據發送到網絡之前轉換爲大端模式;在接收端接收後轉爲相應的主機端模式

TCP/IP協議規定網絡上必須使用網絡字節順序即大端模式

1.2Linux系統提供的轉換函數

linux下 shell下輸入 man byteorder可獲得函數原型

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);//主機unsigned int轉換爲網絡順序   host to network long
uint16_t htons(uint16_t hostshort);//主機unsigned short轉換爲網絡順序
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

linux下 shell下輸入 man inet可獲得inet系統函數原型
因爲我們習慣於使用字符串形式的網絡地址"192.168.0.1",然而網絡上數據傳輸時使用的是二進制形式且大端模式的IP地址。因此有了inet系列。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
1.字符串IP(cp) ==>二進制網絡字節順序IP(inp);
成功返回非0值,參數無效返回0
int inet_aton(const char *cp, struct in_addr *inp);

2.類似於函數1,已過時,推薦使用1
in_addr_t inet_addr(const char *cp);

3.字符串IP ==> 主機字節順序形式的二進制IP地址;
成功返回轉換後結果,參數無效返回-1;
in_addr_t inet_network(const char *cp);

4.網絡字節順序二進制IP轉換爲"."分十進制的字符串IP
成功返回字符指針,參數無效返回NULL
char *inet_ntoa(struct in_addr in);

5.將主機字節順序的二進制網絡號+主機字節順序的二進制主機號組合爲網絡字節順序的二進制網絡地址
struct in_addr inet_makeaddr(int net, int host);

6.從參數中提取主機號
成功返回主機字節順序的主機號(二進制)
in_addr_t inet_lnaof(struct in_addr in);

7.從參數中提取網絡號
成功返回主機字節順序的網絡號(二進制)
in_addr_t inet_netof(struct in_addr in);
在這裏插入代碼片

2.獲取和設置套接字屬性

獲取或設置套接字的工作方式

2.1獲取套接字屬性

man getsocketopt

#include<sys/types.h>
#include<sys/socket.h>
int getsockopt(int s,int level,int optname,void *optval,socklen_t optlen);
參數:
s:套接字
level:進行套接字選項操作層次如下等
	SOL_SOCKET:通用套接字
	IPPROTO_IP:IP層套接字
	IPPROTO_TCP:TCP層套接字
	一般選SOL_SOCKET來進行與特定協議不相關的操作。
optname:套接字選項名稱
optval:用來存放獲得的套接字選項,調用函數前optlen是optval指向的空間大小,調用之後其值爲optval所保存的結果的實際大小。
返回值:成功0失敗-1 錯誤碼存入errno

2.2設置套接字屬性

man setsocketopt

#include<sys/types.h>
#include<sys/socket.h>
int setsockopt(int s,int level,int optname,const void *optval,socklen_t optlen);
參數:
s:套接字
level:進行套接字選項操作層次
optname:套接字選項名稱
optval:待設置的套接字選項的值,optlen是該選項的長度。
返回值:成功0失敗-1 錯誤碼存入errno

帶設置的套接字選項optval可選值

SO_KEEPALIVE
系統檢測該連接是否還有效
服務端使用此選項則某客戶端一段時間內沒有反應則關閉該連接。
服務端未使用此選擇則某客戶端非正常端口連接,服務器進程將一直被阻塞等待。

SO_RCVLOVAT和SO_SNDLOWAT
接收緩衝區的下限:只有接收緩衝區中數據超過下限纔會被送給上層應用程序
發送緩衝區的下限:只有發送緩衝區中數據超過下限纔會將數據發送出去。
注意:Linux下這兩個值均爲1且不能更改,即只要有數據就發送和接收;這兩個選項只能使用getsocketopt獲取而不能使用setsocketopt更改

SO_RCVTIMEO和SO_SNDTIMEO
設置對套接字讀或者寫的超時時間
struct timeval{
 long tv_sec;//秒數
 long tv_usec;//微秒數
}
超時時間爲二者和,在某套接字連接上,若讀寫超時,則認爲接收或發送數據失敗。

SO_BINDTODEVICE
將套接字綁定到特定的網絡接口,此後只有該網絡接口上的數據纔會被套接字處理。若選項設置爲空字符串或者選項長度設置爲0時則將取消綁定。

SO_DEBUG
該選項只能對TCP套接字使用,設置了該選項後系統將保存TCP發送和接收的所有數據的相關信息,以便調試程序。

SO_REUSEADDR
Linux系統中若socket綁定了一個端口,當該socket正常關閉或程序異常退出後,一段時間內,該端口依然維持原綁定狀態,其他程序無法綁定該端口,若設置此參數這不存在此問題。

SO_TYPE
用於獲取套接字的類型,該選項只能被函數getsocketopt用來獲取套接類型,而不能使用setsockopt改變套接字類型。

SO_ACCEPTCONN
該選項用於檢測套接字是否處於監聽狀態
0-非監聽狀態	;1- 正在監聽
該選項只能被getsockopt函數用來獲取監聽狀態信息。

SO_DONTROUTE
設置該選項表示在發送IP數據包時不使用路由表來尋找路由。

SO_BROADCAST
該選項表示套接字是否能夠在網絡上剛播數據
實際應用中要在網絡中廣播數據必須硬件支持且使用SOCK_DGRAM套接字,系統默認不支持廣播。

SO_SNDBUF和SORCVBUF
用於設置套接字的發送和接收緩衝區大小
對於TCP類型的套接字,緩衝區太小會影響TCP流量控制
對於UDP類型的套接字,若數據緩衝區滿則後續數據將被丟棄,實際應用中應選擇合適大小。

SO_SERROR
獲取套接字內部的錯誤變量so_error
當套接字上發生了異步錯誤時,系統將設置套接字的so_error。
異步錯誤是指錯誤的發生和錯誤被發現的時間不一致。通常在目的主機非正常關閉時發生這種錯誤。該選項只能被getsockopt函數用來獲取so_error
注意:調用完getsockeopt完成後,so_error的值將自動被重新初始化。

3.多路複用select()

多路複用:服務器端同時處理多個客戶端的連接請求。

實現方法一:採用非阻塞方式套接字,服務器端不斷查詢各個套接字的狀態,若有數據到達則讀出數據,沒有則查詢下一個套接字(這種方式浪費大量的CUP,效率低)

實現方法二:服務器不主動查詢套接字狀態,而是向系統登記希望監聽的套接字,然後阻塞。當套接字上有事件發生時則系統通知服務器進程對應套接字有什麼事件,由服務器進程查詢對應套接字並進行處理。

使用select()函數可以實現方式2的多路複用。
man select()

#include<sys/select.h>
#include<sys/time.h>
#include<sys/types.h>
#include<unistd.h>
int select(int n,fd_set *readfds,fd_set* writefds,fd_set* execptfds,struct timeval* timeout);
參數:
n:需要監視的文件描述符數,要監視的文件描述符數值爲0~n-1
readfds:需要監視的可讀文件描述符集合,當這個集合的一個描述符有數據到達時,系統將通知select函數的程序。
writefds:需要監視的可寫文件描述符集合,當集合中某個描述符可發送數據時,程序將收到通知。
execptfds:需要監視的異常文件描述符,當集合中某描述符發送異常時,程序將收到通知。
timeout:阻塞時間,若這段時間內監視的文件描述符都沒有事件發生,則select函數返回0;若timeout設置爲NULL則select函數將一直阻塞,直到某個文件描述符發生事件。若timeout設置爲0,則select函數非阻塞模式,函數select查詢完文件描述符集合狀態後立即返回。

注意:這裏的文件描述符既可以是普通文件描述符也可以是套接字描述符。

struct timeval{
	long tv_sec;
	long tv_usec;
}

系統爲文件描述符集合提供了一系列的宏以便操作
FD_CLR(int fd,fd_set* set);//集合中刪除fd文件描述符
FD_ISSET(int fd,fd_set* set)//集合中是否存在fd文件描述符
FD_SET(int fd,fd_set* set);//集合中增加fd文件描述符
FD_ZERO(fd_set* set);//集合中文件描述符清空select()設定的要監控的文件描述符集合中有描述符發生了事件,則select返回發生事件的文件描述符的個數

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章