UDP
1.含義:udp協議是傳輸層的一種協議。(User Datagram Protocol用戶數據報協議)
2.特點:無連接、不可靠、面向數據報。
無連接:不用向服務端建立連接
不可靠:數據傳輸的過程是一個不可靠的--數據可能會丟失
面向數據報:數據報的傳輸方式
3.流程:
4.實現:
(1)首先封裝一個UdpSocket類來實現其基本的功能
//傳輸層基於UDP協議的網絡通信
// 1.創建套接字
// 2.綁定地址信息
// 3.發送數據
// 4.接收數據
// 5.關閉套接字
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string>
using namespace std;
#define CHECK_RET(q) if((q) == false){return -1;}
class UdpSocket{
public:
//將_sockfd的初始值給-1,
UdpSocket():_sockfd(-1){}
//1.創建套接字
bool Socket(){
//socket(int domain, int type, int proto);
// domain:地址域的類型--ipv4/ipv6
// type :套接字類型--數據報/字節流
// proto :協議的類型--udp/tcp
// 返回值:
// 成功--0;失敗--<-1;
_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(_sockfd < 0){
cerr << "socket error\n";
return false;
}
return true;
}
//2.綁定地址信息
bool Bind(string& ip, u_int16_t port){
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(&ip[0]);
addr.sin_port = htons(port);
socklen_t len = sizeof(addr);
//bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
// sockfd :套接字描述符
// addr :地址信息
// addrlen :地址結構的大小--字節
// 返回值:
// 成功--0; 失敗--》-1;
int ret = bind(_sockfd, (sockaddr*)&addr, len);
if(ret < 0){
cerr << "bind error\n";
return false;
}
return true;
}
//3.發送數據
bool Send(string& buf, string& ip, u_int16_t& port){
//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 :目的端地址
// addrlen :地址大小
// 返回值:
// 成功--返回已發送的字符數目;失敗--》-1
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(&ip[0]);
addr.sin_port = htons(port);
socklen_t len = sizeof(addr);
int ret = sendto(_sockfd, &buf[0], buf.size(), 0, (sockaddr*)&addr, len);
if(ret < 0){
cerr << "send error\n";
return false;
}
return true;
}
//4.接收數據
bool Recv(string& buf, string& ip, uint16_t& port){
//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 :地址大小;源端地址非空--就是源端地址的大小;源端地址空--他就是NULL
// 返回值:
// > 0 :接收到的字節數
// ==0 :表示連接已經斷開
// -1 :接收錯誤
char tmp[1024] = {0};
sockaddr_in addr;
socklen_t len = sizeof(addr);
int ret = recvfrom(_sockfd, tmp, 1024, 0, (sockaddr*)&addr, &len);
if(ret < 0 ){
cerr << "recvfrom error\n";
return false;
}else if(ret == 0){
cout << "peer shutdown\n";
return false;
}
ip = inet_ntoa(addr.sin_addr);
port = ntohs(addr.sin_port);
buf.assign(tmp,ret);
return true;
}
//5.關閉套接字
bool Close(){
if(_sockfd < 0){
cerr << "close error\n";
return false;
}
close(_sockfd);
return true;
}
~UdpSocket(){
Close();
}
private:
int _sockfd; //套接字描述符--文件描述符(網絡通信的操作句柄)
};
(2)服務端程序
//傳輸層基於UDP協議的服務端程序
// 1.創建套接字
// 2.綁定地址信息
// 3.接收客戶端發送的數據
// 4.發送數據
// 5.關閉套接字
#include "udp_socket.hpp"
int main(int argc, char* argv[]){
//判斷參數是否足夠
if(argc != 3){
cout << "./udp_srv 192.168.136.146 9000\n";
return -1;
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
UdpSocket sock;
//1.創建套接字
CHECK_RET(sock.Socket());
//2.綁定地址信息
CHECK_RET(sock.Bind(ip, port));
while(1){
//3.循環收發數據
string buf;
CHECK_RET(sock.Recv(buf, ip, port));
cout << "client say:" << buf << endl;
buf.clear();
cin >> buf;
//4.發送數據
CHECK_RET(sock.Send(buf, ip, port));
}
sock.Close();
return 0;
}
(3)客戶端程序
//傳輸層基於UDP協議的客戶端
#include "udp_socket.hpp"
int main(int argc, char* argv[]){
//判斷參數是否足夠
if(argc != 3){
cout << "./udp_cli serverip serverport";
return -1;
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
UdpSocket sock;
//1.創建套接字
CHECK_RET(sock.Socket());
//2.綁定地址信息--可以不用手動綁定
//CHECK_RET(sock.Bind("192.168.136.148", 8000));
//進入循環收發數據
while(1){
string buf;
cin >> buf;
CHECK_RET(sock.Send(buf, ip, port));
buf.clear();
//buf.clear(); //發完以後緩衝區需要清空嗎????????
CHECK_RET(sock.Recv(buf, ip, port));
cout << "server say:" << buf << endl;
}
sock.Close();
return 0;
}
5.UDP協議的報頭信息
16bitUDP長度:報頭+數據;也就是說udp所能發送的數據長度最大是64k-8(2^16 - 8);
16位的校驗和:int sum = 0;sum +=~data[i];