大家好,又見面了,我是全棧君。
前序
UDP(使用者資料報協定)沒有連接配接的,是面向資料報的,是不可靠
# 套接字就是IP位址+端口号
IP位址:4位元組
端口号:2位元組,也就是說範圍是0~65536
端口号分為
知名端口号
0–1023:http,ssh,ftp,telnet等一些協定端口号都是固定的,對于作業系統來說是不能對其進行配置設定的
一些固定的端口号
ssh伺服器,使用22端口
ftp伺服器,使用21端口
telnet伺服器,使用23端口
http伺服器,使用80端口
https伺服器,使用443端口
作業系統動态配置設定的端口号
用戶端伺服器的端口号,這個範圍的端口号作業系統可以對其進行配置設定
檢視端口号
less /etc/services //就可以檢視Linux下所有的端口号了
複制
IP位址的了解:
IP位址用來辨別一個主機
端口号的了解:
端口号就是用來告訴作業系統要對于那一個程序進行操作,也就是說端口号就是用來辨別一個程序
一個端口号隻可被一個程序所占用,但是一個程序可以擁有多個端口号,也就是程序和端口号是一對多的關系
當我們寫一個程式使用端口号的時候,要避開這些知名端口号
【問題】
1.一個程序是否可以bind多個端口号呢?
可以,因為一個程序可以打開多個檔案描述符,而每一個檔案描述符都對應着一個端口号,是以一個程序可以綁定多個端口号
2.一個端口号是否可以被多個程序bind?
不可以
如果一個程序先綁定一個端口号,然後再fork一個子程序,這樣的話就實作了多個程序綁定一個端口号,但是不同的程序綁定同一個端口号是不可以的
TIME_WAIT狀态,伺服器不能立即重新開機也說明不同程序不能同時綁定同一個端口号
3.多個程序可以監聽同一個端口号嗎?
可以。監聽之前要進行建立套接字->綁定ip::端口号->監聽。我們可以在bind之前使用setsockopt函數,設定套接字選項,其中就包括REUSEADDR這個選項,表明多個程序可以複用bind函數中指定的位址和端口号
是以套接字就可以準确的辨別一台主機上的一個程序,進而完成計算機之間的通信
計算機之間的通信:
主機A的某個程序與主機B上的另一個程序進行通信
網絡位元組序轉換
對于資料在網絡中傳輸的時候有着自己遵循的傳輸規則大端傳輸
對于主機上的資料的傳輸序列有着兩種:
大端:即高位位元組序放在低位址上
小端:即低位位元組序放在低位址上
傳輸:均是先傳輸低位址上的資料然後是高位址上的資料
是以對于主機上的資料傳輸的時候傳輸到網絡上的時候有可能導緻資料錯誤(例如主機上是小端的時候,是以需要進行轉換)
轉換函數:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16 hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
複制
h:表示主機host name
n:表示網絡network
l:表示4位元組long
s:表示2位元組short
位址轉換函數
字元串轉化為in_addr
in_addr_t inet_addr(const char* strptr)
複制
in_addr轉化為字元串
char* inet_ntoa(struct in_addr inaddr)
具有不可重入性,也就是不可多次調用,因為該函數自己在靜态區開辟一塊空間用來存放IP位址字元串的
UDP協定
UDP協定端格式
插圖:UDP協定端格式
16為UDP長度,表示整個資料報(UDP首部+UDP資料)的最大長度(64KB)
檢驗和:如果校驗和出錯,就會直接丢棄(檢驗的是把首部和資料部分一起都檢驗)
校驗值首先在資料發送方通過特殊的算法計算得出,在傳遞到接收方之後,還要在重新計算。如果某個資料報在傳輸過程中被第三方篡改或者由于線路噪音等原因受到損壞,發送和接收方的校驗計算值将不會相符,由此UDP協定可以檢驗是否出錯。
源端口号:在對方回信是選用,不需要時可用全0
目的端口号:在終點傳遞報時必須要用到
長度:UDP使用者資料報的長度,其最小值是8(僅有首部)
UDP的特點
無連接配接:直到對端的IP和端口号就直接進行傳輸,不需要建立連接配接
不可靠:沒有确認機制,沒有重傳機制;因為沒有網絡故障該段無法發送到對方,UDP協定層也不會給應用層傳回任何錯誤資訊
面向資料報:不能夠靈活的控制讀寫資料的次數和數量
控制選項較少,資料傳輸過程中延遲小,資料傳輸效率高
面向資料報
應用層交給UDP多長的封包,UDP原樣發送,既不會拆分也不會合并
例:用UDP傳輸100個位元組的資料
如果發送端調用一次sendto,發送100個位元組。那麼接收端也必須調用對應的一次recvfrom,接收100位元組;而不能循環調用10次recvfrom,每次發送10個位元組
UDP的緩存區
UDP沒有發送緩存區,調用sendto之後會直接交給核心,由核心·将資料傳給網絡層協定進行後續的傳輸動作。因為UDP是不面向連接配接的,是以沒有重發機制,也就不需要發送緩存區将已經發送的資料儲存下來為了發送失敗進行重傳做準備
UDP具有接收緩存區。但是這個接收緩存區不能保證收到的UDP報的順序和發送UDP報的順序一緻;如果緩存區滿了,在到達的UDP資料就會被丢棄
UDP的Socket既能讀,也能寫,全雙工
UDP的使用注意事項
UDP協定首部中有一個16位的最大長度,也就是說一個UDP能傳輸的資料的最大長度是64K(包含UDP首部)。但是64K在當今的網際網路環境下,是一個非常小的數字。如果我們需要傳輸的資料超過64K,就需要應用層手動的分包,多次發送,并在接收端拼裝
UDP首部中校驗和的計算方法有些特殊。在計算校驗和時,要在UDP使用者資料報之前增加12個位元組的僞首部
僞首部既不向下傳輸也不想上遞送,而僅僅是為了計算校驗和
與IP資料報的校驗和隻檢驗IP資料報的首部不同,UDP的校驗和是把首部和資料部分一起都檢驗
僞首部:
插圖:僞首部
基于UDP的應用層的協定
NFS:網絡檔案系統
TFTP:簡單檔案傳輸檔案協定
DHCP:動态主機配置協定
DNS:域名解析協定
面試題:用UDP實作可靠傳輸?
參考TCP的可靠性機制,在應用層實作類似的邏輯
引用序列号,保證資料順序
引入确認應答,確定對端收到了資料
引入逾時重傳,如果隔一段時間沒有應答,就重發資料
1. 對于socket函數的使用
1.1 函數原型
int socket(int domain, int type, int protocol);
複制
domain: 領域
AF_INET:IPV4
AF_INET6:IPV6
type: 類型
SOCK_STREAM
SOCK_DGARM
protocol: 協定
複制
1.2 函數的作用
在通信領域中建立一個未被綁定的套接字,并且傳回一個檔案描述符,可以在以後對套接字進行操作的函數調用中使用
2. 對于bind函數的使用
2.1 函數原型
int bind(int socket, const struct sockaddr* address, socklen_t address_len);
複制
2.2. 函數的作用
該函數采用先前建立好的套接字來對于IP位址以及端口号進行綁定,也就是表示該套接字可以辨別出在一個網絡中一台确定的主機并且主機中的程序
3. 對于recvfrom函數的使用
3.1 函數原型
ssize_t recvfrom(int socket, void* restrict buffer, size_t length,
int flags, struct sockaddr* restrict address,
socklen_t* restrict address_len);
socket:要接受那一個套接字的消息
buffer:用來接收消息的緩存區
length:接收的消息的長度
flags:類型
address:空指針或者存儲發送資訊的sockaddr結構
addless_len:指定位址參數指向的sockaddr結構的長度
複制
3.2 函數的作用
用來接收從socket套接字發送來的消息。該套接字的sockaddr結構也知道
4. 對于sendto函數的使用
4.1 函數原型
ssize_t recvfrom(int socket, const void* message, size_t length,
int flags, const struct sockaddr* dest_addr,
socklen_t* dest_len);
複制
4.2 函數的作用
該函數是socket套接字從dest_addr出接收消息
5. 擴充知識
5.1 netstat
netstat是一個用來監控TCP/IP網絡非重要工具
文法:netstat [選項]
功能:檢視網絡狀态
選項:
-a,顯示所有連線的Socket
-c,持續列出網絡狀态
-n,直接使用ip位址,而不通過域名伺服器,也就是顯示為數字
-l,顯示監控中的伺服器的Socket,僅列出監聽(Listen)狀态下的Socket
-p,顯示正在使用Socket的程式的識别碼和名稱(PID/Program name)
-t,顯示TCP傳輸協定的連線狀況
-u,顯示UDP傳輸協定的連線狀況
-v,顯示指令執行過程
-V,顯示版本資訊
-x,顯示UNIX傳輸協定的連線狀況
-s,顯示網絡工作資訊統計表
-h,線上幫助
複制
5.2 pidof
檢視伺服器程序id是非常方面
文法:pisdof [程序名]
複制
功能:通過程序名,檢視程序id
5.3 scp指令
基于ssh登入進行的網絡安全的遠端檔案拷貝指令
例:要将自己目前路徑下的clinet檔案發送到主機IP為192.168.153.140的home目錄下
scp ./clinet [email protected]:/home
複制
5.4 關于防火牆的指令
啟動:systemctl start firewalld
關閉:systemctl stop firewalld
檢視狀态:systemctl status firewalld
開機禁用:systemctl disable firewalld
開機啟用:systemctl enable firewalld
複制
對于UDP書寫伺服器的思路
由于UDP是無連接配接的,是以對于兩個處于同一區域網路下計算機的程序之間通信,是以是不需要兩台計算機之間的程序進行連接配接的,對于UDP使用的接口是需要包含知道從哪裡接收消息的,要發送消息到哪裡的。
實作本地通信
伺服器
隻需要伺服器建立一個套接字
使該套接字對于本地位址(127.0.0.1)進行綁定,并且綁定一個端口号(1024–65535)就行了
綁定本地位址是為了對于本地計算機的兩個程序程序通信,而綁定端口号是為了綁定一個程序,是為了對于用戶端進行發送消息到伺服器的時候,可以找到伺服器
然後就接受用戶端發來的消息
對于用戶端的消息進行處理然後就可以再次将處理後的消息進行傳回
插圖:伺服器流程
用戶端
綁定一個套接字
為了綁定一個程序,可以和伺服器進行通信,将消息發送過去的時候要讓伺服器知道是哪一個程序再和他程序通信
用戶端隻需要向伺服器發送消息
然後再次從用戶端接收消息就好了,不需要考慮要進行連接配接
插圖:用戶端流程
實作處于同一區域網路下的不同主機間進行通信
伺服器
和本地通信的一緻,隻是對于套接字綁定的ip位址不一樣了
也對于套接字要綁定該區域網路的ip位址以及一個端口号,不需要在綁定本地位址(127.0.0.1)了
這樣的話處于同一區域網路下的計算機的程序就可以進行通信了
用戶端
對于用戶端來說沒有任何改變,仍然是隻需要知道伺服器的ip和端口号就行了
對于UDP伺服器要注意的問題
啟動用戶端
啟動用戶端的時候必須給用戶端輸入一個ip位址和端口号,這個ip位址和端口号也就是要知道用戶端要發送消息給哪一個伺服器進行發送
啟動伺服器
必須要給伺服器綁定一個ip位址和端口号,也就是要注意該伺服器處于該計算機上的哪一個程序上
釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/111926.html原文連結:https://javaforall.cn