20191320-2021-2022-1-diocs 學習筆記11
第13章 TCP/IP和網路程式設計
TCP/IP協議
TCP/IP 是網際網路的基礎。TCP代表傳輸控制協議。IP代表網際網路協議。目前有兩個版本的IP,即IPv4和IPv6。IPv4使用32位地址,IPv6則使用128位地址。本節圍繞IPv4進行討論,它仍然是目前使用最多的IP版本。TCP/IP的組織結構分為幾個層級,通常稱為TCP/IP堆疊。
頂層是使用TCP/IP的應用程式。用於登入到遠端主機的ssh、用於交換電子郵件的郵件、用於Web頁面的http等應用程式需要可靠的資料傳輸。通常,這類應用程式在傳輸層使用TCP。另一方面有些應用程式,例如用於查詢其他主機的ping命令,則不需要可靠性。這類應用程式可以在傳輸層使用UDP來提高效率。
主機:主機是支援TCP/IP協議的計算機或裝置。每個主機由一個32位的IP地址來標識。
IP地址:IP地址分為兩部分,即NetworkID欄位和HostID欄位。根據劃分,IP地址分為A~E 類。
例如,一個B類IP地址被劃分為一個16位網路號,其中前2位是10,然後是一 個16位的主機號欄位。發往IP地址的資料包首先被髮送到具有相同網路號的路由器。 路由器將通過主機號將資料包轉發到網路中的特定主機。每個主機都有一個本地主機名localhost,預設IP地址為127.0.0.1。
路由器是接收和轉發資料包的特殊IP主機。如果有的話, 一個IP資料包可能會經過許多路由器,或者跳躍到達某個目的地。
UDP
UDP是使用者資料報協議。
UDP在IP上執行,用於傳送/接收資料報。與IP類似,UDP不能保證可靠性,但是快速高效。它可用於可靠性不重要的情況。
ping使用的是UDP協議。
TCP
TCP(傳輸控制協議)是一種面向連線的協議,用於傳送/接收資料流。TCP也可在IP上執行,但它保證了可靠的資料傳輸。通常,UDP類似於傳送郵件的USPS,而TCP類似於電話連線。
埠和應用
應用程式=(主機IP,協議,埠號)
TCP/IP網路中的資料流
應用程式層的資料被傳遞到傳輸層,傳輸層給資料新增一個TCP或UDP報頭來標識使用的傳輸協議。合併後的資料被傳遞到IP網路層,新增一個包含IP地址的IP報頭來標識傳送和接收主機。然後,合併後的資料再被傳遞到網路錐路層,網路鏈路層將資料分成多個幀,並添加發送和接收網路的地址,用於在物理網路之間傳輸:IP地址到網路地址的對映由地址解析協議(ARP)執行。在接收端,資料編碼過程是相反的。 每一層通過剝離資料頭來解包接收到的資料,重新組裝資料並將資料傳遞到上層。傳送主機上的應用程式原始資料最終會被傳遞到接收主機上的相應應用程式。
網路程式設計
伺服器-客戶機計算模型:大多數網路程式設計任務都基於伺服器-客戶機計算模型。
套接字:
struct sockaddr_in ( sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr;
);
struct in_addr {
uint32_t s_addr;
在套接字地址結構中,
- TCP/IP 網路的 sin_family 始終設定為 AF_INET。
- sm_port包含按網路位元組順序排列的埠號。
- sin_addr是按網路位元組順序排列的主機IP地址。
Lint套接字(int域,int型別,int協議)
int udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
將會建立一個用於傳送/接收UDP資料報的套接字。
int tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
將會建立一個用於傳送/接收資料流的面向連線的TCP套接字。
實踐與問題解決
程式設計練習:使用C語言實現簡單UDP傳輸。
本實踐可以通過C語言,在客戶機和伺服器之間實現UDP的資料傳輸。
程式碼如下:
客戶端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#define MAXBUF 256
int main(int argc, char const *argv[])
{
int s = 0;
int n = 0;
int reuse = 1;
int port = 1987;
struct sockaddr_in srv;
char buf[MAXBUF] = {0};
/*解析引數*/
if (argc != 2)
{
printf("Usage:%s ServerIP\n", argv[0]);
return -1;
}
bzero(&srv, sizeof(srv));
srv.sin_family = PF_INET;
srv.sin_addr.s_addr = inet_addr(argv[1]);
srv.sin_port = htons(port);
/*建立 UDP 套節字*/
s = socket(AF_INET, SOCK_DGRAM, 0);
if(s<0){
perror("socket");
return -1;
}
while(1){
memset(buf, 0, MAXBUF);
/*讀取使用者輸入到buf中*/
fgets(buf, MAXBUF, stdin);
/*通過套節字 s 向伺服器傳送資料*/
if ((n = sendto(s, buf, strlen(buf), 0, (struct sockaddr *) &srv, sizeof(struct sockaddr))) < 0)
{
perror("sendto");
return -1;
}else{
printf("send to %s(port=%d) len %d:%s\n", argv[1], port, n, buf);
}
}
}
伺服器端:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#define MAXBUF 256
int main(int argc, char const *argv[])
{
int s = 0;
int n = 0;
int reuse = 1;
int cli_len = sizeof(struct sockaddr);
int port = 1987;
char buf[MAXBUF] = {0};
struct sockaddr_in addr, cli;
/*初始化本地監聽埠資訊*/
bzero(&addr, sizeof(addr));
addr.sin_family = PF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
/*建立UDP套節字*/
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s<0)
{
perror("socket");
return -1;
}
/*允許埠複用*/
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
/*繫結指定埠*/
if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
perror("bind");
return -1;
}
while(1){
memset(buf, 0, MAXBUF);
/*從套節字s中讀取資料*/
n = recvfrom(s, buf, MAXBUF, 0, (struct sockaddr *)&cli, &cli_len);
if(n<0){
perror("recvfrom");
return -1;
}else{
printf("receive msg from %s(port=%d) len %d: %s\n",inet_ntoa(cli.sin_addr), port, n, buf);
}
}
return 0;
}
實踐截圖:
程式碼連結
程式碼包括一些以前的程式碼,在碼雲。連結:https://gitee.com/Ressurection20191320/code/tree/master/IS