【草履虫10天学会C++网络编程】 UDP
學習路線:
目錄
學習路線:
1、TCP/IP協議特點
1.1 網絡分層結構
1.?2? IP(因特網互聯協議)協議簡介(網絡協議)
1.3 MAC地址、IP地址、Netmask
1.4 端口
1.5 組包拆包
1.6 協議格式
IP報頭:
tcp報頭
2、網絡開發模式
2.1 tcp和udp的區別
2.2 字節序
?2.3 網絡編程
?2.4 網絡字節序轉API
2.5 地址轉換函數
2.6?網絡編程接口 socket
2.7?網絡編程接口 socket
3. UD?
3.1 代碼編寫
3.2? 創建套接字
創建UDP套接字demo:
3.3 TFTP簡介、通信過程
3.4?UDP廣播
1、TCP/IP協議特點
這是一個協議簇(多種協議的集合)
1.1 網絡分層結構
因為網絡在通訊過程中是非常復雜的事情,我們要將網絡分層一片片小的事情,把每一層做好了就可以發送了。上一層對下一層,或者下一層對上一層是屏蔽的。
?OSI開發系統互聯模型(七層)
4層模型(常見)
1.?2? IP(因特網互聯協議)協議簡介(網絡協議)
- 特點:不可靠,不能保證IP數據包能成功到達它的目的地
- 無連接:不維護后續數據包的狀態信息。每個數據包的處理是相互獨立的,可以不按照發送順序接收。?
1.3 MAC地址、IP地址、Netmask
- 網卡(適配器):硬件,能傳輸數據的都是網卡信號轉為數字信號,每個網卡都有自己的物理地址(全球理論上唯一)-> MAC地址48bit 6字節
- IP地址:表示手機或者網卡的虛擬地址,ipv4(局域網):32位; ipv6(廣域網)。128位? 主機ip(192.168.1.12)+子網id(255.255.255.0)
- 子網掩碼:連續的1或者0,結合ip一起使用
- 子網id:IP中被子網掩碼1中連續覆蓋的位置
- 主機id:IP中被子網掩碼0中連續覆蓋的位置
?回環地址:用來測試本主機,到網絡層就截止了
linux下設置地址:
ifconfig eth0 192.168.1.1 netmask 255.255.255.01.4 端口
四層網絡->鏈路層->網絡層->傳輸層->應用層
端口號的主要用途是在計算機網絡和應用程序之間(傳輸層)傳輸數據
端口號的范圍從 0 到 65535(一個字節8位,2的16次方=65536)。它們分為三個子范圍:
-
知名端口 (0-1023,比如說SSH:22)?– 由IANA(互聯網號碼分配機構)分配給特定服務。在 Linux 系統上,只有以 root 身份運行的特權程序才能使用 1024 以下的端口。
-
注冊端口 (1024-49151)?– 組織可以向 IANA 注冊以用于特定服務的端口。
-
動態端口 (49152-65535)?– 由客戶端程序使用。
在網絡中有兩種使用端口號的主要傳輸協議TCP和UDP)
?端口可以重用,但是不能重復(標記應用程序的,為啥不用進程號?因為啟動的時候沒法確定進程號)
1.5 組包拆包
?A主機的MAC地址和IP地址:應用層->發送飛秋協議->組合包->傳輸層走UDP(局域網)協議->
網絡層走IP協議->鏈路層加mac
B主機的MAC地址和IP地址:反方向時鐘
1.6 協議格式
?注意:
無線網封裝格式 :IEEE802.2
以太網封裝:有限局域網 無線網比有限多8個字節
IP報頭:
?前部有20個字節用來放協議。
TTL:是一個經過了多少個路由器,默認是64個值,每經過一個就-1.
ping www.baidu.com?可以看出來,百度和你之間經過了64-56=8個路由器,當TTL減到0的時候,這個數據包就不會轉發了,他會覺得你這個包是個垃圾,就不要了。
tcp報頭
?序列號:我當前包有個序列號
確認序列號:如果我收到了你給我發的那個包,確認收到(很重要,面試會問)
2、網絡開發模式
B/S browser/sever 瀏覽器與服務器模型
- 缺點:對服務器性能要求過高,不能用于對性能要求高的程序。
- 優點:不容易偷數據,比較安全,移植性高不依賴平臺。開發平臺較短
C/S? clien/sever 客戶端和服務器
- 缺點:不是特別安全,依賴平臺
- 優點:可運行對性能要求較高的程序
2.1 tcp和udp的區別
TCP是面向連接的:打電話模型的抽象,本質上是一個管道。分為建立連接->使用連接->中止連接
UDP面向無連接。(沒有服務器和客戶端一說):郵件系統服務模式的抽象,速度快。自己加協議,讓其可靠。
- 不能保證分組的先后順序
- 不能進行分組出錯的恢復和重傳
- 不能抱枕數據傳輸的可靠性
總結:TCP是傳輸控制協議,面向連接的打電話的抽象模型,出錯重傳,比較安全可靠,每次收到協議就會回應給ACK,UDP是用戶協議包協議出錯不重傳,相對不可靠w。
2.2 字節序
大端和小端:
- 大端:低位存高地址,高位存低地址(服務器,網絡端)
- 小端:地位存低地址,高位存高地址(電腦)
為了保證傳輸的正確,發送轉成網絡字節序,收就轉主機序列
?2.3 網絡編程
確定電腦是大小端的代碼:
#include <stdio>typedef union stf {unsigned short a;unsighef char b[2]; }STD; int main() {STD tmp;tmp.a = 0x0102;if (tmp.b[0] == 0x01){printf('big');}else{printf('small');}return 0; }?2.4 網絡字節序轉API
字節序轉換函數:
htonl(host_to_online)?將主機數轉為網路段(大段) 32位
htons?將主機數轉為網路字節序數據(大段) 16位
ntohl?將32位網絡字節序數據轉換成主機字節序數據(大段) 16位
ntohs?將16位網絡字節序數據轉換成主機字節序數據
#include <stdio.h> #include <arpa/inet.h> int main() {int num = 0x01020304; //小端int sum = htonl(num);short b = htons(a);return 0; }? ?
2.5 地址轉換函數
inet_pton?字符串ip地址轉整型數據
int inet_pton(int family,const char *strptr, void *addrptr);- 功能:將點分十進制數串轉換成32位無符號整數
- 參數:family:協議族? ?|? strptr:點分十進制數串 |addrptr:32位無符號整數的地址
inet_ntop?字符串ip地址轉整型數據
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len); #include<stdio.h> #include<arpa/inet.h> int main() {unsigned char ip[]={10,0,13,252};char ip_str[16];inet_ntop(AF_INET,(unsigned int *)ip,ip_str,16);printf("ip_str = %s\n",ip_str);return 0; }output:10.0.13.252
2.6?網絡編程接口 socket
網絡通信要解決的是不同主機進程間的通信:
?socket?套接字解決(socket只能成對出現) —>socket pair 底層就是緩存區
?
?
2.7?網絡編程接口 socket
?對于UDP來說可以么有服務器,服務器只是認為規定的一種,主動發送的一方為客戶端,被動接受的一方為服務器。如果作為服務器被動等待別人發送數據,這個服務器需要綁定上固定的IP+端口。
3. UD?
流程:創建套接字->綁定端口
數據自上而下傳輸的過程,首先一開始通常是我們的客戶端主動連接服務器,?
自己的端口號+別人的端口號+報頭和內容長度+校驗和驗證報文是否有問題+內容
3.1 代碼編寫
udp.h
#pragma once #include "SocketInit.h" #include "TcpEvent.h"class Udp {Udp();~Udp(); public:static Udp& GetInstance();void SetBindAddress(const char* ip = "127.0.0.1", int port = 0);void SetRemoteAddress(const char* ip, int port);bool Init(bool setBroadCast);void Close();int SendTo(TcpEvent* tcpEvent);int RecvFrom(TcpEvent* tcpEvent);bool ZipSendTo(TcpEvent* tcpEvent);bool ZipRecvFrom(TcpEvent* tcpEvent);private:static Udp m_Instance;SOCKET m_Socket;sockaddr_in m_BindAddress;sockaddr_in m_RemoteAddress;int m_RemoteAddressLen; };udp.cpp
#include "Udp.h" #include "Logger.h" #include "zip/zlib.h" #include <stdio.h> #include <iostream>using namespace std;Udp Udp::m_Instance;Udp::Udp() {m_Socket = INVALID_SOCKET;memset(&m_BindAddress, 0, sizeof(m_BindAddress));memset(&m_RemoteAddress, 0, sizeof(m_BindAddress));m_RemoteAddressLen = sizeof(SOCKADDR); } Udp::~Udp() {closesocket(m_Socket); } Udp& Udp::GetInstance() {return m_Instance; } void Udp::SetBindAddress(const char* ip, int port) {m_BindAddress.sin_family = AF_INET;m_BindAddress.sin_addr.S_un.S_addr = inet_addr(ip);m_BindAddress.sin_port = htons(port);WRITE_LOG(LogLevel::Info, "Udp SetBindAddress IP[%s], Port[%d]", ip, port); } void Udp::SetRemoteAddress(const char* ip, int port) {m_RemoteAddress.sin_family = AF_INET;m_RemoteAddress.sin_addr.S_un.S_addr = inet_addr(ip);m_RemoteAddress.sin_port = htons(port);m_RemoteAddressLen = sizeof(m_RemoteAddress);WRITE_LOG(LogLevel::Info, "Udp SetRemoteAddress IP[%s], Port[%d]", ip, port); } bool Udp::Init(bool setBroadCast) {m_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (m_Socket == INVALID_SOCKET){WRITE_LOG(LogLevel::Error, "Create Udp Socket Failed.");Close();return false;}if (setBroadCast){BOOL bBroadcast = TRUE;if (::setsockopt(m_Socket, SOL_SOCKET, SO_BROADCAST, (char*)&bBroadcast, sizeof(BOOL)) == SOCKET_ERROR){WRITE_LOG(LogLevel::Error, "setsockopt SO_BROADCAST Failed.");Close();return false;}}auto ret = bind(m_Socket, (sockaddr*)&m_BindAddress, sizeof(SOCKADDR));if (ret == SOCKET_ERROR){WRITE_LOG(LogLevel::Error, "bind Failed. ret[%d]", ret);Close();return false;}WRITE_LOG(LogLevel::Info, "Udp Init Successed.");return true; } void Udp::Close() {closesocket(m_Socket);m_Socket = INVALID_SOCKET; }int Udp::SendTo(TcpEvent* tcpEvent) {auto sendLen = ::sendto(m_Socket, tcpEvent->ReadPos, tcpEvent->Length, 0, (sockaddr*)&m_RemoteAddress, sizeof(m_RemoteAddress));WRITE_LOG(LogLevel::Info, "Udp SendTo:[%s:%u], Len:[%d], Ret:[%d], Buff:[%s]", inet_ntoa(m_RemoteAddress.sin_addr), ntohs(m_RemoteAddress.sin_port), tcpEvent->Length, sendLen, (unsigned char*)tcpEvent->Buff);cout << "[";for (auto i = 0; i < sendLen; i++){printf("%02x", (unsigned char)tcpEvent->ReadPos[i]);}cout <<"]" << endl;return sendLen; } int Udp::RecvFrom(TcpEvent* tcpEvent) {int recvLen = recvfrom(m_Socket, tcpEvent->Buff, BuffSize - 1, 0, (SOCKADDR*)&m_RemoteAddress, &m_RemoteAddressLen);WRITE_LOG(LogLevel::Info, "Udp RecvFrom:[%s:%u], recvLen:[%d]", inet_ntoa(m_RemoteAddress.sin_addr), ntohs(m_RemoteAddress.sin_port), recvLen);tcpEvent->Length = recvLen;cout << "[";for (auto i = 0; i < recvLen; i++){printf("%02x", (unsigned char)tcpEvent->ReadPos[i]);}cout << "]" << endl;return recvLen; } bool Udp::ZipSendTo(TcpEvent* tcpEvent) {WRITE_LOG(LogLevel::Info, "Udp ZipSendTo: Len:[%d], Buff:[%s]", tcpEvent->Length, tcpEvent->ReadPos);TcpEvent* tcpEvent2 = TcpEvent::Allocate();unsigned long destLen = BuffSize;auto ret = compress2((unsigned char*)tcpEvent2->Buff, &destLen, (unsigned char*)tcpEvent->Buff, tcpEvent->Length, Z_BEST_SPEED);auto result = true;if (ret != Z_OK){WRITE_LOG(LogLevel::Error, "compress Failed. ret:[%d]", ret);result = false;}else{tcpEvent2->Length = destLen;result = SendTo(tcpEvent2) > 0;}tcpEvent2->Free();return result; } bool Udp::ZipRecvFrom(TcpEvent* tcpEvent) {TcpEvent* tcpEvent2 = TcpEvent::Allocate();auto result = true;int len = RecvFrom(tcpEvent2);if (len <= 0){tcpEvent2->Free();return false;}unsigned long sourceLen = len;unsigned long destLen = BuffSize - 1;auto ret = uncompress2((unsigned char*)tcpEvent->Buff, &destLen, (const unsigned char*)tcpEvent2->Buff, &sourceLen);if (ret != Z_OK){WRITE_LOG(LogLevel::Error, "compress Failed. ret:[%d]", ret);result = false;}else{tcpEvent->Length += destLen;tcpEvent->Buff[tcpEvent->Length] = '\0';WRITE_LOG(LogLevel::Info, "Udp ZipRecvFrom: Len:[%d], Buff:[%s]", tcpEvent->Length, tcpEvent->ReadPos);}tcpEvent2->Free();return ret; }UdpCLient:
#include "Logger.h" #include "Udp.h" #include <iostream>using namespace std;void UdpTest() {Udp::GetInstance().SetBindAddress();Udp::GetInstance().SetRemoteAddress("127.0.0.1", 6000);Udp::GetInstance().Init(true);while (true){TcpEvent* tcpEvent = TcpEvent::Allocate();cin >> tcpEvent->Buff;tcpEvent->Length = strlen(tcpEvent->Buff) + 1;Udp::GetInstance().ZipSendTo(tcpEvent);tcpEvent->Free();} }int main(int argc, char* argv[]) {Logger::GetInstance().Init(argv[0]);Logger::GetInstance().Start();UdpTest();Logger::GetInstance().Stop();Logger::GetInstance().Join();return 0; }UdpServer:
#include "Logger.h" #include "Udp.h"using namespace std;void UdpTest() {Udp::GetInstance().SetBindAddress("127.0.0.1", 6000);if (!Udp::GetInstance().Init(false)){return;}while (true){TcpEvent* tcpEvent = TcpEvent::Allocate();Udp::GetInstance().ZipRecvFrom(tcpEvent);tcpEvent->Free();} }int main(int argc, char* argv[]) {Logger::GetInstance().Init(argv[0]);Logger::GetInstance().Start();UdpTest();Logger::GetInstance().Stop();Logger::GetInstance().Join();return 0; }3.2? 創建套接字
創建UDP套接字demo:
#include <sys/socket.h> int sockfd = 0; sockfd = socket(AF_INET,SOCK_DGRAM,0); if(sockfd < 0) {perror("socket");exit(-1); }IPv4套接字地址結構:
#include <netinet/in.h> struct in_addr {in_addr_t s_addr;//4字節 }; struct sockaddr_in {{perror("socket");exit(-1); }注意: AF_INET:IPv4協議 SOCK_DGRAM:數據報套接字 0:選擇所給定的family和type組合的系統默認值2.4 UDP編程-發送、綁定、接收數據 2.4.1 IPv4套接字地址結構頭文件:#include <netinet/in.h> struct in_addr {in_addr_t s_addr;//4字節 }; struct sockaddr_in {sa_family_t sin_family;//2字節in_port_t sin_port;//2字節struct in_addr sin_addr;//4字節char sin_zero[8]//8字節 };3.3 TFTP簡介、通信過程
簡單文件傳輸協議(一般用于局域網,被用于設計小文件),基于UDP,不會對用戶進行有效驗證。
傳輸格式:
- octet:二進制模式
- netascii:文本模式
提供給客戶端上傳和下載文件的功能。默認端口:69。
? ? ?
?
記住:會創建一個新的套接字哈
- 下載:每次收到文件 ——>客戶端必須要發ACK(N) ——>不發繼續發 ——>重復6次——>當數據小于512則代表是最后一次發送
- 上傳:在讀寫作碼上改成下載請求
3.4?UDP廣播
發送的報文在局域網中所有的主機無條件接受,廣播不能跑到另一個局域網。路由器會對廣播進行封殺,只能UDP或者IP不能用TCP。
- 地址解析協議(ARP)
- 動態主機配置協議(DHCP)
- 網絡時間協議(NTP)
特點:
?
?單撥:會一對一找地址
廣播:不會,全部無條件接受
總結
以上是生活随笔為你收集整理的【草履虫10天学会C++网络编程】 UDP的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python高级网络编程_Python高
- 下一篇: css打字机