1、目的
實現不同主機上進程間的通信。
2、問題
- 主機與主機之間在物理層面必須互聯互通。
- 進程與進程在軟件層面必須互聯互通。
- IP地址:計算機的軟件地址,用來標識計算機設備。
- MAC地址:計算機的硬件地址(固定)。
- 網絡的端口號:標記同一主機上的不同網絡進程。
3、網絡協議
網絡通信標準
OSI七層模型
開放系統互聯模型,是不同設備間網絡通信的通信標準,可概括為“物數網傳會表應”。
- 應用層:要傳輸的數據信息,如文件傳輸、電子郵件等。
- 表示層:進行數據加密、解密操作,壓縮、解壓縮。
- 會話層:建立數據傳輸通道(會話)。
- 傳輸層:確定傳輸方式(UDP、TCP),涉及端口號。
- 網絡層:實現數據路由、路徑規劃(路由器、IP),涉及傳輸方式(UDP、TCP)和端口號。
- 數據鏈路層:將各層數據封裝成幀,實現點對點通信(局域網內通信),進行差錯檢測(交換機、ARP)。
- 物理層:定義物理設備標準、電器特性,涉及網線、光纖等傳輸介質。
TCP/IP模型
應用模型,有五層和四層兩種劃分方式。
五層劃分
- 應用層:
- HTTP:超文本傳輸協議。
- HTTPS:超文本傳輸協議(SSL加密算法)。
- FTP:文件傳輸協議(TCP)。
- TFTP:簡單文本傳輸協議(UDP)。
- MQTT:消息隊列遙測傳輸(物聯網協議)。
- DNS:域名解析服務(將域名轉換為IP地址)。
- 傳輸層:
- TCP:傳輸控制協議。
- UDP:用戶數據報協議。
- 網絡層:
- IP協議:包括IPv4和IPv6。
- 數據鏈路層:
- ARP協議:地址解析協議。
- 物理層
四層劃分
- 應用層
- 傳輸層
- 網絡層
- 網絡接口層
4、IP協議
基本概念
IP協議位于網絡層。
- IPv4為32位,如192.168.1.128。
- IPv6為128位。
IP地址的用戶表示形式為點分十進制,如192.168.1.140;
計算機存儲形式為32位二進制,如11000000 10101000 00000000 01000011。
組成與相關概念
- IP地址組成:IP地址 = 網絡位 + 主機位。
- 例如192.168.0.121/24,其中24表示網絡位的位數。
- 網絡位:標識該IP地址位于哪個網段(局域網)內。
- 主機位:標識在這個網段(局域網)內的第幾臺主機。
- 例如192.168.0.121/24,其中24表示網絡位的位數。
- 子網掩碼:用于區分IP地址的網絡位和主機位,搭配IP地址使用。
- 子網掩碼是1的部分對應IP地址的網絡位,是0的部分對應IP地址的主機位。
- 如255.255.255.0,其二進制形式為11111111.11111111.11111111.00000000。
- 網段號:IP地址網絡位不變,主機位全為0,則為該IP地址的網段號。
- 例如,IP地址192.168.1.3,子網掩碼255.255.0.0,其網段號為192.168.0.0,位于192.168.1.0網段內(網段內的IP能直接通信)。
- 廣播號:IP地址網絡位不變,主機位全為1,則為該IP地址的廣播號。
- 向廣播號發送信息,所有局域網內IP都能收到此信息。例如,IP地址192.168.1.3,子網掩碼255.255.255.0,其廣播號為192.168.1.255(如feiQ、VNC使用)。
- 網關地址:如192.168.1.1。
IP地址劃分
類別 | 范圍 | 子網掩碼 | 用途 | 私有IP地址范圍 |
---|---|---|---|---|
A類地址 | 1.0.0.0 - 126.255.255.255 | 255.0.0.0 | 管理大規模網絡 | 10.0.0.0 - 10.255.255.255 |
127.0.0.0 | 回環地址 | |||
B類地址 | 128.0.0.0 - 191.255.255.255 | 255.255.0.0 | 管理大中規模網絡 | 172.16.0.0 - 172.31.255.255 |
C類地址 | 192.0.0.0 - 223.255.255.255 | 255.255.255.0 | 管理中小規模網絡 | 192.168.0.0 - 192.168.255.255 |
D類地址 | 224.0.0.0 - 239.255.255.255 | 組播和廣播使用 | ||
E類地址 | 240.0.0.0 - 255.255.255.254 | 用來進行實驗 |
- 公有IP:由電信公司直接分配,需要付費,可以直接訪問internet。
- 私有IP:不能直接訪問internet的IP地址,目的是節省IP地址。
5、網絡端口號
基本信息
????????端口號是16位的整形數據(unsigned short),范圍為0~65535,功能是標記同一主機的不同網絡進程。
分類
- 1-1023之間的端口號:任何TCP/IP實現所提供的服務使用。如http:80,FTP:20/21,TFPT:69,HTTPS:443。
- 1024-49151:被注冊的端口號,被IANA指定為特殊服務使用。如MQTT:1883/8883。
- 49152-65535:動態或私有端口號。
6、網絡配置
- ping命令:ping ip地址/域名,用于查看當前主機和IP/域名所對應的主機網絡是否聯通。如ping www.baidu.com。
- 查看IP地址:
- 在Linux中使用????????ifconfig。
- 在Windows上使用? ? ? ipconfig。
- 網絡配置步驟:
- 虛擬機--》設置--》網絡適配器---》橋接模式。
- 編輯--》虛擬網絡編輯器--》更改設置--》VMnet0---》橋接至--》當前PC正在上網的網卡上--》應用--》確定。
- 修改網絡配置文件:sudo vim /etc/network/interfaces,文件內容如下:
auto lo iface lo inet loopback auto ens33 iface ens33 inet dhcp
- 重啟網絡服務:sudo /etc/init.d/networking restart
- 測試:ping www.baidu.com
7、網絡協議--UDP
基本概念
UDP(User Datagram Protocol)即傳輸層用戶數據報協議。
網絡編程模型
- B/S模型(browser/server,瀏覽器/服務器):
- 客戶端是通用的客戶端(瀏覽器)。
- 一般只做服務器開發。
- 客戶端要加載的數據均來自服務器。
- C/S模型(client/server,客戶端/服務端):
- 客戶端是一個專用的客戶端。
- 服務器和客戶端都需開發。
- 客戶端可以保存資源,本地加載,無需所有數據都請求服務器。
UDP編程
套接字
套接字是文件描述符,是網絡通信時應用層可操作的端口。
相關函數
socket函數
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol);
- 功能:創建通信的套接字。
- 參數:
- domain:網絡層使用的協議族,AF_INET表示IPv4,AF_INET6表示IPv6。
- type:規定傳輸層的協議,SOCK_DGRAM表示UDP協議,SOCK_STREAM表示TCP協議,SOCK_RAW表示原始套接字。
- protocol:0按照默認協議方式創建。
- 返回值:成功返回套接字,失敗返回-1。
sendto函數
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
- 功能:向網絡套接字發送數據。
- 參數:
- sockfd:套接字。
- buf:要發送的數據的首地址。
- len:要發送的字節數。
- flags:0表示按照默認方式發送。
- dest_addr:接收方的地址信息(IP+端口號)。
- addrlen:接收方地址的大小。
- 返回值:成功返回實際發送的字節數,失敗返回-1。
sockaddr_in結構體(man 7 ip)
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ in_port_t sin_port; /* port in network byte order */ struct in_addr sin_addr; /* internet address */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
字節序轉換函數? ??
字節序轉換
網絡字節序(大端順序)和主機字節序(小端順序)在數據存儲方式上存在差異。
網絡字節序中,高位字節存儲在低地址;主機字節序中,低位字節存儲在低地址。
例如,數值$0x1234$:
- 小端存儲:低地址為$0x34$,高地址為$0x12$
- 大端存儲:低地址為$0x12$,高地址為$0x34$
標準庫函數提供了字節序轉換功能,具體聲明如下:
uint32_t htonl(uint32_t hostlong); // 主機轉網絡(32位)
uint16_t htons(uint16_t hostshort); // 主機轉網絡(16位)
uint32_t ntohl(uint32_t netlong); // 網絡轉主機(32位)
uint16_t ntohs(uint16_t netshort); // 網絡轉主機(16位)
in_addr_t inet_addr(const char *cp);
功能:將字符串IP地址轉換成二進制IP地址形式char *inet_ntoa(struct in_addr in);
功能:將二進制ip轉換成字符串
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:綁定自己的IP地址和端口號
參數:sockfd:套接字addr:需要綁定的地址addrlen:地址大小
返回值:成功:0失敗:-1
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
功能:從套接字上接收數據
參數:sockfd:套接字buf:存放接收數據的內存首地址len:希望接收的字節數flags:0 :按照默認方式接收(阻塞)src_addr:發送方的地址信息addrlen:發送發地址的指針
功能:成功:實際接收到的字節數失敗:-1
8、代碼訓練
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include<string.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("socket error");return -1;}// struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.179");// 創建子進程char buff[1024] = {0};while(1){fgets(buff, sizeof(buff), stdin);ssize_t cnt = sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&seraddr, sizeof(seraddr));if(cnt < 0){perror("sendto error");return -1;}printf("cnt = %ld\n", cnt);memset(buff, 0, sizeof(buff));}close(sockfd);return 0;
}
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <stdlib.h>int main(int argc, char const *argv[])
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){perror("socket error");return -1;}// 服務端自己的地址信息變量struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.177");int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if(ret < 0){perror("bind error");ret -1;}char buff[1024] = {0};while(1){ssize_t cnt = recvfrom(sockfd, buff, sizeof(buff), 0, NULL, NULL);if(cnt < 0){perror("recvfrom error");return -1;}printf("cnt = %ld, buff = %s\n", cnt, buff);memset(buff, 0, sizeof(buff));}close(sockfd);return 0;
}