【网络】UDP网络服务器简单模拟实现

这篇具有很好参考价值的文章主要介绍了【网络】UDP网络服务器简单模拟实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【网络】UDP网络服务器简单模拟实现


UDP的封装:

UDP网络服务器模拟实现:主要分为makefile文件进行编译

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

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

makefile

创建makefile文件:

makefile里可以定义变量,如cc=g++

【网络】UDP网络服务器简单模拟实现

服务端udpServer

udpServer.cc

客户端进行调用的逻辑代码:构建udpServer的对象,然后进行初始化,在进行启动起来;调用逻辑如下:

#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;
}

udpServer.hpp

客户端的实现代码逻辑:对外提供了一个初始化接口以及启动接口。

作为一款服务器:要有自己的服务端口号uint16_t _port,同时网络服务器需要有对应的string _ip地址,文件描述符_sockfd:进行各种各样的数据通信,在类内进行读写操作

对于ip地址的类型:字符串型只在我们用户层作为参数传递,这个不用去管,调用接口转换即可

【网络】UDP网络服务器简单模拟实现

初始化

对于UDP服务器如何初始化:完成两步即可:1.创建套接字socket2.绑定端口号port和ip

1.创建套接字socket,如果要进行网络通信用套接字来进行创建

NAME
       socket - create an endpoint for communication

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

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

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

参数protocol:缺省为0,可由前两个类型确定

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

2.绑定端口号和ip:bind

NAME
       bind - bind a name to a socket

SYNOPSIS
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

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

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

参数addr:利用struct sockaddr_in强转

参数addrlen:结构体的长度

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

定义一个sockaddr_in结构体填充数据,传递进去:

【网络】UDP网络服务器简单模拟实现

我们第三个参数也就是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)];
  };

__SOCKADDR_COMMON的定义:

typedef unsigned short int sa_family_t;
#define	__SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family

##是将字符串合并,也就是将接收到的sin_与family合并起来,形成了sin_family

创建结构体后要先清空数据(初始化),可以用memset,系统也提供了接口:

#include <strings.h>
void bzero(void *s, size_t n);

inet_addr:1.把字符串转化成整数;2.再把整数转化成对应的网络序列

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
in_addr_t inet_addr(const char *cp);
//const char*cp:点分十进制风格的IP地址

代码实现

	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,ip
            struct 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();
            int n = bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n==-1)
            {
                cerr<<"bind error: "<<errno<<" : "<<strerror(errno)<<endl;
                exit(BIND_ERR);
            }
            //UDP Server 的预备工作完成
        }

启动

服务器的本质就是一个死循环,死循环不退出的就是常驻内存的进程。OS本质就是一个死循环

客户端发来的消息,服务端可能对这个消息做一点处理,然后发会给客户端;所以得先读取数据recvfrom

#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这个结构体得知

        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);
                //1.数据是什么?2.谁发的
                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;
                }

            }
        }

测试

【网络】UDP网络服务器简单模拟实现

本地环回:服务器代码的测试

./udpServer 127.0.0.1 8080

查看网络情况就可以用指令netstat

-a:显示所有连线中的Socket;
-e:显示网络其他相关信息;
-i:显示网络界面信息表单;
-l:显示监控中的服务器的Socket;
-n:直接使用ip地址(数字),而不通过域名服务器;
-p:显示正在使用Socket的程序识别码和程序名称;
-t:显示TCP传输协议的连线状况;
-u:显示UDP传输协议的连线状况;

【网络】UDP网络服务器简单模拟实现

现在如果绑定云服务器IP地址:

./udpServer 43.143.177.75 8080

【网络】UDP网络服务器简单模拟实现

云服务器是虚拟化的服务器,不能直接bind你的公网IP,可以绑定内网IP(ifconfig);如果是虚拟机或者独立真实的Linux环境,你可以bind你的IP;如何保证云服务器能够被别人访问:实际上,一款网络服务器不建议指明一个IP,也就是不要显示地绑定IP,服务器IP可能不止一个,如果只绑定一个明确的IP,最终的数据可能用别的IP来访问端口号,访问不出来,所以真实的服务器IP一般采用INADDR_ANY(全0,任意地址)代表任意地址bind

【网络】UDP网络服务器简单模拟实现

客户端udpClient

udpClient.cc

客户端如何去调用:./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;
}

udpClient.hpp

作为一个客户端:对外提供一个初始化接口,以及一个启动run接口

初始化

对于初始化接口:服务端有套接字,客户端也必须得有

  • 客户端不需要显示的bind

在服务端bind的时候,最重要的不是绑定IP,而是绑定端口,客户端需要显示地绑定端口是为了服务器未来要明确的port,不能随意改变

而客户端需要端口,但是不重要,自己有端口号就可以,不需要显示地绑定:写服务器的是一家公司,写client的是无数家公司;由OS自动形成端口进行bind

代码实现

       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;
            //2.client必须要bind,client不要显示地bind,不需要程序员自己bind,由OS自动形成端口号进行bind
        }

启动

对于run接口:需要发送数据,发送数据需要用到接口sendto

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

最后运行起来测试一下(这里是同一台主机之间测试,如果是不同的机器,我们传递参数的时候就要传递公网IP)

【网络】UDP网络服务器简单模拟实现文章来源地址https://www.toymoban.com/news/detail-472766.html

整体代码

//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,ip
            struct 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;
}

到了这里,关于【网络】UDP网络服务器简单模拟实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 简单的UDP网络程序·续写(后端服务器)

    该文承接文章 简单的UDP网络程序 对于客户端和服务端的基本源码参考上文,该文对服务器润色一下,并且实现几个基本的业务服务逻辑 目录 demo1 第一个功能:字典翻译 初始化字典 测试代码:打印 字符串分割 客户端修改 成品效果 字典热加载 signal demo2 远端命令行解析 p

    2024年02月09日
    浏览(45)
  • Java 简单实现一个 UDP 回显服务器

    只需要继承自之前写的服务端, 然后重写父类 process 方法即可. 添加一些查询字典的逻辑. 效果 ✨ 本文记录了一个简单的 UDP 回显服务器代码. ✨ 想了解更多计算机网络的知识, 可以收藏一下本人的计算机网络学习专栏, 里面会持续更新本人的学习记录, 跟随我一起不断学习. ✨

    2024年02月21日
    浏览(51)
  • 【网络编程】demo版UDP网络服务器实现

    在上一章【网络编程】socket套接字中我们讲述了TCP/UDP协议,这一篇就是简单实现一个UDP协议的网络服务器。 我们也讲过其实 网络通信的本质就是进程间通信 。而进程间通信无非就是读和写(IO)。 所以现在我们就要写一个服务端(server)接收数据,客户端(client)发送数据

    2024年02月02日
    浏览(47)
  • 【网络编程】实现UDP/TCP客户端、服务器

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、UDP 1、Linux客户端、服务器 1.1udpServer.hpp 1.2udpServer.cc 1.3udpClient.hpp 1.4udpClient.cc 1.5onlineUser.hpp 2、Windows客户端 二、T

    2024年02月06日
    浏览(61)
  • 【网络编程】基于UDP数据报实现回显服务器/客户端程序

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【网络编程】【Java系列】 本专栏旨在分享学习网络编程的一点学习心得,欢迎大家在评论区交流讨论💌 前言 我们如果想让应用程序进行网络通信的话,就需要调用传

    2024年02月04日
    浏览(54)
  • 【Java网络编程】基于UDP-Socket 实现客户端、服务器通信

    ​ 哈喽,大家好~我是你们的老朋友: 保护小周ღ   本期为大家带来的是网络编程的 UDP Socket 套接字,基于 UDP协议的 Socket 实现客户端服务器通信 ,Socket 套接字可以理解为是,传输层给应用层提供的一组 API,如此程序,确定不来看看嘛~~ 本期收录于博主的专栏 : JavaEE_保

    2024年02月02日
    浏览(71)
  • 简单的Udp服务器

    2024年02月19日
    浏览(43)
  • 【网络】UDP网络服务器

    代码的整体逻辑: UDP服务端 :udpServer.cc(服务端的调用),udpServer.hpp(服务端的实现) UDP客户端 :udpClient.cc(客户端的调用),udpClient.hpp(客户端的实现) 服务端:1.初始化服务器 2.启动服务器  作为一款服务器:要有自己的服务端口号uint16_t _port,同时网络服务器需要有对应

    2024年02月08日
    浏览(42)
  • 网络字节序——TCP接口及其实现简单TCP服务器

    简单TCP服务器的实现 TCP区别于UDP在于要设置套接字为监控状态,即TCP是面向链接,因此TCP套接字需要设置为监听状态 socket函数原型 domain 表示协议族,常用的有 AF_INET (IPv4)和 AF_INET6 (IPv6)。 type 表示Socket类型,常用的有 SOCK_STREAM (TCP)和 SOCK_DGRAM (UDP)。 protocol 通常可

    2024年02月10日
    浏览(45)
  • 【计算机网络】HTTP协议以及简单的HTTP服务器实现

    虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输议)就是其中之一。 平时我们俗称的 “网址” 其实就是说的 URL 像 / ? : 等这样的字符, 已经被url当做特殊意义理解了.

    2024年01月20日
    浏览(63)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包