[Network] UDP network server simple simulation implementation

[Network] UDP network server simple simulation implementation


Encapsulation of UDP :

UDP network server simulation implementation: mainly divided into makefile files for compilation

UDP客户端: udpClient.cc (client call), udpClient.hpp (client implementation)

UDP服务端: udpServer.cc (server call), udpServer.hpp (server implementation)

makefile

Create makefile:

Variables can be defined in makefile, such as cc=g++

image-20230505222243055

server udpServer

udpServer.cc

The logic code for the client to call: construct the object of udpServer, then initialize it, and start it up; the calling logic is as follows:

#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

The implementation code logic of the client: it provides an initialization interface and a startup interface to the outside world.

As a server: It must have its own service port number uint16_t _port, and the network server needs to have a corresponding string _ip address, file descriptor _sockfd: perform various data communications, and perform read and write operations within the class

For the type of ip address: the string type is only passed as a parameter in our user layer, this is not necessary, just call the interface conversion

image-20230505232656063

initialization

How to initialize the UDP server: complete two steps: 1. Create a socket socket2. Bind the port number port and ip

1. Create a socket socket, if you want to use a socket for network communication to create

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

Parameter data domain: domain, the future socket is for network communication or local communication, mainly use the following two types :
AF_UNIX, AF_LOCAL Local communication unix(7)
AF_INET IPv4 Internet protocols ip(7)
parameter type: socket provided The type of service, such as SOCK_STREAM: streaming service TCP policy, SOCK_DGRAM: datagram service, UDP policy

Parameter protocol: the default is 0, which can be determined by the first two types

Return value: -1 on failure, file descriptor on success

2. Bind port number and 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);

Parameter sockfd: file descriptor for communication, that is, the return value of calling socket

Parameter addr: Use struct sockaddr_in to force transfer

Parameter addrlen: the length of the structure

Return value: return 0 on success, return -1 on failure

Define a sockaddr_instructure to fill in the data and pass it in:

img

Our third parameter is the inside of the sockaddr_in structure:

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

Definition of __SOCKADDR_COMMON:

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

##It is to combine the strings, that is, to combine the received sin_ and family to form sin_family

After creating the structure, you must first clear the data (initialization), you can use memset, and the system also provides an interface :

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

inet_addr: 1. Convert the string into an integer; 2. Then convert the integer into the corresponding network sequence

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

Code implementation :

	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 的预备工作完成
        }

start up

The essence of the server is an infinite loop , and the process that does not exit the infinite loop is the resident memory process. The essence of OS is an infinite loop

For the message sent by the client, the server may do some processing on the message, and then send it to the client; so you must first read the data 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: a specific socket, buf: read to a specific buffer, len: how long

flags: the way of reading, the default is 0, blocking reading

src_addr: In addition to the message received, you must know who sent it, input and output parameters, and return the corresponding message content from which client, len: what is the size

Return -1 means failure, success returns the number of bytes

At the same time, we have to know who sent it, so we can know it through the sockaddr_in structure

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

            }
        }

test

insert image description here

Local Loopback: Testing of Server Code

./udpServer 127.0.0.1 8080

To view the network status, you can use the command netstat:

-a: Display Sockets in all connections;
-e: Display other related information of the network;
-i: Display network interface information form;
-l: Display the Socket of the server under monitoring;
-n: Directly use the ip address (number) , without going through the domain name server;
-p: display the program identification code and program name that are using the Socket;
-t: display the connection status of the TCP transmission protocol;
-u: display the connection status of the UDP transmission protocol;

image-20230506134553093

Now if you bind the cloud server IP address:

./udpServer 43.143.177.75 8080

image-20230506134837052

The cloud server is a virtualized server, you cannot directly bind your public IP, you can bind the internal IP (ifconfig); if it is a virtual machine or an independent real Linux environment, you can bind your IP; how to ensure that the cloud server can Accessed by others: In fact, it is not recommended to specify an IP for a web server, that is, do not explicitly bind the IP. There may be more than one server IP. If only one specific IP is bound, the final data may be sent by another IP. The access port number cannot be accessed, so the real server IP generally uses INADDR_ANY (all 0, any address) to represent any address bind

image-20230506140814619

client udpClient

udpClient.cc

How the client calls: ./udpClient server_ip server_port, if the client wants to connect to the server, it must know the other party’s IP (public network IP); the calling logic is as follows:

#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

As a client: provide an initialization interface and a start run interface

initialization

For the initialization interface : the server must have a socket, and the client must also have

  • The client does not need to display the bind

When the server binds, the most important thing is not to bind the IP, but to bind the port. The client needs to explicitly bind the port for the server to specify the port in the future, and it cannot be changed arbitrarily.

The client needs a port, but it’s not important, as long as it has its own port number, it doesn’t need to be bound explicitly: the server is written by one company, and the client is written by countless companies; the OS automatically forms the port for binding

Code

       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
        }

start up

For the run interface: data needs to be sent, and the interface sendto is required to send data

#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: which socket, buf: buffer, len: length, flags: 0, send if there is data, block if there is no data

dest_addr: This structure is also forcibly transferred from struct sockaddr_in, to whom it is sent, and filled with the other party's IP and port

addrlen: no pointer, input parameter

Code

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

Finally, run it and test it (here is a test between the same host, if it is a different machine, we must pass the public network IP when passing the parameters)

overall code

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

Guess you like

Origin blog.csdn.net/weixin_60478154/article/details/130534801