網(wǎng)站建設(shè)下坡路江小白網(wǎng)絡(luò)營銷案例
socket網(wǎng)絡(luò)編程
- 主機字節(jié)序列和網(wǎng)絡(luò)字節(jié)序列
- 套接字地址結(jié)構(gòu)
- 通用socket地址結(jié)構(gòu)
- 專用的socket地址結(jié)構(gòu)
- IP地址轉(zhuǎn)換函數(shù)
- 網(wǎng)絡(luò)編程接口
- TCP編程流程
- 代碼舉例
- 服務(wù)器代碼
- 客戶端
- 服務(wù)器并發(fā)處理多個客戶端
- 多線程
- 多進(jìn)程
主機字節(jié)序列和網(wǎng)絡(luò)字節(jié)序列
主機字節(jié)序列分為大端模式和小端模式,不同主機可能采用的不相同。大端模式是指一個整數(shù)的高字節(jié)位存儲在內(nèi)存的低地址處,低位字節(jié)存儲在內(nèi)存的高地址處。小端則是剛好相反。在兩臺使用不同字節(jié)序的主機之間傳遞數(shù)據(jù)時,可能會出現(xiàn)沖突。所以,在將數(shù)據(jù)發(fā)送到網(wǎng)絡(luò)時 規(guī)定整形數(shù)據(jù)使用大端字節(jié)序,所以也把 大端字節(jié)序成為網(wǎng)絡(luò)字節(jié)序列。對方接收到數(shù)據(jù)后,可以根據(jù)自己的字節(jié)序進(jìn)行轉(zhuǎn)換。
linux提供了四個函數(shù)來完成主機字節(jié)序和網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換。
#include <netinet/in.h>
uint32_t htonl(uint32_t hostlong);//長整型的主機字節(jié)序轉(zhuǎn)網(wǎng)絡(luò)字節(jié)序
uint32_t ntohl(uint32_t netlong);//長整型的網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)主機字節(jié)序
uint16_t htons(uint16_t hostshort);//短整型的主機字節(jié)序轉(zhuǎn)網(wǎng)絡(luò)字節(jié)序
uint16_t ntohs(uint16_t netshort);//短整型的網(wǎng)絡(luò)字節(jié)序列轉(zhuǎn)主機字節(jié)序列
套接字地址結(jié)構(gòu)
通用socket地址結(jié)構(gòu)
socket網(wǎng)絡(luò)變成接口中表示socket地址的是結(jié)構(gòu)體sockaddr,定義如下:
#include<bits/socket.h>
struct sockaddr{sa_family_t sa_family;char sa_data[14];
}
專用的socket地址結(jié)構(gòu)
TCP/IP 協(xié)議族有 sockaddr_in 和 sockaddr_in6 兩個專用 socket 地址結(jié)構(gòu)體,它們分別用于 IPV4 和 IPV6。
1. //
2. //sin_family: 地址族 AF_INET
3. //sin_port: 端口號,需要用網(wǎng)絡(luò)字節(jié)序表示
4. //sin_addr: IPV4 地址結(jié)構(gòu):s_addr 以網(wǎng)絡(luò)字節(jié)序表示 IPV4 地址
5. //
6. struct in_addr
7. {
8. u_int32_t s_addr;
9. };
11.
10. struct sockaddr_in
11. {
12. sa_family_t sin_family;
13. 15. u_int16_t sin_port;
16. struct in_addr sin_addr;
17. };
18.
19. struct in6_addr
20. {
21. unsigned char sa_addr[16]; // IPV6 地址,要用網(wǎng)絡(luò)字節(jié)序表示
22. };
23.
24. struct sockaddr_in6
25. {
26. sa_family_t sin6_family; // 地址族:AF_INET6
27. u_inet16_t sin6_port; // 端口號:用網(wǎng)絡(luò)字節(jié)序表示
28. u_int32_t sin6_flowinfo; // 流信息,應(yīng)設(shè)置為 0
29. struct in6_addr sin6_addr; // IPV6 地址結(jié)構(gòu)體
30. u_int32_t sin6_scope_id; // scope ID,尚處于試驗階段
31. };
IP地址轉(zhuǎn)換函數(shù)
通常,人們習(xí)慣用點分十進(jìn)制字符串表示 IPV4 地址,但編程中我們需要先把它們轉(zhuǎn)化為整數(shù)方能使用,下面函數(shù)可用于點分十進(jìn)制字符串表示的 IPV4 地址和網(wǎng)絡(luò)字節(jié)序整數(shù)表示的 IPV4 地址之間的轉(zhuǎn)換。
#include<arpa/inet.h>
in_addr_t inet_addr(const char* cp);//字符串表示的IPV4地址轉(zhuǎn)化位網(wǎng)絡(luò)字節(jié)序
char* inet_ntoa(struct in_addr in);//IPV4地址的網(wǎng)絡(luò)字節(jié)序轉(zhuǎn)化為字符串表示
網(wǎng)絡(luò)編程接口
#include<sys/types.h>
#inlcude<sys/socket.h>
int socket(int domain, int type, int protocol);//創(chuàng)建套接字,成功返回套接字文件描述符,失敗返回-1
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);//bind()將 sockfd 與一個 socket 地址綁定,成功返回 0,失敗返回-1
int listen(int sockfd, int backlog);//創(chuàng)建一個監(jiān)聽隊列等待客戶連接。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);//accept()從 listen 監(jiān)聽隊列中接收一個連接,成功返回一個新的連接 socket,唯一標(biāo)識鏈接成功失敗。失敗返回-1int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);//客戶端通過此系統(tǒng)來主動與服務(wù)器產(chǎn)生鏈接,成功返回0,失敗返回-1.int close(int sockfd);//關(guān)閉一個連接ssize_t recv(int sockfd, void *buff, size_t len, int flags);//recv()讀取 sockfd 上的數(shù)據(jù),buff 和 len 參數(shù)分別指定讀緩沖區(qū)的位置和大小ssize_t send(int sockfd, const void *buff, size_t len, int flags);// send()往 socket 上寫入數(shù)據(jù),buff 和 len 參數(shù)分別指定寫緩沖區(qū)的位置和數(shù)據(jù)長度ssize_t recvfrom(int sockfd, void *buff, size_t len, int flags,struct sockaddr* src_addr, socklen_t *addrlen);//讀取 sockfd 上的數(shù)據(jù),buff 和 len 參數(shù)分別指定讀緩沖區(qū)的位置和大小
ssize_t sendto(int sockfd, void *buff, size_t len, int flags,struct sockaddr* dest_addr, socklen_t addrlen);// sendto()往 socket 上寫入數(shù)據(jù),buff 和 len 參數(shù)分別指定寫緩沖區(qū)的位置和數(shù)據(jù)長度
TCP編程流程
TCP提供的是面向連接的可靠的字節(jié)流服務(wù)。TCP的服務(wù)器端和客戶端編程流程如下圖:
socket()方法是用來創(chuàng)建一個套接字,有了套接字就可以通過網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)的收發(fā)。
bind()方法是用來指定套接字使用的 IP 地址和端口。
listen()方法是用來創(chuàng)建監(jiān)聽隊列。
accept()處理存放在 listen 創(chuàng)建的已完成三次握手的隊列中的連接。
connect()方法一般由客戶端程序執(zhí)行,需要指定連接的服務(wù)器端的 IP 地址和端口。該方法執(zhí)行后,會進(jìn)行三次握手, 建立連接。
send()方法用來向 TCP 連接的對端發(fā)送數(shù)據(jù)。send()執(zhí)行成功,只能說明將數(shù)據(jù)成功寫入
到發(fā)送端的發(fā)送緩沖區(qū)中,并不能說明數(shù)據(jù)已經(jīng)發(fā)送到了對端。send()的返回值為實際寫入到發(fā)送緩沖區(qū)中的數(shù)據(jù)長度。
recv()方法用來接收 TCP 連接的對端發(fā)送來的數(shù)據(jù)。recv()從本端的接收緩沖區(qū)中讀取數(shù)據(jù),如果接收緩沖區(qū)中沒有數(shù)據(jù),則 recv()方法會阻塞。返回值是實際讀到的字節(jié)數(shù),如果recv()返回值為 0, 說明對方已經(jīng)關(guān)閉了 TCP 連接。
close()方法用來關(guān)閉 TCP 連接。此時,會進(jìn)行四次揮手。
代碼舉例
服務(wù)器代碼
客戶端
服務(wù)器并發(fā)處理多個客戶端
多線程
客戶端代碼同上相同
多進(jìn)程