一些概念
簡單了解一下TCP,UDP這兩個協議,和一些概念
TCP與UDP
學校教過TCP是
- 傳輸層協議
- 有連接
- 可靠傳輸
- 面向字節流
而UDP是
- 傳輸層協議
- 無連接
- 不可靠傳輸
- 面向數據報
當時完全不知道這些什么意思
網絡字節序
網絡通信,要接收和發送數據。我們知道機器分為大端和小端存儲,那傳輸數據的時候就需要一個固定的標準,防止出錯。
于是有了:TCP/IP規定的網絡字節序,低地址高字節
1.發送主機通常將發送緩沖區中的數據按內存地址從低到高的順序發出;
接收主機把從網絡上接到的字節依次保存在接收緩沖區中,也是按內存地址從低到高的順序保存;
2.因此,網絡數據流的地址應這樣規定:先發出的數據是低地址,后發出的數據是高地址.
TCP/IP協議規定,網絡數據流應采用大端字節序,即低地址高字節. 不管這臺主機是大端機還是小端機,
都會按照這個TCP/IP規定的網絡字節序來發送/接收數據;
3.如果當前發送主機是小端, 就需要先將數據轉成大端; 否則就忽略, 直接發送即可;
主機序列與網絡序列的相互轉換
在頭文件<arpa/inet.h> 給了4個接口
函數 | 含義 | 作用 |
---|---|---|
htons | host to network short | 本地主機的 16 位整數轉為網絡序 |
htonl | host to network long | 本地主機的 32 位整數轉為網絡序 |
ntohs | network to host short | 網絡序的 16 位整數轉為主機序 |
ntohl | network to host long | 網絡序的 32 位整數轉為主機序 |
Socket編程
sockaddr
sockaddr是網絡通信的通用結構體
struct sockaddr {sa_family_t sa_family; // 地址族(協議族),如 AF_INETchar sa_data[14]; // 協議地址(具體內容依賴于地址族)
};
還有另外兩個結構體:
- sockaddr_in
struct sockaddr_in {sa_family_t sin_family; // 地址族,必須是 AF_INETuint16_t sin_port; // 端口號(網絡字節序)struct in_addr sin_addr; // IP 地址char sin_zero[8]; // 填充字節,保持與 sockaddr 一致
};
特別提示: sin_addr
結構體in_addr封裝如下
struct in_addr {in_addr_t s_addr; // IPv4 地址,32 位,網絡字節序
};
所以賦值的時候:要對s_addr進行操作
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 點分十進制轉網絡字節序
地址類型給AF_INET,表示網絡通信
2. sockadd_un
struct sockaddr_un {sa_family_t sun_family; // 地址族,必須是 AF_UNIXchar sun_path[108]; // 本地文件路徑,作為套接字標識
};
地址類型給AF_UNIX,表示本地通信
IP地址格式
IP地址有點分十進制(“127.0.0.1”)和十六進制4字節序列(0xC0A80101)
點分十進制是為了方便閱讀,但是,s_addr是4字節序列的格式,像下面這樣
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 點分十進制轉網絡字節序
就需要調用函數來進行轉換
函數 | 作用 |
---|---|
inet_aton | 點分十進制 → 4 字節網絡序 |
inet_ntop | 4 字節網絡序 → 點分十進制 |
in_addr_t inet_addr(const char *cp);
網絡字節序再轉換一次?
轉換為4字節序列后,需要轉換成網絡字節序再存入sockaddr_in
好消息,好消息!
inet_addr先完成轉換到4字節序列,再轉換為網絡字節序
// 創建 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);
// 開始監聽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);
補充:內存置0另一種方法(bzero與memset)
memset很常用,這里補充一個新的接口
void bzero(void *s, size_t n);
//s:指向要清零的內存塊的起始地址。
//n:要清零的字節數。
舉個例子
struct sockaddr_in addr;
bzero(&addr, sizeof(addr));
他等價于
memset(&addr, 0, sizeof(addr));
當然:依然推薦用memset
因為bzero 在 POSIX 標準中已廢棄
小結
介紹了socket基本概念和socket初始化的操作