在之后的文章中我們將來講解網(wǎng)絡編程中的相關知識點,再本文中我們首先來講解一下網(wǎng)絡編程中的預備知識:
預備知識
源IP地址和目的IP地址
在IP數(shù)據(jù)包中有兩個IP地址分別是源IP地址和目的IP地址,此時這里就會出現(xiàn)一個問題就是:如果我們光有IP地址,是無法完成通信的。有了IP地址只能夠將消息發(fā)送到對方的機器上,但是還徐亞歐有一個其他的標識位來進行區(qū)分,這個數(shù)據(jù)需要發(fā)送給哪一個程序進行解析。
端口號
端口號是(port)是傳輸層協(xié)議的內容
- 端口號是一個2字節(jié)16位的整數(shù);
- 端口號用來標識一個進程,告訴操作系統(tǒng),當前的這個數(shù)據(jù)要交給哪一個進程來處理;
- IP地址 + 端口號能夠標識網(wǎng)絡上的某一臺主機的某一個進程;
- 一個端口號只能被一個進程占用。
需要注意的是:一個進程可以綁定多個端口號,但是一個端口號不能被多個進程綁定
理解端口號和進程ID
之前我們在學習系統(tǒng)編程的時候,學習過在Linux中pid可以表示一個進程;此處在網(wǎng)絡編程中端口號也表示唯一的一個進程,那么這兩者有什么關系?對于此處的理解在我看來就是:我們使用的操作系統(tǒng)有很多,那么就意味著每一個操作系統(tǒng)都有著自己的進程ID的處理方式,但是網(wǎng)絡在目前只有一個,所有的操作系統(tǒng)都需要接入這個網(wǎng)絡,有了端口號就可以讓網(wǎng)絡在各個操作系統(tǒng)上都可以正常的運行,不用讓網(wǎng)絡來對操作系統(tǒng)進行適配,同時可以讓網(wǎng)絡與操作系統(tǒng)進行解耦。
認識TCP協(xié)議和UDP協(xié)議
在此處我們簡單直觀的了解一下TCP協(xié)議和UDP協(xié)議,后面我們再對其進行詳細的學習。
TCP協(xié)議
- 傳輸層協(xié)議
- 有連接
- 可靠傳輸
- 面向字節(jié)流
UDP協(xié)議
- 傳輸層協(xié)議
- 無連接
- 不可靠傳輸
- 面向數(shù)據(jù)報
網(wǎng)絡字節(jié)序
在之前學習C語言的時候,我們遇到過一個問題就是:計算機內存中的多字節(jié)序對于內存地址有大段和小段之分,磁盤文件中的多字節(jié)數(shù)據(jù)相對于文件中的偏移地址也有大段小段之分。對于網(wǎng)絡數(shù)據(jù)流來說同樣有著大段小段之分。
- 發(fā)送主機通常將發(fā)送緩沖區(qū)的數(shù)據(jù)按照內存地址從低到高的順序發(fā)出;
- 接受主機把網(wǎng)絡上接收到的字節(jié)依次保存在接受緩沖區(qū)中,也是按照內存地址從低到高的順序保存;
- 因此網(wǎng)絡數(shù)據(jù)流的地址應該這樣規(guī)定:先發(fā)出的數(shù)據(jù)是低地址,后發(fā)出的數(shù)據(jù)是高地址;
- TCP/IP協(xié)議規(guī)定,網(wǎng)絡數(shù)據(jù)流應采用大端字節(jié)序,即低地址高字節(jié);
- 不管這臺主機是大端機還是小端機,都會按照這個TCP/IP規(guī)定的網(wǎng)絡字節(jié)序來發(fā)送/接收數(shù)據(jù);如果當前發(fā)送主機是小端,就需要將數(shù)據(jù)轉成大端;否則就忽略,直接發(fā)送即可。
例子:將0x1234abcd寫入到以0x0000開始的內存中,則結果為:
big-endian | little-endian | |
---|---|---|
0x0000 | 0x12 | 0xcd |
0x0001 | 0x34 | 0xab |
0x0002 | 0xab | 0x34 |
0x0003 | 0xcd | 0x12 |
為了使網(wǎng)絡程序具有可移植性,使同樣的C代碼在大端和小端計算機上編譯后都能正常運行,可以調用一下的庫函數(shù)做網(wǎng)絡字節(jié)序和主機字節(jié)序的轉換。 |
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
// 這幾個函數(shù)名都比較好記: h表示host, n表示network, l表示32為長整數(shù), s表示16為短整數(shù)
// htonl 表示將32位的長整數(shù)從主機字節(jié)序轉換成網(wǎng)絡字節(jié)序,例如將IP地址轉化后準備發(fā)送
// 如果主機是小段字節(jié)序,這些函數(shù)將參數(shù)做相應的大小端轉換然后返回
// 如果主機是大段字節(jié)序,這些函數(shù)將不做轉換,將參數(shù)原封不動的返回
socket 常見API
// 創(chuàng)建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務器)
int socket(int domain, int type, int protocol);
// 綁定端口號 (TCP/UDP, 服務器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 開始監(jiān)聽socket (TCP, 服務器)
int listen(int socket, int backlog);
// 接收請求 (TCP, 服務器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立連接 (TCP, 客戶端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
在上述的API中我們都可以看到一個類型 – struct sockaddr結構
socket API是一層抽象的網(wǎng)絡編程接口,適用于各種底層網(wǎng)絡協(xié)議,如IPv4、 IPv6。然而, 各種網(wǎng)絡協(xié)議的地址格式并不相同。
struct sockaddr和struct sockaddr_in這兩個結構體用來處理網(wǎng)絡通信的地址。
- IPv4和IPv6的地址格式定義在netinet/in.h中,IPv4地址用sockaddr_in結構體表示,包括16位地址類型, 16位端口號和32位IP地址
- IPv4、 IPv6地址類型分別定義為常數(shù)AF_INET、 AF_INET6. 這樣,只要取得某種sockaddr結構體的首地址,不需要知道具體是哪種類型的sockaddr結構體,就可以根據(jù)地址類型字段確定結構體中的內容
- socket API可以都用struct sockaddr *類型表示, 在使用的時候需要強制轉化成sockaddr_in; 這樣的好處是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各種類型的sockaddr結構體指針做為參數(shù);
sockaddr結構
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
雖然socket api的接口是sockaddr,但是我們真正在基于IPv4編程時,使用的結構是sockaddr_in,這個結構體中主要有三部分信息:地址類型,端口號,IP地址文章來源:http://www.zghlxwxcb.cn/news/detail-697730.html
sockaddr_in
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
// -----------------------------------------------------------------
// __SOCKADDR_COMMON (sin_);的定義如下:
/*
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
sa_prefix##family 是一個預處理宏中的宏替換語法。它的作用是將兩個標識符合并成一個單詞,并用于創(chuàng)建結構體的字段名。在這個語法中:
sa_prefix 是一個標識符(通常是一個結構體字段的前綴),它是宏的一個參數(shù)。
## 是預處理器中的連接運算符,用于將兩個標識符連接在一起,形成一個新的標識符。
family 是另一個標識符(通常是表示套接字地址族的字段名稱),它在這里與 sa_prefix 合并。
// Type to represent a port.
typedef uint16_t in_port_t;
// Internet address.
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
*/
后面的文章中我們將開始使用UDP協(xié)議、TCP協(xié)議等來進行代碼的編寫。文章來源地址http://www.zghlxwxcb.cn/news/detail-697730.html
到了這里,關于網(wǎng)絡編程套接字 | 預備知識的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!