网站空间 按流量计费,优秀的设计,石家庄百度快照优化排名,构建新发展格局前文#xff1a;Socket套接字编程 UDP协议特点
无连接#xff1a;UDP在发送数据之前不需要建立连接#xff0c;减少了开销和发送数据之前的时延。尽最大努力交付#xff1a;UDP不保证可靠交付#xff0c;主机不需要维持复杂的连接状态表。面向报文#xff1a;UDP对应用层… 前文Socket套接字编程 UDP协议特点
无连接UDP在发送数据之前不需要建立连接减少了开销和发送数据之前的时延。尽最大努力交付UDP不保证可靠交付主机不需要维持复杂的连接状态表。面向报文UDP对应用层交下来的报文既不合并也不拆分而是保留这些报文的边界。首部开销小UDP的首部只有8个字节比TCP的20个字节要短。支持多种交互通信UDP支持一对一、一对多、多对一和多对多的交互通信。
大体思路
将通过客户端与服务端的创建实现客户端能够与服务端进行通信
利用Socket编程接口实现双端互通
UdpServer.hpp
包含一个udp服务端的类通过一些成员函数能够实现对应创建类对象进行初始化和启动对应服务端 对于服务器来说只需要创建对应的端口号IP地址这里为本地地址已经确定。
代码
#pragma once#includeiostream
using namespace std;
#includeLog.hpp
#includestring
#includecerrno
#includecstring
#includecstdlib
#includesys/socket.h
#includesys/types.h
#includearpa/inet.h
#includenetinet/in.h
#includeInetAddr.hpp
//描述错误码
enum
{SOCKET_ERROR 1,BIND_ERROR,USAGE_ERROR
};
const static int defaultfd -1;//默认文件描述符为无
class UdpServer
{
public:UdpServer(uint16_t port):_sockfd(defaultfd),_port(port),_isrunning(false){}void InitServer(){//1.创建udp socket 套接字 _sockfd socket(AF_INET,SOCK_DGRAM,0);if(_sockfd0){LOG(FATAL,socket error, %s, %d\n,strerror(errno),errno);exit(SOCKET_ERROR);}LOG(INFO,socket create success,sockfd: %d\n,_sockfd);//2 先填充sockadd_in结构struct sockaddr_in local;bzero(local,sizeof(local));local.sin_familyAF_INET;local.sin_port htons(_port);//htons:将主机字节序转换为网络字节序//local.sin_addr.s_addr inet_addr(_ip.c_str());local.sin_addr.s_addrINADDR_ANY;//INADDR_ANY告诉套接字监听来自任何IP地址的连接。这对于服务器来说非常有用因为服务器通常需要能够接受来自客户端网络上任何位置的连接请求。//再通过函数bind将套接字和网络信息连接起来int n bind(_sockfd,(struct sockaddr* )local,sizeof(local));if(n0){LOG(FATAL,bind error,%s, %d\n,strerror(errno),errno);exit(BIND_ERROR);}LOG(INFO,socket bind success\n);}//通过循环不断接收客户端的数据打印出对应的内容void Start(){_isrunning true;while(true){char buffer[1024];struct sockaddr_in peer;socklen_t len sizeof(peer);ssize_t nrecvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr* )peer,len);if(n0){buffer[n] 0;InetAddr addr(peer);//创建一个网络地址的类对象LOG(DEBUG, get message from [%s:%d]: %s\n, addr.Ip().c_str(),addr.Port(),buffer);// 2. 我们要将server收到的数据发回给对方表示已接收sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)peer, len);}}_isrunning false;}
private:int _sockfd; //sock文件描述符uint16_t _port; //端口号bool _isrunning; //服务器是否启动
};解释 利用socket函数创建socket编程接口AF_INET表示IPv4SOCK_DGRAM表示udp数据报套接字的 INADDR_ANY: 调用bind()函数将套接字与一个特定的端口以及可选的IP地址绑定时可以将IP地址设置为INADDR_ANY其值通常为0表示“任何地址”。这样做的好处是UDP服务器将能够接收来自任何网络接口的连接请求而不仅仅是绑定时指定的那一个。 这样bind之后就能让IP地址与端口号和Socket编程接口联系起来让接口 “有名有性”
将接收的数据存储在buffer中peer可以接收到发送端的地址信息 只有n大于0才表示接收到了具体数据 将接收到的数据信息可以经过一定的处理然后发送回客户端这里中间没有经过处理只是将接收到的信息直接反馈给客户端而已也就是将buffer中的内容发送到客户端中
InetAddr.hpp
InetAddr类将包含主机上一切有关地址的信息
代码
#pragma once#includeiostream
#includesys/types.h
#includesys/socket.h
#includearpa/inet.h
#includenetinet/in.h
using namespace std;class InetAddr
{
public:InetAddr(const struct sockaddr_in addr):_addr(addr){GetAddress(_ip,_port);}string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}
private://通过已知的sockaddr信息获取对应的ip地址信息和端口号void GetAddress(string* ip,uint16_t* port){*portntohs(_addr.sin_port);*ipinet_ntoa(_addr.sin_addr);}struct sockaddr_in _addr; //sockaddr的信息string _ip; //IP地址uint16_t _port; //端口号
};main.cc
初始化服务端并启动服务端
#includeiostream
#includememory
using namespace std;
#includeUdpServer.hppvoid Usage(string proc)
{coutUsage:\n\tproc local_port\nendl;
}//示例 ./main.cc 8888
int main(int argc,char* argv[])
{if(argc!2){Usage(argv[0]);exit(USAGE_ERROR);}EnableScreen();//打印到屏幕uint16_t port stoi(argv[1]); //获取对应的端口号unique_ptrUdpServer usvrstd::make_uniqueUdpServer(port);//智能指针usvr-InitServer(); // 初始化服务端usvr-Start(); // 启动服务端return 0;
}通过智能指针的性质创建一个指向UdpServer类对象的指针能够在周期结束时自动回收对应的智能指针
UdpClient.cc
作为客户端能够不断给服务端发送数据并接收对应数据构建Socket编程接口与上面一致。
#includeiostream
using namespace std;
#includestring
#includecstring
#includecstdlib
#includesys/types.h
#includesys/socket.h
#includenetinet/in.h
#includearpa/inet.hvoid Usage(string proc)
{coutUsage:\n\tproc serverip serverport\nendl;
}//示例 ./UdpClient.cc 127.0.0.1 8080
int main(int argc,char* argv[])
{if(argc!3){Usage(argv[0]);exit(1);}string serveripargv[1];//服务端ipuint16_t serverportstoi(argv[2]);//服务端端口号//创建socketint sockfd socket(AF_INET,SOCK_DGRAM,0);if(sockfd0){cerrsocket errorendl;}//构建目标主机socket信息struct sockaddr_in server;memset(server,0,sizeof(server));server.sin_familyAF_INET;server.sin_porthtons(serverport);server.sin_addr.s_addrinet_addr(serverip.c_str());string message;while(true){coutPlease Enter# ;getline(cin,message);//获取输入的一行字符串空格亦可接收输入到message中sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr *)server,sizeof(server));//发送给服务端struct sockaddr_in peer;socklen_t len sizeof(peer);char buffer[1024];//接收存放数据ssize_t n recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)peer, len);//接收服务端发送数据if(n 0){buffer[n] 0;std::cout server echo# buffer std::endl;}}
}127.0.0.1
127.0.0.1 是一个特殊的IP地址被称为回环地址Loopback Address。在IPv4网络中它被用于指向本机即运行代码的计算机。当你尝试访问 127.0.0.1 上的某个端口时你的操作系统会识别这是一个内部请求并将该请求直接路由回本机上的相应服务而不会通过网络发送到外部。
这种机制使得开发者能够在本地测试网络服务而无需担心外部网络的影响。
验证 我们可以通过命令行netstat来进行查询对应网络接口 netstat网络统计命令是Linux系统中用于显示网络连接、路由表、接口统计、伪装连接和多播成员等网络相关信息的强大工具。