您的位置:首页 > 科技 > IT业 > 云应用开发_建个视频网站多少钱_店铺推广软文案例_市场调研报告800字

云应用开发_建个视频网站多少钱_店铺推广软文案例_市场调研报告800字

2025/3/12 13:25:57 来源:https://blog.csdn.net/newbie5277/article/details/146137790  浏览:    关键词:云应用开发_建个视频网站多少钱_店铺推广软文案例_市场调研报告800字
云应用开发_建个视频网站多少钱_店铺推广软文案例_市场调研报告800字

目录

UDP网络通信

简单的udpServer实现

头文件

封装udp服务

1、私有成员

 2、构造函数和析构函数

3、 udp服务器初始化

4、udp服务器运行

5、完整代码

简单的udpClient实现

UDP实现最简单的公共聊天

 完整代码 

inet_ntoa()相关问题 


UDP网络通信

在正式实现UDP网络通信前,我们先实现一个打印日志内容的文件。

#pragma once#include<cstdio>
#include<ctime>
#include<cassert>
#include<cerrno>
#include<cstdlib>
#include<cstdarg>#define DEBUG 0
#define NOTICE 1
#define WARINING 2
#define FATAL 3const char* log_level[]={"DEBUG","NOTICE","WARINING","FATAL"};
void logMessage(int level,const char* format,...)
{assert(level>=DEBUG);assert(level<=FATAL);char* name=getenv("USER");char logBuffer[1024];va_list ap;va_start(ap,format);vsnprintf(logBuffer,sizeof(logBuffer)-1,format,ap);va_end(ap);FILE* out=(level==FATAL)? stderr:stdout;time_t tm = time(nullptr);struct tm* localTm=localtime(&tm);char timeStr[26]; // 考虑到asctime函数返回的字符串自带换行符,为了打印出来好看,下面手动去除这个换行符snprintf(timeStr, sizeof(timeStr), "%s", asctime(localTm));timeStr[strcspn(timeStr, "\n")] = 0; // Remove trailing newlinefprintf(out,"%s | %s | %s | %s\n",log_level[level],timeStr,name==nullptr?"unknow":name,logBuffer);
}

1、上面运用了可变参数的知识点,简单介绍一下可变参数的使用

  • 首先,需要包含<stdarg.h>(C语言)或<cstdarg>(C++)头文件。
  • 使用 va_list 类型声明一个变量来存储参数信息。
  • 调用 va_start 宏初始化上述变量,使其指向可变参数列表中的第一个参数。
  • 使用 va_arg 宏访问参数。
  • 最后调用 va_end 宏完成清理工作。

2、vsnprintf()

   它用于格式化字符串输出,并允许传递一个 va_list 类型的参数来支持可变数量的参数。

int vsnprintf(char *str, size_t size, const char *format, va_list ap);
  • str: 指向用于存储生成字符串的缓冲区。
  • size: 缓冲区str的大小(以字符为单位)。如果生成的字符串长度(包括终止的空字符)超过这个值,则会被截断。
  • format: 格式化字符串,指定如何转换参数并格式化到输出字符串中。
  • ap: 一个va_list类型的对象,包含了要使用的所有参数的信息

3、获取当前时间

time_t tm = time(nullptr);
struct tm* localTm=localtime(&tm);

第一行代码调用了 time 函数,该函数返回自纪元(1970年1月1日00:00:00 UTC)以来的秒数,并存储在 time_t 类型的变量 tm 中。

第二行使用 localtime函数将 time_t 类型的值转换为本地时间表示结果是一个指向 struct tm类型的指针 localTm。struct tm 结构体包含了年、月、日、时、分、秒等详细的时间信息。


下面就开始实现一个简单的UDP网络通信

简单的udpServer实现

实现简单的udpServer的功能

  1. 创建套接字:首先,服务端需要创建一个UDP套接字。
  2. 绑定地址和端口:然后,将该套接字绑定到特定的IP地址和端口号上,以便客户端能够发送数据到这个地址和端口。
  3. 接收数据:使用 recvfrom 函数来接收来自客户端的数据。此函数不仅可以接收数据,还可以获取发送方的地址信息。
  4. 发送响应:如有必要,可以使用 sendto 函数向客户端发送响应(这里暂时不响应)。
头文件
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
封装udp服务
1、私有成员

在进行网络通信时我们需要的是 端口号、IP还有 套接字 信息 。

private:uint16_t _port;string _ip;int _sockFd;

 2、构造函数和析构函数
udpServer(uint16_t port,string ip=""):_port(port),_ip(ip){}
~udpServer(){}

 这里需要传入的参数是端口号和IP地址。

传入的IP地址可以默认为空,即当创建这个UDP服务器对象时,如果没有提供第二个参数(IP地址),那么_ip 成员变量将被初始化为空字符串。这是因为如果用户没有特别指出要监听哪个IP地址,那就默认监听所有的网络接口.换句话说,它会监听该主机上所有的IP地址,无论数据包到达哪个网络接口,只要目标端口匹配,你的服务就能接收到这些数据包。

同一台主机可以拥有多个IP地址。

  • 多个网络接口:如果一台计算机配置了多个网络接口(例如,一个以太网卡和一个无线网卡),每个接口都可以被分配一个独立的IP地址。
  • 子接口(VLANs):在一些情况下,单个物理网络接口可以通过虚拟局域网(VLAN)技术划分成多个逻辑子接口,每个子接口可以配置不同的IP地址。
  • IPv6与IPv4共存:现代操作系统支持同时配置IPv4和IPv6地址。这意味着即使只有一个网络接口,它也可能拥有至少两个IP地址——一个IPv4地址和一个IPv6地址。
  • 别名接口:某些操作系统允许给单一的网络接口配置多个IP地址,通过创建所谓的“别名”接口来实现。这对于需要托管多个网站或服务的服务器特别有用,因为每个服务可以绑定到不同的IP地址。
  • 动态分配的临时地址:在某些情况下,比如使用DHCP时,可能会为同一个接口动态分配额外的临时IP地址。

3、 udp服务器初始化
 void init(){_sockFd=socket(AF_INET,SOCK_DGRAM,0);//创建套接字if(_sockFd<0){logMessage(FATAL,"socket() fail::%s :%d",strerror(errno),_sockFd);exit(1);}logMessage(DEBUG,"socket creat success: %d", _sockFd);struct sockaddr_in local;//创建结构体对象bzero(&local,sizeof(local));//该函数的作用是将一段内存区域的内容设置为0,这里是将结构体內容全置空。local.sin_family=AF_INET;//地址类型,选择协议local.sin_port=htons(_port);//选择端口号,端口号是要发送到网络的,所以需要从本地字节序转成网络字节序local.sin_addr.s_addr=_ip.empty()?htonl(INADDR_ANY):inet_addr(_ip.c_str());if(bind(_sockFd,(const struct sockaddr*)&local,sizeof(local))==-1){//绑定失败logMessage(FATAL,"bind() failed::%s : %d",strerror(errno),_sockFd);exit(2);}//绑定成功logMessage(DEBUG,"socket bind success: %d",_sockFd);}
  •   local.sin_addr.s_addr=_ip.empty()?htonl(INADDR_ANY):inet_addr(_ip.c_str());

    下面是对于这一行代码的解释

  • IP 不能直接填充到结构体中, 因为类中的 _ip 是字符串,  而网络中的IP 通常用4字节的二进制表示, 结构体中同样是此类型 所以 还需要将 点分十进制字符串型的_ip 转换为 uint32_t 才能填充到 结构体中。
  • 判断ip地址是否为空,如果为空则传入INADDR_ANY结构体,否则就将 _ip字符串转换为 in_addr_t 类型, 然后填充到结构体中
  • INADDR_ANY 其实是 强转为 in_addr_t 类型的 0.
  •  网络服务器 使用 INADDR_ANY 作为IP, 绑定到主机中. 表示监听本机上所有的IP 网络接口.一台服务器主机可能有许多的IP, 使用 INADDR_ANY 意思就是说, 其他主机可以通过服务器主机上的 任意IP:指定port 找到服务器进程实现通信
  • 当绑定指定的IP时, 就表示 其他主机只能通过服务器主机上的 指定IP:指定port 找到服务器进程实现通信.
  • 如果使用其他本机上的IP:指定port, 服务器是不会响应的. 因为服务器进程 只接收通过指定 IP 发送给服务器进程的信息
  • IP也是要向网络中发送的, 所以要将 IP转换成网络字节序. inet_addr() 则会自动将ip转换为网络字节序 
  • 需要了解的是, inet_addr() 接口 可以将点分十进制字符串类型的IP地址, 转换为 in_addr_t 类型的IP

    同时, 也会 将转换后的IP自动转换为网络字节序存储形式


4、udp服务器运行

其具体功能就是循环地监听、接收发送到服务器上的信息

     void start(){char inBuffer[1024];//用于储存接收到的信息while(1){struct sockaddr_in peer;//输出型参数,用于接收对方的主机网络信息socklen_t peerlen=sizeof(peer);//输入输出型参数ssize_t s=recvfrom(_sockFd,inBuffer,sizeof(inBuffer)-1,0,(struct sockaddr*)&peer,&peerlen);//接收发送到服务器上的信息及发送端的网络信息if(s>0)//当字符串结尾{inBuffer[s]=0;}else if(s==-1)//读取失败{logMessage(WARINING,"recvfrom() error::%s: %d",strerror(errno),_sockFd);continue;}//读取成功,除了读取到发过来的信息,还要读到对方的网络信息string peerIP=inet_ntoa(peer.sin_addr);//拿到对方的ipuint32_t peerPort=ntohs(peer.sin_port);//拿到对方的端口号logMessage(NOTICE,"[%s:%d]# %s",peerIP.c_str(),peerPort,inBuffer);//将对方发送过来的信息打印出来}}    

 介绍一下接收接口 recvfrom

是用于接收通过套接字发送的数据的系统调用,特别适用于无连接的协议如UDP(用户数据报协议),或者在需要知道数据来源的情况下使用。它不仅可以接收数据,还能获取发送方的信息。

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: 调用操作的选项,通常设置为0。其他可能的值包括 MSG_PEEK(查看数据而不实际读取)、MSG_WAITALL(等待直到请求的数据量全部到达)等。
  • address: (可选)指向一个结构体的指针,该结构体用来保存源地址信息(即发送数据的主机的地址)。如果不需要这个信息,可以传递NULL。
  • address_len: (与address一起使用)在调用时,应设置为address所指向结构体的大小;返回时,包含实际写入的地址结构体的大小。

5、主函数

//main函数需要通过获取命令行参数实现获取端口号和ip
int main(int argc,char*argv[])
{if(argc != 2&& argc!=3){Usage(argv[0]);exit(3);}
//获取端口号和ipuint16_t port=atoi(argv[1]);string ip;if(argc==3){ip=argv[2];}
//使用端口号和ip实例化udpServer对象udpServer uSvr(port,ip);
//初始化并启动服务器uSvr.init();uSvr.start();return 0;
}

5、完整代码
#include<iostream>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cerrno>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>#include"logMessage.hpp"using std::cout;
using std::endl;
using std::string;class udpServer{
public:udpServer(uint16_t port,string ip=""):_port(port),_ip(ip){}~udpServer(){}//server init,creat socket,bind hostvoid init(){_sockFd=socket(AF_INET,SOCK_DGRAM,0);if(_sockFd<0){logMessage(FATAL,"socket() fail::%s :%d",strerror(errno),_sockFd);exit(1);}logMessage(DEBUG,"socket creat success: %d", _sockFd);struct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=_ip.empty()?htonl(INADDR_ANY):inet_addr(_ip.c_str());if(bind(_sockFd,(const struct sockaddr*)&local,sizeof(local))==-1){logMessage(FATAL,"bind() failed::%s : %d",strerror(errno),_sockFd);exit(2);}logMessage(DEBUG,"socket bind success: %d",_sockFd);}void start(){char inBuffer[1024];while(1){struct sockaddr_in peer;socklen_t peerlen=sizeof(peer);ssize_t s=recvfrom(_sockFd,inBuffer,sizeof(inBuffer)-1,0,(struct sockaddr*)&peer,&peerlen);if(s>0){inBuffer[s]=0;}else if(s==-1){logMessage(WARINING,"recvfrom() error::%s: %d",strerror(errno),_sockFd);continue;}string peerIP=inet_ntoa(peer.sin_addr);uint32_t peerPort=ntohs(peer.sin_port);logMessage(NOTICE,"[%s:%d]# %s",peerIP.c_str(),peerPort,inBuffer);}}    
private:uint16_t _port;string _ip;int _sockFd;
};
static void Usage(const string porc)//用于帮助用户正确了解特定的命令行程序,即在用户没有正确
//输入命令的情况下该函数作为提示
{cout<<"Usage:\n\t"<<porc<<"port [ip]"<<endl;
}int main(int argc,char*argv[])
{if(argc != 2&& argc!=3){Usage(argv[0]);exit(3);}//****main函数的参数在c和c++中主要用于接收命令行参数,这些参数默认是以字符串形式传递的。//****argv是一个指向字符串数组的指针,所以需要atoi函数来转换为整数uint16_t port=atoi(argv[1]);string ip;if(argc==3){ip=argv[2];}udpServer uSvr(port,ip);uSvr.init();uSvr.start();return 0;
}

运行结果

 查看网络信息

 这个简单的udpserver就已经完成了,只会接收信息,没有回复功能。

简单的udpClient实现

udp客户端需要什么功能呢?

  • 创建套接字:类似于服务端,客户端也需要创建一个UDP套接字。
  • 指定服务端地址:定义一个 sockaddr_in 结构体,设置服务端的IP地址和端口号。
  • 发送数据:使用 sendto 函数向服务端发送数据。
  • 接收响应:如果服务端会返回响应,则客户端可以用 recvfrom 接收响应。

实现了服务器端的代码,客户端这里也是大同小异,如下 

#include <cstdint>
#include <iostream>
#include <ostream>
#include <string>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "logMessage.hpp"using std::cout;
using std::cin;
using std::endl;
using std::getline;
using std::string;static void Usage(const string porc)
{cout<<"Usage::\n\t"<<porc<<"server_IP server_Port"<<endl;
}int main(int argc,char* argv[])//服务器的ip和端口号是从这里输入的
{if(argc!=3){Usage(argv[0]);exit(1);}获取服务器的ip和端口号string server_IP=argv[1];uint16_t server_Port=atoi(argv[2]);;//创建客户端套接字int sockFd=socket(AF_INET,SOCK_DGRAM,0);if(sockFd<0){logMessage(FATAL,"socket() failed:: %s :%d",strerror(errno),sockFd);exit(2);}logMessage(DEBUG,"socket() success:: %d",sockFd);//填充服务器的网络信息struct sockaddr_in server;bzero(&server,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(server_Port);server.sin_addr.s_addr=inet_addr(server_IP.c_str());//通信string inBuffer;while(true){cout<<"Please Enter >>";getline(cin,inBuffer);//向server发送消息从命令行接收到的服务器IP和端口号, 是需要填充在 sockaddr_in 结构体中的, 因为 向服务器网络进程发送信息需要使用sendto(sockFd,inBuffer.c_str(),inBuffer.size(),0,(const struct sockaddr*)&server,sizeof(server));// 在首次向 server 发送消息的时候, 操作系统会自动将Client网络进程信息 绑定到操作系统内核}close(sockFd);return 0;
}

需要注意的是:

      服务端的网络信息和绑定都需要手动去输入,但是客户端的最好不要我们自己去做, 让操作系统自动帮我们完成,因为我们不需要手动指定IP以及端口号, 尤其是端口号. 如果手动指定了端口号 还有可能会造成其他问题,
    服务器需要手动指定端口号, 是因为服务器是需要让其他主机去连接的, 所以知道且固定. 如果是随机的, 那服务器绝对没人用.
    而客户端一般没人会主动来连接、访问, 一般都是每次打开客户端绑定网络时, 就让操作系统代操作, 不然手动指定端口号 可能会影响其他的网络进程
    所以 我们不需要手动去填充 udpClient 的网络信息, 也不需要手动绑定。

运行结果


下面让客户端和服务端交互一下看看效果

 如图所示,可以进行正常的简单通信。


UDP实现最简单的公共聊天

在上面的部分中,我们只是简单实现了客户端发消息,服务端接收消息而已,服务端并没有消息回复功能。下面我们就在上面所完成的基础上做一些修改和添加,以实现相互通信的功能。

  • 服务器内,使用哈希表实现一个存储不同主机进程信息的 用户表
  • 让服务器收到消息之后可以转发给 用户表内的所有用户进程
  • 让客户端也可以 接收来自服务器的信息, 并输出

在udpServer.cc中添加了如下代码: 

void CheckOnlineUsers(string &ip,uint16_t port,struct sockaddr_in &peer){string key=ip;key += ":";key += std::to_string(port);auto itUser=_users.find(key);if(itUser==_users.end()){_users.insert({key,peer});}}void messageRount(string &ip ,uint16_t port,string info){string message="[";message += ip;message +=":";message +=std::to_string(port);message +="]";message +=info;for(auto &user : _users){sendto(_sockFd,message.c_str(),message.size(),0,(struct sockaddr*)&(user.second),sizeof(user.second));}}

在类中添加了一个成员变量 _users ,是一个储存用户网络信息的哈希表。

然后增加了上面的两个成员函数 

  • CheckOnlineUsers:用来检测 向服务器发送消息的客户端是否已经在服务器的用户表中. 如果不在, 则添加
  • messageRount:则是实现消息路由转发的功能. 服务器 接收到 某个客户端发来的消息之后, 会将客户端的信息(IP:Port) 以及发过来的消息, 传入此函数内. 然后 此函数整合信息和消息, 再将整合后的信息 转发给所有在服务器用户表中的客户端用户

在udpClient中添加了如下代码:

void* recverAndPrint(void* args)//函数接收一个 void* 类型的参数 args,这是一个指向整数类型的指针,实际上存储的是一个套接字描述符 (sockFd)。
{while(true){int sockFd=*(int*)args;//在每次循环迭代中,都将 args 转换为 int* 并解引用以获取套接字描述符。char buffer[1024];struct sockaddr_in temp;socklen_t templen=sizeof(temp);ssize_t s=recvfrom(sockFd,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&templen);if(s>0){buffer[s]=0;cout<<buffer<<endl;}}
}
pthread_t t;pthread_create(&t,nullptr,recverAndPrint,&sockFd);while(true){std::cerr<<"please enter>>";string inBuffer;inBuffer +="[";inBuffer +="nick_Name";inBuffer +="]# ";string tempS;getline(cin,tempS);inBuffer +=tempS;sendto(sockFd, inBuffer.c_str(), inBuffer.size(), 0,(const struct sockaddr*)&server, sizeof(server));close(sockFd);

 这部分多了一个线程执行函数recverAndPrint,是为了接收来自服务器的消息. 其实就是 接收所有人发送的消息.此函数需要多线程执行,

我们在 udpClient 代码中, 获取用户在命令行输入的内容的实现是用 getline(); 实现的.

是一个阻塞式的等待输入操作.

如果 recverAndPrint() 也在主线程内执行. 那么就会出现 只有用户输入完毕之后, 来自服务器的消息才能输出在客户端中,这样显然是不正确的. 所以使用多线程执行 recverAndPrint(). 主线程不干扰此线程.

为了方便展示、查看我们要知道

  • 标准输出 (stdout)这是程序默认的输出流,通常用于输出程序的结果或正常信息。可以通过命令行中的 > 操作符进行重定向。

  • 标准错误 (stderr)这是一个独立于标准输出的流,通常用于输出错误消息或其他诊断信息。即使标准输出被重定向,标准错误仍然会显示在终端上。

客户端接收到的服务器发来的信息. 演示时, 将 udpClient 的标准输出内容 重定向到了一个 命名管道文件中.

       并且, 为了将来自服务器的信息重定向到其中 并且不出现扰乱信息, 我们将 udpClient 中其他 部分输出 由 std::cout 标准输出 换成了 std::cerr 标准错误. 比如, 输入提示的部分:这样 可以避免将 输入提示符 也重定向到管道文件中. 因为 命令行中 > 是标准输出重定向。


 完整代码 

udpServer.cc:

#include<iostream>
#include<string>
#include<unordered_map>
#include<cassert>
#include<cstdlib>
#include<cerrno>
#include<unistd.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<cstdarg>
#include"logMessage.hpp"
using std::cout;
using std::endl;
using std::string;
using std::unordered_map;class udpServer
{public:udpServer(uint16_t port,string ip=""):_port(port),_ip(ip){} ~udpServer(){}void Init(){_sockFd=socket(AF_INET,SOCK_DGRAM,0);if(_sockFd<0){logMessage(FATAL,"socket() failed:: %s : %d",strerror(errno),_sockFd);exit(1);}logMessage(DEBUG,"socket() success:: %d",_sockFd);struct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=_ip.empty()?htonl(INADDR_ANY):inet_addr(_ip.c_str());if(bind(_sockFd,(const struct sockaddr*)&local,sizeof(local))==-1){logMessage(FATAL,"bind() failed::%s : %d",strerror(errno),_sockFd);exit(2);}logMessage(DEBUG,"bind() success::%d",_sockFd);}void start(){char serBuffer[1024];while(1){struct sockaddr_in peer;socklen_t peerlen=sizeof(peer);ssize_t s=recvfrom(_sockFd,serBuffer,sizeof(serBuffer)-1,0,(struct sockaddr*)&peer,&peerlen);if(s>0){serBuffer[s]=0;}else if(s==-1){logMessage(WARINING,"recvfrom()::%s : %d",strerror(errno),_sockFd);continue;}string peerIp=inet_ntoa(peer.sin_addr);uint16_t peerPort=ntohs(peer.sin_port);CheckOnlineUsers(peerIp,peerPort,peer);logMessage(NOTICE,"[%s:%d]# %s",peerIp.c_str(),peerPort,serBuffer);string infoUser(serBuffer);messageRount(peerIp,peerPort,infoUser);}   }void CheckOnlineUsers(string &ip,uint16_t port,struct sockaddr_in &peer){string key=ip;key += ":";key += std::to_string(port);auto itUser=_users.find(key);if(itUser==_users.end()){_users.insert({key,peer});}}void messageRount(string &ip ,uint16_t port,string info){string message="[";message += ip;message +=":";message +=std::to_string(port);message +="]";message +=info;for(auto &user : _users){sendto(_sockFd,message.c_str(),message.size(),0,(struct sockaddr*)&(user.second),sizeof(user.second));}}private:uint16_t _port;string _ip;int _sockFd;unordered_map<string, struct sockaddr_in> _users;
};static void Usage(const string proc)
{cout<<"Usage:\n\t"<<proc<<"port [ip]"<<endl;
}int main(int argc,char* argv[])
{if(argc!=2&&argc!=3){Usage(argv[0]);exit(3);}uint16_t port=atoi(argv[1]);string ip;if(argc==3){ip=argv[2];}udpServer uSvr(port,ip);uSvr.Init();uSvr.start();return 0;}

udpClient.cc: 

#include <iostream>
#include <string>
#include <cstdlib>
#include <cassert>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include<cstdarg>
#include "logMessage.hpp"using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::string;void* recverAndPrint(void* args) 接收并打印服务器消息
{while(true){int sockFd=*(int*)args;char buffer[1024];struct sockaddr_in temp;socklen_t templen=sizeof(temp);ssize_t s=recvfrom(sockFd,buffer,sizeof(buffer),0,(struct sockaddr*)&temp,&templen);if(s>0){buffer[s]=0;cout<<buffer<<endl;//为了方便演示这里保持使用 std::cout,以便重定向到命名管道文件}}
}static void Usage(const string proc){cout<<"Usage::\n\t"<<proc<<"server_IP server_Port nick_Name";
}int main(int argc,char* argv[])
{if(argc!=4){Usage(argv[0]);exit(1);}string server_IP = argv[1];uint16_t server_Port = atoi(argv[2]);string nick_Name = argv[3];int sockFd = socket(AF_INET, SOCK_DGRAM, 0);//只有创建了本地的套接字,才能使用recvfrom循环监听别人发向本地接口的信息if (sockFd < 0) {logMessage(FATAL, "socket() faild:: %s : %d", strerror(errno), sockFd);exit(2);}logMessage(DEBUG, "socket create success: %d", sockFd);struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(server_Port);server.sin_addr.s_addr = inet_addr(server_IP.c_str());pthread_t t;pthread_create(&t,nullptr,recverAndPrint,&sockFd);while(true){std::cerr<<"please enter>>";//使用 std::cerr 输出提示信息string inBuffer;inBuffer +="[";inBuffer +=nick_Name;inBuffer +="]# ";string tempS;getline(cin,tempS);//用于从标准输入读取用户的消息输入到tempS,能读取包含空格的字符串直到遇到换行符inBuffer +=tempS;//最终组合发送给服务端sendto(sockFd, inBuffer.c_str(), inBuffer.size(), 0,(const struct sockaddr*)&server, sizeof(server));}close(sockFd);return 0;
}

makefile: 

.PHONY:all
all:udpServer udpClientudpServer: udpServer.ccg++ -std=c++11 -o $@ $^
udpClient: udpClient.ccg++ -std=c++11 -o $@ $^ -lpthread.PHONY:clean
clean:rm -rf udpServer udpClient

inet_ntoa()相关问题 

在前面向 struct sockaddr_in 结构体中填充 IP 地址时使用了一个接口 :inet_addr(),并做了简单介绍,它是将 点分十进制(我们能看懂的)的 ip 转换成了 uint32_t(int _addt_t)类型的4字节的表示的ip,并且会自动把4字节的ip存储数据设置为网络字节序。使用起来是非常方便的,但是还存在着另一个接口: inet_nota()

该接口的作用是将 struct sockaddr_in 结构体 里面存储的4字节IP,转换成我们看的懂得点分十进制得IP字符串,然后以 char*类型返回。所以就需要一块空间来存储这个转换后的字符串,事实上这个空间是处于静态区的空间,返回的字符串都会放在这里,且值得注意的是,整个程序中 inet_ntoa() 也就只会返回处于这块的静态区空间。这也就是说,在一个 inet_ntoa()被多次执行的程序中,后面被转换出来的字符串会覆盖掉之前的IP字符串。所以在实际使用中要避免多次使用或者避免直接使用函数返回的指针。

但是我们可以使用另一个接口来代替: inet_ntop()

该接口可以将网络字节序IPv4或者IPv6地址转换成 点分十进制字符串表示

  1. af: 地址族,可以是 AF_INET(IPv4)或 AF_INET6(IPv6)
  2. src: 指向网络地址(以网络字节序存储)的指针。对于IPv4来说,这个参数是一个 struct in_addr 类型的指针;对于IPv6,则是 struct in6_addr 类型的指针。
  3. dst: 目标缓冲区,用于存放转换后的ASCII表示的IP地址。
  4. size: 目标缓冲区的大小

总结

通过上述步骤,UDP服务端和客户端就可以进行基本的数据交换了。由于UDP是无连接的,每次数据传输都是独立的,因此每次调用 sendto recvfrom 都不需要事先建立连接。这种方式非常适合那些对延迟敏感的应用程序,但也意味着数据包可能丢失或乱序到达。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com