对 UDP(User Datagram Protocol 用户数据报协议)它属于传输层协议,是无连接的 不可靠的 面向数据报的,应用:用于传输实时性高于安全性的场景--例如:视频传输
网络中通信的两端主机:客户端 服务端
客户端:是通信中主动发起请求的一端
服务端:是通信中针对请求提供服务的一端,也是被动接收请求的一端
服务端通常需要提前告知客户端自己的地址信息,并且也不能随意改变
udp通信编程流程
服务端
- 创建套接字:在内核中开辟一个socket结构体
- 绑定地址信息:给创建的socket结构体指定源端IP和源端端口 发送数据时指定源端地址信息;告诉系统收到的哪条数据应该交给这个socket
- 接收数据:从对应的socket结构体的接收缓冲区中取出数据
- 发送数据:将数据放到指定的socket发送缓冲区中
- 关闭套接字:释放socket结构体资源
客户端
- 创建套接字
- 为套接字绑定地址信息 : 客户端通常不主动绑定地址 ,操作系统会根据系统资源自动分配一个未使用的端口进行地址绑定
- 接收数据:从对应的socket结构体的接收缓冲区中取出数据
- 发送数据:将数据放到指定的socket发送缓冲区中
- 关闭套接字:释放socket结构体资源
接口介绍
1.创建套接字
int socket(int domain, int type, int protocol);
domain:地址域类型 :AF_INET -ipv4的地址类域型
AF_INET6 -ipv6的地址域类型
AF_UNIX--struct sockaddr_un;
AF_INET--struct sockaddr_in
AF_INET6 --struct sockaddr_in6
type:套接字类型
SOCK_STREAM:流式套接字 提供字节流传输 默认TCP协议
SOCK_DGRAM:数据报套接字 提供数据报传输 默认UDP协议protocol:协议所使用的协议类型;0-套接字类型默认协议
IPPROTO_TCP:tcp协议
IPPROTO_UDP:UDP协议
返回值:成功返回一个文件描述符--操作句柄;失败返回-1
2.为套接字绑定地址信息
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd:创建套接字返回值
addr:struct sockaddr通用地址协议
len:指定地质结构长度
返回值:成功返回0 失败返回-1;
3.发送数据
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
sockfd:套接字描述符 buf:发送的数据首地址
len:发送的数据长度 flag:标志位 默认为0-表示阻塞发送
dest_addr:指定对端的地址信息 addrlen:地址信息长度
返回值:成功返回实际发送的数据长度 失败返回-1
4.接收数据
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
sockfd:套接字描述符 buf 空间地址 用于存放接收的数据
len:要获取的数据长度 flag:0-默认阻塞接收
src_addr:获取发送端的地址信息
addrlen:输入输出参数--指定想要获取多长的地址 返回实际的地址长度
返回值:返回实际接受到的数据长度 失败返回-1;
5.关闭套接字
int close(int fd);
字节序转换接口
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
字符串点分十进制ip地址转换为网络字节序整数IP地址
in_addr_t inet_addr(const char *cp); //只能转换ipv4的地址
int inet_pton(int af, const char *src, void *dst);//ipv6和ipv4都可以
网络字节序整数转换为字符串点分十进制
char *inet_ntoa(struct in_addr in);//只能转换ipv4
const char *inet_ntop(int af,const void *src,char *dst,socklen_t size);ipv4和ipv6都可以转换
基于UDP协议实现客户端、服务端聊天程序
封装的公共头文件
#include <iostream>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
using namespace std;
#define CHECK_RET(q) if((q)==false){return -1;}
class UdpSocket
{
public:
UdpSocket() : _sockfd(-1) {}
bool Socket()
{
_sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (_sockfd < 0)
{
perror("socket error");
return false;
}
return true;
}
void Addr(sockaddr_in *addr, const string &ip, const uint16_t port)
{
addr->sin_port = htons(port);
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(ip.c_str());
}
bool Bind(const string &ip, const uint16_t port)
{
sockaddr_in addr;
Addr(&addr, ip, port);
int ret = bind(_sockfd, (sockaddr *)&addr, sizeof(sockaddr_in));
if (ret < 0)
{
perror("bind error");
return false;
}
return true;
}
bool Sendto(const string &str, const string &ip, uint16_t port)
{
sockaddr_in addr;
Addr(&addr, ip, port);
int ret = sendto(_sockfd, str.c_str(), str.size(), 0, (sockaddr *)&addr, sizeof(sockaddr_in));
if (ret < 0)
{
perror("sendto error");
return false;
}
return true;
}
bool Recvfrom(string *buf, string *ip = nullptr, uint16_t *port = nullptr)
{
sockaddr_in addr;
socklen_t len = sizeof(sockaddr_in);
char tmp[4096] = {0};
int ret = recvfrom(_sockfd, tmp, 4096, 0, (sockaddr *)&addr, &len);
if (ret < 0)
{
perror("recvfrom error");
return false;
}
if (ip != nullptr)
*ip = inet_ntoa(addr.sin_addr);
if (port != nullptr)
*port = ntohs(addr.sin_port);
buf->assign(tmp,ret);
return true;
}
bool Close()
{
if (_sockfd != -1)
close(_sockfd);
return true;
}
private:
int _sockfd;
};
服务端
#include "udpsocket.hpp"
int main()
{
UdpSocket server;
CHECK_RET(server.Socket());
CHECK_RET(server.Bind("10.206.0.2", 9000));
while (1)
{
string str;
string ip;
uint16_t port;
CHECK_RET(server.Recvfrom(&str, &ip, &port));
cout << "client-[" << ip << ":" << port << "] say:" << str << endl;
cout << "server say:";
str.clear();
cin >> str;
CHECK_RET(server.Sendto(str, ip, port));
}
server.Close();
return 0;
}
客户端
#include "udpsocket.hpp"
int main()
{
UdpSocket client;
CHECK_RET(client.Socket());
while (1)
{
string str;
cout << "client say:";
cin >> str;
CHECK_RET(client.Sendto(str, "10.206.0.2", 9000));
str.clear();
CHECK_RET(client.Recvfrom(&str));
cout << "client say:" << str << endl;
}
client.Close();
return 0;
}