一,單進(jìn)程處理服務(wù)器
1,基本概念
想要學(xué)習(xí)socket網(wǎng)絡(luò)編程的讀者一定要首先學(xué)好計(jì)算機(jī)網(wǎng)絡(luò)的理論知識(shí),包括
1)osi網(wǎng)絡(luò)七層模型與ip四層模型
2)套接字含義
3)局域網(wǎng)通信過程
4)廣域網(wǎng)通信過程
5)tcp,udp通信協(xié)議,在這兩個(gè)協(xié)議中的連接建立,數(shù)據(jù)封裝,傳輸過程,傳輸中可能遇到的問題的處理(差錯(cuò)控制,擁塞控制)
6)ip網(wǎng)絡(luò)層協(xié)議,以太網(wǎng)幀協(xié)議
7)數(shù)據(jù)的封裝
以上的知識(shí)點(diǎn)是個(gè)人覺得學(xué)習(xí)計(jì)算機(jī)網(wǎng)絡(luò)必須要理解透徹的一些知識(shí)點(diǎn),讀者可以自行學(xué)習(xí),這里不過多介紹,接下來要介紹的socket網(wǎng)絡(luò)編程便是基于以上知識(shí)點(diǎn)學(xué)習(xí)的。下文主要介紹socket編程。
socket網(wǎng)絡(luò)編程也是系統(tǒng)編程的一種,系統(tǒng)為我們提供了一系列的一些與網(wǎng)絡(luò)有關(guān)的接口,當(dāng)我們編寫一個(gè)網(wǎng)絡(luò)程序時(shí),需要用到網(wǎng)絡(luò)功能的時(shí)候只需要調(diào)用這些接口即可。理解透徹了前面所說的七個(gè)知識(shí)點(diǎn)有助于我們理解這些接口內(nèi)部具體的實(shí)現(xiàn)原理,方便我們?cè)诰W(wǎng)絡(luò)編程的時(shí)候遇到錯(cuò)誤可以更快地調(diào)試出正確的運(yùn)行效果。接下來就介紹這些接口的使用。
2,socket編程
2.1 字節(jié)序轉(zhuǎn)換,IP地址轉(zhuǎn)換,套接字賦值
套接字對(duì)應(yīng)程序猿來說就是一套網(wǎng)絡(luò)通信的接口,使用這套接口就可以完成網(wǎng)絡(luò)通信。網(wǎng)絡(luò)通信的主體主要分為兩部分:客戶端和服務(wù)器端。在客戶端和服務(wù)器通信的時(shí)候需要頻繁提到三個(gè)概念:IP、端口、通信數(shù)據(jù),下面先介紹一下需要注意的一些細(xì)節(jié)問題。
2.1.1 字節(jié)序
字節(jié)序即我們計(jì)算機(jī)組成原理曾經(jīng)學(xué)過的一個(gè)數(shù)據(jù)是采用大端存儲(chǔ)還是小端存儲(chǔ)的問題,下面也可以簡單復(fù)習(xí)一遍:大端模式——數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中;比如說一個(gè)以字節(jié)為單位進(jìn)行尋址的計(jì)算機(jī),要存儲(chǔ)一個(gè)十六進(jìn)制的數(shù)“0x1234”這個(gè)數(shù)字的話,每一個(gè)數(shù)據(jù)位占據(jù)4個(gè)存儲(chǔ)位,那么沒兩個(gè)數(shù)據(jù)位就占8個(gè)存儲(chǔ)位即一個(gè)字節(jié),具體存儲(chǔ)在硬盤中就是:數(shù)據(jù)的高字節(jié)“12”存儲(chǔ)在內(nèi)存的低地址中,數(shù)據(jù)的低字節(jié)“34”存儲(chǔ)在內(nèi)存的高地址中。也就是說我們要從內(nèi)存的低地址處開始取數(shù)據(jù)的話,依次取出來的是一個(gè)數(shù)據(jù)的從高到低的數(shù)據(jù)位。而小端存儲(chǔ)的話:數(shù)據(jù)的高數(shù)據(jù)字節(jié)存儲(chǔ)在高地址位;數(shù)據(jù)的低數(shù)據(jù)字節(jié)存儲(chǔ)在低地址位。
那么問題就來了,對(duì)于要通信的多個(gè)主機(jī)來說,他們內(nèi)部選擇的數(shù)據(jù)存儲(chǔ)方式是不一樣的,這也就導(dǎo)致他們讀取一串?dāng)?shù)據(jù)的讀取方式也是不一樣的,甚至在網(wǎng)絡(luò)中的數(shù)據(jù)存儲(chǔ)方式也是不一樣的,(網(wǎng)絡(luò)字節(jié)序一搬是大端)。因此在數(shù)據(jù)傳輸?shù)臅r(shí)候必須要對(duì)數(shù)據(jù)進(jìn)行處理,以保證接收數(shù)據(jù)的一方使用它自己的主機(jī)序接收數(shù)據(jù)時(shí)得到正確的數(shù)據(jù)。具體的處理方法為下面四個(gè)函數(shù)。
#include <arpa/inet.h>
// u:unsigned
// 16: 16位, 32:32位
// h: host, 主機(jī)字節(jié)序
// n: net, 網(wǎng)絡(luò)字節(jié)序
// s: short
// l: int
// 這套api主要用于 網(wǎng)絡(luò)通信過程中 IP 和 端口 的轉(zhuǎn)換
// 將一個(gè)短整形從主機(jī)字節(jié)序 -> 網(wǎng)絡(luò)字節(jié)序
uint16_t htons(uint16_t hostshort);
// 將一個(gè)整形從主機(jī)字節(jié)序 -> 網(wǎng)絡(luò)字節(jié)序
uint32_t htonl(uint32_t hostlong);
// 將一個(gè)短整形從網(wǎng)絡(luò)字節(jié)序 -> 主機(jī)字節(jié)序
uint16_t ntohs(uint16_t netshort)
// 將一個(gè)整形從網(wǎng)絡(luò)字節(jié)序 -> 主機(jī)字節(jié)序
uint32_t ntohl(uint32_t netlong);
這四個(gè)函數(shù)一般用來轉(zhuǎn)換ip地址(32位)與端口(16位),例如主機(jī)A某一個(gè)ip經(jīng)過 htonl 就可以將A自己的主機(jī)端序轉(zhuǎn)化為網(wǎng)絡(luò)端序,而另一個(gè)主機(jī)接收到這個(gè)數(shù)據(jù)只需要 ntohl 就可以將網(wǎng)絡(luò)端序轉(zhuǎn)換為B主機(jī)自己的端序,讓其正確讀取。
注意:一般char型的數(shù)據(jù)不需要轉(zhuǎn)換,因?yàn)楸緛硪粋€(gè)char型變量就會(huì)存儲(chǔ)一個(gè)字節(jié),不論大端小端,他們的數(shù)據(jù)存儲(chǔ)效果都是一樣的。
2.1.2 IP地址轉(zhuǎn)換
為了方便我們?nèi)庋圩x取ip地址,我們一般會(huì)把一個(gè)32位的ip地址表示成點(diǎn)分十進(jìn)制的形式,這種形式是方便人類理解了,但是計(jì)算機(jī)并不理解。因此,如果要在編程中使用點(diǎn)分十進(jìn)制的形式為一個(gè)ip進(jìn)行初始化的話,一定要記得將這個(gè)點(diǎn)分十進(jìn)制的IP地址轉(zhuǎn)換成一個(gè)整型數(shù)(網(wǎng)絡(luò)字節(jié)序,下面稱呼為大端序),轉(zhuǎn)換的方法如下:
// 主機(jī)字節(jié)序的IP地址轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序
// 主機(jī)字節(jié)序的IP地址是字符串, 網(wǎng)絡(luò)字節(jié)序IP地址是整形
int inet_pton(int af, const char *src, void *dst);
參數(shù):
af:地址族(IP地址的家族包括ipv4和ipv6)協(xié)議
AF_INET: ipv4格式的ip地址
AF_INET6: ipv6格式的ip地址
src:傳入?yún)?shù),對(duì)應(yīng)要轉(zhuǎn)換的點(diǎn)分十進(jìn)制的ip地址:192.168.1.100
dst:傳出參數(shù),函數(shù)調(diào)用完成,轉(zhuǎn)換得到的大端整形IP被寫入到這塊內(nèi)存中
O返回值:成功返回1,失敗返回0或者- 1
另外,我們當(dāng)然也可以將一個(gè)整型數(shù)轉(zhuǎn)換為一個(gè)點(diǎn)分十進(jìn)制的字符串
#include <arpa/inet.h>
// 將大端的整形數(shù), 轉(zhuǎn)換為小端的點(diǎn)分十進(jìn)制的IP地址
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
注:還有一組函數(shù)也能進(jìn)程I地址大小端的轉(zhuǎn)換,但是只能處理ipv4的ip地址:文章來源:http://www.zghlxwxcb.cn/news/detail-616114.html
// 點(diǎn)分十進(jìn)制IP -> 大端整形
in_addr_t inet_addr (const char *cp);
// 大端整形 -> 點(diǎn)分十進(jìn)制IP
char* inet_ntoa(struct in_addr in);
2.1.3, 結(jié)構(gòu)體:sockaddr,sockaddr_in
1,sockaddr結(jié)構(gòu)體
系統(tǒng)內(nèi)核通過讀取該結(jié)構(gòu)體當(dāng)中的內(nèi)容獲取我們要寫的套接字的ip地址與端口,比如bind函數(shù)就需要輸入一個(gè)sockaddr的參數(shù)來指明其要綁定的ip地址與端口,這個(gè)結(jié)構(gòu)體成員如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-616114.html
typedef unsigned short int sa_family_t;
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
struct sockaddr {
sa_family_t sa_family; // 地址族協(xié)議, ipv4
到了這里,關(guān)于網(wǎng)絡(luò)編程(一)TCP單進(jìn)程服務(wù)器編程詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!