UDP
# 前序
UDP(用戶資料報協議)沒有連接的,是面向資料報的,是不可靠
# 套接字
就是IP地址+埠號
IP地址:4位元組
埠號:2位元組,也就是說范圍是0~65535
埠號分為
知名埠號
0--1023:http,ssh,ftp,telnet等一些協議埠號都是固定的,對于作業系統來說是不能對其進行分配的
一些固定的埠號
ssh服務器,使用22埠
ftp服務器,使用21埠
telnet服務器,使用23埠
http服務器,使用80埠
https服務器,使用443埠
作業系統動態分配的埠號
客戶端服務器的埠號,這個范圍的埠號作業系統可以對其進行分配
查看埠號
less /etc/services //就可以查看Linux下所有的埠號了
IP地址的理解:
IP地址用來標識一個主機
埠號的理解:
埠號就是用來告訴作業系統要對于那一個行程進行操作,也就是說埠號就是用來標識一個行程
一個埠號只可被一個行程所占用,但是一個行程可以擁有多個埠號,也就是行程和埠號是一對多的關系
當我們寫一個程式使用埠號的時候,要避開這些知名埠號
【問題】
一個行程是否可以bind多個埠號呢?
可以,因為一個行程可以打開多個檔案描述符,而每一個檔案描述符都對應著一個埠號,所以一個行程可以系結多個埠號
一個埠號是否可以被多個行程bind?
不可以
如果一個行程先系結一個埠號,然后再fork一個子行程,這樣的話就實作了多個行程系結一個埠號,但是不同的行程系結同一個埠號是不可以的
TIME_WAIT狀態,服務器不能立即重啟也說明不用行程不能同時系結同一個埠號
多個行程可以監聽同一個埠號嗎?
可以,監聽之前要進行創建套接字->系結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 函式的作用
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 root@192.168.153.140:/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://www.uj5u.com/qita/224195.html
標籤:其他


