libev這一類的。開(kāi)始首先是萬(wàn)物根源的協(xié)議信息
TCP/IP 協(xié)議了
TCP/IP 協(xié)議指的并不是一個(gè)協(xié)議,往往在生活中聽(tīng)見(jiàn)的術(shù)語(yǔ)如:IP地址, TCP連接 等,總會(huì)被誤導(dǎo),以為就是一個(gè)東西TCP/IP說(shuō)的是一個(gè) 協(xié)議族 ,也就是說(shuō)是一堆協(xié)議的統(tǒng)稱| OSI | TCP/IP |
|---|---|
| 應(yīng)用層 表示層 會(huì)話層 | 應(yīng)用層 |
| 傳輸層 | 傳輸層 |
| 網(wǎng)絡(luò)層 | 網(wǎng)絡(luò)層 |
| 鏈路層 物理層 | 網(wǎng)絡(luò)接口層 |
IP地址 就是由它進(jìn)行封裝并傳往下一層*nix: 在 Terminal 中輸入 ifconfig -aWindows: 在 PowerShell 中輸入 ipconfig
*nix 和 Windows 都可以通過(guò) ping <domain name> 命令進(jìn)行查詢一個(gè)完整的應(yīng)用程序傳輸數(shù)據(jù)時(shí)候 封裝 的過(guò)程(從右二向左依次封裝):
| 以太網(wǎng)首部 | IP | TCP/UDP | 真實(shí)數(shù)據(jù) | 尾部 |
|---|---|---|---|---|
| MAC地址 | IP地址 | TCP或者UDP協(xié)議 | 應(yīng)用程序數(shù)據(jù) | 效驗(yàn)碼 |
| 源和目的MAC地址以及 | 及前層協(xié)議類型 | 源和目的端口號(hào)及前層應(yīng)用程序首部信息 | 應(yīng)用軟件信息和真正的數(shù)據(jù) |
其中端口號(hào)實(shí)際上就是 應(yīng)用程序的信息
接收數(shù)據(jù)時(shí)的 拆解 順序與 封裝 正好相反。
其中在傳輸過(guò)程中,作為接收方最開(kāi)始使用的是 網(wǎng)絡(luò)接口層/數(shù)據(jù)鏈路層 的驅(qū)動(dòng)程序(即操作系統(tǒng)自帶或另行安裝,總之不用使用的程序員寫就對(duì)了),來(lái)判斷這個(gè)包是否屬于我,判斷的依據(jù)就是 MAC地址,如果是再判斷什么協(xié)議
48bit 的, 前24bit由 IEEE 分配, 后24bit 由廠商分配。原則上是唯一的。MAC地址 和 IP地址
也許,許多大學(xué)計(jì)算機(jī)基礎(chǔ)課程,會(huì)講到 IP地址 有種類,分為 A,B,C...類,老師還介紹了各種類型的地址范圍。
但是在現(xiàn)代,這種分類早已經(jīng)失效,或者說(shuō)正在逐漸消失,因?yàn)楫?dāng)下的 IP 地址的 子網(wǎng)掩碼 可以是任意位,并以反斜杠跟在 IP地址后方。
比較現(xiàn)代的 IP地址 表示形式一般如此 1.185.223.1/24 代表著子網(wǎng)掩碼是由 24個(gè) 從左至右連續(xù)的的二進(jìn)制1 組合而成,其余位為0。稱為CIDR分類
事實(shí)上有一些實(shí)用且挺炫酷的函數(shù),可以先提一下
gethostbyname 用于域名查找 IP信息及各類信息
struct hostent * gethostbyname(const char * hostname)struct hostent 是存儲(chǔ)查找到的各類型信息,后方會(huì)有介紹hostname 即要查詢的域名gethostbyaddr 用于IP地址查找 域名及各類信息
struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family)
addr 是要查詢的 IP地址,之所以是 const char * 是因?yàn)镃語(yǔ)言歷史遺留的原因,實(shí)際上其類型應(yīng)為 struct in_addr *(IPv4)len 地址的長(zhǎng)度,即 IPv4 為4, IPv6 為16family 即協(xié)議的種類, IPv4 為 AF_INET, IPv6 為 AF_INET6| struct hostent 的成員 | . | 類型 | . | 解釋 |
|---|---|---|---|---|
| h_name | char * | 官方名稱 | ||
| h_aliases | char ** | 域名集合,以NULL結(jié)尾 | ||
| h_addrtype | int | 地址族的類型 AF_INET 或 AF_INET6 | ||
| h_length | int | 地址的長(zhǎng)度 4 或 16 | ||
| h_addr_list | char ** | IP的集合,以NULL結(jié)尾, 實(shí)際上每個(gè)元素的類型為 struct in_addr* |
實(shí)際上,這并不是一個(gè)好的方法,在后方將記錄 現(xiàn)代人的我們 該如何做到這些事情,以上只是以前的TCP/IP 編程
只適用于 IPv4
選擇使用 C 語(yǔ)言進(jìn)行編程
TCP 和 UDPPOSIX 標(biāo)準(zhǔn)->*nix平臺(tái)標(biāo)準(zhǔn) 和 Windows 平臺(tái)標(biāo)準(zhǔn)
對(duì)比兩種不同連接方式的不同地位的創(chuàng)建,使用
| TCP服務(wù)器 | TCP客戶端 | UDP服務(wù)器 | UDP客戶端 | 注釋 |
|---|---|---|---|---|
| socket() | socket() | socket() | socket() | 創(chuàng)建套接字 |
| bind() | bind() | bind() | 綁定所分配IP地址和端口號(hào) | |
| listen() | connect() | 客戶端則綁定IP地址和端口號(hào),并等待連接;服務(wù)器則是等待連接 | ||
| accept() | 服務(wù)器接受連接 | |||
| ... | ... | sendto/recvfrom() | sendto/recvfrom() | 對(duì)于UDP即是連接也是操作 |
| close() | close() | close() | close | 雙向直接關(guān)閉連接 |
| shutdown() | shutdown() | shutdown() | shutdown() | 可選擇方向的關(guān)閉連接,即更加靈活 |
如此對(duì)比雖然有一些小瑕疵,但是能夠大體上反映出真?zhèn)€網(wǎng)絡(luò)編程上不同方式的區(qū)別
注1: 對(duì)于 sendto recvfrom 這兩個(gè)接口函數(shù),并不一定是只能用在 UDP類型的 套接字上,同樣 TCP類型的 套接字也能使用,但是這么做并沒(méi)有什么意義。
注2: 實(shí)際上 UDP 沒(méi)有所謂的 服務(wù)器和和護(hù)短,因?yàn)楸緛?lái)就是單純的互相發(fā)來(lái)發(fā)去。客戶端端口 一般是隨機(jī)的
以上是 *nix平臺(tái)下的標(biāo)準(zhǔn), Windows下的操作方式和 API有細(xì)微不同,但大部分是一致的。
| Windows | *nix |
|---|---|
| socket() | socket() |
| bind() | bind() |
| connect() | connect() |
| listen() | listen() |
| accept() | accept() |
| closesocket() | close() |
| send() | send() |
| read() | read() |
| sendto() | sendto() |
| recvfrom() | recvfrom() |
不僅僅是接口名字相同,參數(shù)個(gè)數(shù)以及功能也是一致,即使有一個(gè)例外,其參數(shù)以及使用方法也相同。
那豈不是可以直接移植了?
并不!
在 Windows 套接字編程時(shí) , 由于 Windows 將其實(shí)現(xiàn)為動(dòng)態(tài)庫(kù),所以在使用時(shí)需要將其加載進(jìn)程序。
故而多加了加載操作。
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData /* 這是一個(gè)結(jié)構(gòu)體, 傳入類型為WSADATA* */
);
int WSACleanup(void);
每當(dāng)在 Windows 上進(jìn)行套接字編程時(shí),總要指定某個(gè)版本的套接字庫(kù):
WSADATA wsaData;
int err_code;
/*
* MAKEWORD()的作用在于將版本號(hào)轉(zhuǎn)為指定格式傳入
* 當(dāng)下(2015-10)套接字庫(kù)的版本號(hào)最高是 2.2
*/
err_code = WSAStartup(MAKEWORD(2, 2), &wsaData);
/* TODO Something */
WSACleanup();
這是最基本的在 Windows 上使用 套接字 編程的流程,但是如果本平臺(tái)的套接字庫(kù)最高版本并不符合當(dāng)前要求呢?
那么首先會(huì)將套接字版本庫(kù)盡可能設(shè)置到平臺(tái)的 最高版本 ,可以通過(guò)結(jié)構(gòu)體 WSADATA 進(jìn)行查詢
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
總體而言,
Windows平臺(tái)和*uix平臺(tái)的區(qū)別在于,前者使用時(shí)需要 加載和清除 套接字庫(kù) 其余邏輯流程一致,畢竟只有統(tǒng)一才能越利于編程世界的發(fā)展。