网络套接字编程(UDP协议)
创始人
2024-02-28 15:20:29

文章目录

    • 预备知识
    • socket(网络套接字)编程接口
    • 简单的UDP网络程序
    • 增加多用户可以互相通信

预备知识

网络字节序
大端存储:数据的高字节内容保存在内存的低地址处,数据的低字节内容保存在内存的高地址处
小端存储:数据的高字节内容保存在内存的高地址处,数据的低字节内容保存在内存的低地址处

内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏
移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.

不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;

如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可

网络字节序和主机字节序的转换

#include uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;

如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

socket(网络套接字)编程接口

// 创建 socket 文件描述符 
int socket(int domain, int type, int protocol);// 绑定端口号 
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

sockaddr结构
socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,然而, 各种网络协议的地址格式并不相同.

在这里插入图片描述

简单的UDP网络程序

socket函数

int socket(int domain, int type, int protocol);

参数说明:

domain:创建套接字的域或者叫做协议家族,也就是创建套接字的类型。该参数就相当于struct sockaddr结构的前16个位。如果是本地通信就设置为AF_UNIX,如果是网络通信就设置为AF_INET(IPv4)或AF_INET6(IPv6)。

type:创建套接字时所需的服务类型。其中最常见的服务类型是SOCK_STREAM和SOCK_DGRAM,如果是基于UDP的网络通信,我们采用的就是SOCK_DGRAM,叫做用户数据报服务,如果是基于TCP的网络通信,我们采用的就是SOCK_STREAM,叫做流式套接字,提供的是流式服务。

protocol:创建套接字的协议类别。你可以指明为TCP或UDP,但该字段一般直接设置为0就可以了,设置为0表示的就是默认,此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数说明:

sockfd:创建套接字返回的文件描述符
addr:网络相关的属性
传入的addr的长度

struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port;			/* Port number.  */struct in_addr sin_addr;		/* Internet address.  *//* Pad to size of `struct sockaddr'.  */unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];};

sin_port:表示端口号
sin_family:表示协议家族
sin_addr:表示IP地址

字符串转in_addr

in_addr_t inet_addr(const char *cp);

服务器绑定

class udpServer
{udpServer(uint16_t port, string ip = "") : _port(port), _ip(ip){}void init(){//1. 创建套接字_sockfd = socket(AF_INET,SOCK_DGRAM,0);if(_sockfd < 0){logmessage("error");exit(1);}logmessage("socket create sucessfully");// 2. 创建ip与端口号struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family = AF_INET;local.sin_port=htons(_port);// INADDR_ANY表示让服务器端计算机上的全部网卡的IP地址均可以做为服务器IP地址local.sin_addr.s_addr =  _ip.empty() ?  htonl(INADDR_ANY)  :inet_addr(_ip.c_str());socklen_t len = sizeof(local);//3.连接if(bind(_sockfd,(struct sockaddr*)&local,len) == -1) {logmessage("bind error");exit(2);}logmessage("bind sucessfully");}
private:int _sockfd; //套接字uint16_t _port;  // 端口号string _ip; // ip地址
};

服务器读取用户发来的数据

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 
struct sockaddr *src_addr, socklen_t *addrlen);

参数说明:

sockfd:对应操作的文件描述符。表示从该文件描述符索引的文件当中读取数据。
buf:读取数据的存放位置。
len:期望读取数据的字节数。
flags:读取的方式。一般设置为0,表示阻塞读取。
src_addr:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
addrlen:调用时传入期望读取的src_addr结构体的长度,返回时代表实际读取到的src_addr结构体的长度,这是一个输入输出型参数。

服务器接收用户信息

message.hpp

struct message
{/* data */message(){_time=(uint64_t)time(nullptr);}friend ostream& operator<<(ostream& out,const message& mess);char _name[10];char _message[1024];uint64_t _time;
};ostream& operator<<(ostream& out,const message& mess)
{out<<"时间:"<
void start()
{message mess;struct sockaddr_in client;socklen_t len = sizeof(client);memset(&client,0,sizeof(client));while(1){ssize_t s = recvfrom(_sockfd,&mess,sizeof(mess),0,(struct sockaddr*)&client,&len);if(s < 0){logmessage("recvfrom error");continue;}logmessage("recvfrom sucess");cout<

客户端创建套接字

class udpClient
{public:udpClient(const string &serip, uint16_t serport) : _serip(serip), _serport(serport){//创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);}private:int _sockfd; // 套接字string _serip;     // 服务器ipuint16_t _serport; // 服务器port
};

为什么客户端不需要绑定
所谓的"不需要",指的是: 不需要用户自己bind端口信息!因为OS会自动给你绑定,推荐这么做

sendto函数

ssize_t 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:对端网络相关的属性信息,包括协议家族、IP地址、端口号等。
addrlen:传入dest_addr结构体的长度。

客户端发送消息

void communicate()
{message mess;struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(_serport);server.sin_addr.s_addr = inet_addr(_serip.c_str());cout<<"请输入你的名字"<>mess._name;while (1){cout<<"请输入你要发的消息"<>mess._message;sendto(_sockfd, &mess, sizeof(mess), 0, (struct sockaddr *)&server, sizeof(server));}
}

效果:在这里插入图片描述

增加多用户可以互相通信

class udpServer
{
public:void start(){message mess;struct sockaddr_in client;socklen_t len = sizeof(client);memset(&client,0,sizeof(client));while(1){ssize_t s = recvfrom(_sockfd,&mess,sizeof(mess),0,(struct sockaddr*)&client,&len);if(s < 0){logmessage("recvfrom error");continue;}logmessage("recvfrom sucess");cout<ip_port,client});}}
private:
void messageRoutine(const message& mess){for(auto user:users){sendto(_sockfd,&mess,sizeof(mess),0,(struct sockaddr*)&(user.second),sizeof(user.second));}}private:int _sockfd; //套接字uint16_t _port;  // 端口号string _ip; // ip地址unordered_map users; // 所有的用户
};
class udpClient
{public:udpClient(const string &serip, uint16_t serport) : _serip(serip), _serport(serport){//创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);}void communicate(){pthread_t t;pthread_create(&t,nullptr,ReadMessage,(void*)&_sockfd);pthread_detach(t);message mess;struct sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(_serport);server.sin_addr.s_addr = inet_addr(_serip.c_str());cout<<"请输入你的名字"<>mess._name;cout<<"开始通信"<cin>>mess._message;sendto(_sockfd, &mess, sizeof(mess), 0, (struct sockaddr *)&server, sizeof(server));}}~udpClient(){}
private:
static void* ReadMessage(void* args)
{int socket = *(int*)args;message mess;struct sockaddr_in tmp;socklen_t len=sizeof(tmp);while(1){recvfrom(socket,&mess,sizeof(mess),0,(sockaddr*)&tmp,&len);cout<

效果:
在这里插入图片描述
在这里插入图片描述

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...