【网络】UDP网络服务器

news/2025/2/22 5:06:36/

代码的整体逻辑:

UDP服务端:udpServer.cc(服务端的调用),udpServer.hpp(服务端的实现)

UDP客户端:udpClient.cc(客户端的调用),udpClient.hpp(客户端的实现)

1.udp服务端

服务端:1.初始化服务器 2.启动服务器 

作为一款服务器:要有自己的服务端口号uint16_t _port,同时网络服务器需要有对应的string _ip地址,文件描述符_sockfd:进行各种各样的数据通信,在类内进行读写操作。对于ip地址的类型:字符串型只在我们用户层作为参数传递,传递的IP格式为" 127.0.0.1 "这种格式的IP称之为“点分十进制"IP,这样的IP是字符串,可读性高,但是仅仅在我们用户层使用,但是在网络通信的过程中IP是需要转换为 uint32_t 类型,这时候需要调用相关的接口转换为网络通信使用的的IP风格。

一.初始化服务器

初始化服务器服务器包括:创建套接字和绑定端口号和IP

参数据数domain:域,未来套接字是进行网络通信还是本地通信,主要使用下面这两种 :
AF_UNIX, AF_LOCAL 
AF_INET 
参数type:套接字提供服务的类型,如SOCK_STREAM:流式服务TCP策略,SOCK_DGRAM:数据报服务,UDP策略

参数protocol:缺省为0,可由前两个类型由系统决定并填充

返回值:失败返回-1,成功返回文件描述符

绑定端口号和IP的接口:

参数sockfd:文件描述符进行通信,也就是调用socket的返回值

参数addr:利用struct sockaddr_in强转

参数addrlen:结构体的长度

返回值:成功返回0,失败返回-1

这里需要定义一个sockaddr_in结构体并填充数据,然后传递进去

 sockaddr_in结构体的内部成员:

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)];};

在结构体内部:首先填充协议家族,给别人发消息未来还要从对方接收消息,所以需要将自己的port和IP填充进去,port需要由主机序列转为网络序列,因为初始传来的ip是string类型的所以IP需要由点分十进制转为整数然后再转为网络序列。

一款网络服务器不建议指明一个IP,也就是不要显示地绑定IP,服务器IP可能不止一个,如果只绑定一个明确的IP,最终的数据可能用别的IP来访问端口号,访问不出来,所以真实的服务器IP一般采用INADDR_ANY(全0,任意地址)代表任意地址bind
 

初始化服务器的代码:

	void initServer(){//1.创建套接字_sockfd = socket(AF_INET,SOCK_DGRAM,0);if(_sockfd == -1){cerr<<"socket error: "<<errno<<" : "<<strerror(errno)<<endl;exit(SOCKET_ERR);}cout << "socket success " << " : " << _sockfd << endl;//2.绑定端口号port,ipstruct sockaddr_in local;//定义了一个变量//结构体清空bzero(&local,sizeof(local));local.sin_family = AF_INET;//协议家族local.sin_port=htons(_port);//给别人发消息,port和ip也要发给对方,需要大小端转换//local.sin_addr.s_addr = inet_addr(_ip.c_str());//1.ip转成整数 2.整数要转成网络序列:htonl();local.sin_addr.s_addr=inet_addr(INADDR_ANY);int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n==-1){cerr<<"bind error: "<<errno<<" : "<<strerror(errno)<<endl;exit(BIND_ERR);}}

二.启动服务器

服务器只要启动了就是不断地在接收并处理数据最后有必要时还要给客户端返回一些数据,所以要一直循环的运行,服务器是一个常驻内存的进程。

接收数据:

#include <sys/types.h>
#include <sys/socket.h>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:收到消息除了本身,还得知道是谁发的,输入输出型参数,返回对应的消息内容是从哪一个client发来的,len:大小是多少

返回-1表示失败,成功返回字节数

创建一个类型为sockaddr_in 的结构体,未来根据这个结构体可以得知发送数据主机的IP和端口号

        void start(){char buffer[gnum];for(;;){struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(s>0){buffer[s] = 0;string clientip = inet_ntoa(peer.sin_addr);//1.网络序列转化成本地序列2.4字节整数IP转化成点分十进制uint16_t clientport = ntohs(peer.sin_port);string message = buffer;cout<<clientip<<"["<<clientport<<"]# "<<message<<endl;}}}

三.上层的调用

#include "udpServer.hpp"
#include <memory>using namespace std;
using namespace Server;static void Usage(string  proc)
{cout<<"\nUsage:\n\t"<<proc<<"  local_port\n\n";
}
//  ./udpServer  port
int main(int argc,char*argv[])
{if(argc != 2){Usage(argv[0]);exit(1);}uint16_t port = atoi(argv[1]);std::unique_ptr<udpServer> usvr(new udpServer(port));usvr->initServer();usvr->start();return 0;
}

2.udp客户端

1.客户端上层的调用

启动客户端方法为  :  ./udpClient server_ip server_port,客户端想连接服务器,必须得知道对方的IP(公网IP)和端口号调用逻辑如下:

#include "udpClient.hpp"
#include <memory>using namespace Client;
static void Usage(string  proc)
{cout<<"\nUsage:\n\t"<<proc<<" server_ip server_port\n\n";
}
//./udpCleint server_ip server_port
int main(int argc,char*argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);unique_ptr<udpClient> ucli(new udpClient(serverip,serverport));ucli->initClient();ucli->run();return 0;
}

2.初始化客户端

初始化客户端需要创建套接字,客户端需要端口,但是不重要,自己有端口号就可以,不需要显示的绑定会由操作系统帮助我们找到一个没有被绑定的端口号并绑定。

3.启动客户端

#include <sys/types.h>
#include <sys/socket.h>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:这个结构体也是由struct sockaddr_in强转,向谁发,填充对方的IP和端口

addrlen:没有指针,输入型参数

代码实现:

	    void run(){struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(_serverip.c_str());server.sin_port = htons(_serverport);string message;while(!_quit){cout<<"Please Enter# ";cin>>message;//发送给远端服务器sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));}}

3.测试服务器

 

 IP 为 127.0.0.1是本地回环地址,通俗的讲,就是我们在主机上发送给127开头的IP地址的数据包会被发送的主机自己接收,根本传不出去,外部设备也无法通过回环地址访问到本机,数据只是在本机内协议贯穿,根本不会到达物理层,用来服务器代码的测试。

4.整体代码

//udpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <arpa/inet.h>
#include <strings.h>
#include <netinet/in.h>
#include <functional>
namespace Server
{using namespace std;static const string defaultIP = "0.0.0.0";static const int gnum = 1024;enum {USAGE_ERR = 1,SOCKET_ERR,BIND_ERR};class udpServer{public:udpServer(const uint16_t&port,const string&ip = defaultIP):_port(port),_ip(ip),_sockfd(-1){}void initServer(){//1.创建套接字_sockfd = socket(AF_INET,SOCK_DGRAM,0);if(_sockfd == -1){cerr<<"socket error: "<<errno<<" : "<<strerror(errno)<<endl;exit(SOCKET_ERR);}cout << "socket success " << " : " << _sockfd << endl;//2.绑定端口号port,ipstruct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family = AF_INET;local.sin_port=htons(_port);//local.sin_addr.s_addr = inet_addr(_ip.c_str());local.sin_addr.s_addr = htons(INADDR_ANY);int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n==-1){cerr<<"bind error: "<<errno<<" : "<<strerror(errno)<<endl;exit(BIND_ERR);}}void start(){char buffer[gnum];for(;;){struct sockaddr_in peer;socklen_t len = sizeof(peer);ssize_t s = recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(s>0){buffer[s] = 0;string clientip = inet_ntoa(peer.sin_addr);//1.网络序列转化成本地序列2.4字节整数IP转化成点分十进制uint16_t clientport = ntohs(peer.sin_port);string message = buffer;cout<<clientip<<"["<<clientport<<"]# "<<message<<endl;}}}private:uint16_t _port;//端口号string _ip;//ip地址int _sockfd;//文件描述符};
}//udpServer.cc
#include "udpServer.hpp"
#include <memory>
using namespace std;
using namespace Server;
static void Usage(string  proc)
{cout<<"\nUsage:\n\t"<<proc<<"  local_port\n\n";
}
//  ./udpServer  port
int main(int argc,char*argv[])
{if(argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);std::unique_ptr<udpServer> usvr(new udpServer(port));usvr->initServer();usvr->start();return 0;
}//udpClient.hpp
#pragma once
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <arpa/inet.h>
#include <strings.h>
#include <netinet/in.h>
namespace Client
{using namespace std;class udpClient{public:udpClient(const string&serverip,const uint16_t &serverport):_serverip(serverip),_serverport(serverport),_sockfd(-1),_quit(false){}void initClient(){//客户端创建socket_sockfd = socket(AF_INET,SOCK_DGRAM,0);if(_sockfd == -1){cerr<<"socket error: "<<errno<<" : "<<strerror(errno)<<endl;exit(2);}cout<<"socket success "<<" : "<<_sockfd<<endl;}void run(){struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(_serverip.c_str());server.sin_port = htons(_serverport);string message;while(!_quit){cout<<"Please Enter# ";cin>>message;sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));}}private:int _sockfd;string _serverip;uint16_t _serverport;bool _quit;};
}//udpClient.cc
#include "udpClient.hpp"
#include <memory>
using namespace Client;
static void Usage(string  proc)
{cout<<"\nUsage:\n\t"<<proc<<" server_ip server_port\n\n";
}
//./udpCleint server_ip server_port
int main(int argc,char*argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);unique_ptr<udpClient> ucli(new udpClient(serverip,serverport));ucli->initClient();ucli->run();return 0;
}

makefile文件:


http://www.ppmy.cn/news/213395.html

相关文章

华为设备路由器直连路由器配置

如图,PC1现在可以ping通ISP1路由器的GE0/0/1端口IP10.1.1.1,但是不能ping通ISP1路由器的GE0/0/0端口IP100.1.1.1 肯定会有人说出口路由器与ISP1、ISP2、internet路由器之间都运行ospf就可以了 但是这个实验模拟的是公司局域网与internet的连通性,这样配置ospf就没意义了 该…

华为路由器ip地址和mac地址绑定命令

首先进入路由器命令行使用display current-configuration命令查看当前配置确定要绑定ip所在的VLAN&#xff0c;然后输入system-view进入配置模式输入命令interface vlanif XX&#xff08;这里是你ip所在的vlan编号&#xff09;进入到相应vlan输入命令dhcp server static-bind i…

华为路由器hilink怎么用_HUAWEI HiLink 两个华为路由器无线中继实测效果【图解】...

原标题&#xff1a;文章是关于"HUAWEI HiLink 两个华为路由器无线中继实测效果【图解】"的相关知识分享&#xff0c;希望可以帮到大家。 。 公众号里有朋友问有关于华为路由器的HiLink功能&#xff0c;说可以让两台华为路由器无线桥接后达到无缝漫游的效果。我查了一…

华为4G无线路由器测试锁频软件,华为4g无线路由器 新款E5375路由器测试

对于新款的路由器大家想买但是又不知道好不好用&#xff0c;想买4g无线路由器的同学注意了&#xff0c;今天小编为大家这款华为4g无线路由器——E5375的测试&#xff0c;究竟好不好用呢&#xff0c;下面就跟随小编一起来看看吧。 华为4g无线路由器E5375主要参数如下&#xff1a…

华为路由器可以连接几个设备_华为如何设置连接两个无线路由器

在网络互联设备中,路由器是使用最广的一种网络互联设备,是数据通信的重要互联设备&#xff0c;下面是学识网小编整理的一些关于华为设置连接两个无线路由器的资料&#xff0c;供你参考。 华为设置连接两个无线路由器的方法 1、网口2的位置添加一个家用交换机&#xff0c;有4个口…

华为无线_AC+AP小型无线网络配置实验_v1

允许转载&#xff0c;但必须注明出版处与原文链接&#xff0c;否则追究其法律责任&#xff0c;谢谢合作&#xff01; (原文博客&#xff1a;https://blog.51cto.com/11179786) 华为无线-ACAP小型无线网络配置实验_v1 网络结构图&#xff1a; 步骤一&#xff1a;配置网络连通性 …

XCIE-HUAWEI-WLAN无线

XCIE-HUAWEI-WLAN-无线 什么叫WLAN Wireless LAN&#xff08;无线局域网&#xff09; 解释一下WLAN和WIFI的区别 手机上有的叫WLAN有的叫WIFI&#xff0c;选择可以告诉你&#xff0c;WLAN比WIFI牛逼 WIFI是一句802.11ac ax等的无线局域网&#xff08;比如WIFI-6&#xff09; W…

华为无线AC6005+(4个)AP配置双WiFi模板案例

ensp双击云 直接新增udp网卡 再选择一张虚拟网卡增加 入端口编号为1&#xff08;udp&#xff09; 出端口编号为2&#xff08;增加的网卡&#xff09; 勾选双向通道增加&#xff0c;就可以和主机通信 配置AC管理VLAN和业务VLAN------------------------------------------------…