欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux网络中UDP协议构造一个服务端和客户端的流程总结

程序员文章站 2022-03-04 08:53:26
...

**

因为本人之前一直写的是云笔记,对自己学会的东西作一个总结,所以基本都是文字,本来想全发成博客的形式,发现全发成博客比较花费时间,而且一直发博客质量不是很好,而且通过发博客学到的东西也会变少,所以准备先把笔记发出来,后续再将它们改成博客的形式,争取2天至少改一篇博客,觉得我总结的还行的可以先关注我,后续会发成博客形式,内容也会更加完善

**

UDP的服务端:
在进行套接字编程时,通常将需要的用到套接字函数在类中进行封装,增加一些其它的功能,或者是做一些防错措施,以及完善这些函数的流程,所以将这些函数集中在类中是很有作用的
我们在搭建UDP服务端时,通常是用main函数自带的三个参数来获取用户的输入信息,这个输入信息是我们在发送数据时要用的的IP地址和所用到的端口号,然后分别创建两个字符串对象来存储它们,方便一会在进行UDP传输时传入参数,然后我们一般创建一个之前写好的,UDP套接字函数对象(通过调用里面的函数来实现UDP数据报传输),因为UDP是单向的传输,不用管对端,也就是不用建立连接,所以我们可以直接调用之前封装好的socket函数,进行套接字的创建,然后再调用封装好的bind函数,来进行地址信息的绑定,因为我们现在是服务端,所以一般情况下自己绑定自己的IP地址,然后再绑定自己规定的端口号,所以这两个信息我们要传入bind函数中,当地址信息绑定完成后,就可以开始发送和接收数据了,因为我们是服务端,所以一般是先等待客户端给我们发送请求数据,然后我们再获取客户端的地址信息,发送对应的数据包给它们,所以我们先调用封装好的recvfrom函数进行接收,因为我们不知道服务端的数据包什么时候过来,所以就得一直等待服务端,所以一般情况下我们要接收客户端的数据时,就写个while(1),让它一直循环接收,如果接收到了,我们就获取到了客户端的地址信息,进而我们就可以调用封装好的sendto函数,进行数据的发送,在所有信息都传递完成,且后续不需要再接收客户端信息时,我们就可以调用封装好的close函数,关闭套接字
我们在搭建UDP客户端时,通常是用main函数自带的三个参数来获取用户的输入信息,这个输入信息是服务端的IP地址和所用到的端口号,然后分别创建两个字符串对象来存储它们,方便一会在进行UDP传输时传入参数,然后我们一般创建一个之前写好的,UDP套接字函数对象(通过调用里面的函数来实现UDP数据报传输),因为UDP是单向的传输,不用管对端,也就是不用建立连接,所以我们可以直接调用之前封装好的socket函数,进行套接字的创建,另外有一点要说明,一般情况下UDP的客户端不建议自己绑定地址信息,服务端需要绑定地址信息的原因是,如果服务端不绑定地址信息,那么客户端就不能确定服务端到底是用什么地址来接收数据包的,所以客户就不能向服务传输数据包了,但是客户端的话就不一样了,因为客房端是向服务请求数据包的,也就是客户端先向服务端发送数据包,服务端在接收到数据包后,可以获取客户端的地址信息,进而也就可以给客户端发送数据包了,所以客户端可以不绑定地址信息,而且客户端不绑定地址信息的好处也有很多,比如我第一次向服务端请求数据包时,用的是一个网卡上的IP地址,但过了一阵时间,我换了一个网卡,我的地址信息变了,但是由于我没有绑定地址信息,所以我仍可以向服务端请求数据包,所以客户端一般不建议绑定地址信息,所以现在我们就可以直接根据服务端的地址信息,利用我们封装好的sendto函数,向服务端发送数据包了,我们给服务端发送完数据包后,就可以等待服务端给我们回复数据包了,因为我们不知道服务端的数据包什么时候过来,所以就得一直等待服务端,所以一般情况下我们要接收服务端的数据时,就写个while(1),让它一直循环接收,如果接收到了,我们就获取到了服务端的地址信息在所有信息都传递完成,且后续不需要再接收客户端信息时,我们就可以调用封装好的close函数,关闭套接字

在构建服务端和客户端前封装的udp函数类:

#include <iostream>
#include <string>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define CHECK_RET(q) if((q) == false){return -1;}

class UdpSocket
{
    public:
        UdpSocket(){
        }
        ~UdpSocket(){
        }
        bool Socket(){  
            _sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if (_sock < 0) {
                perror("socket error");
                return false;
            }
            return true;
        }
        bool Bind(std::string &ip, uint16_t port){
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            addr.sin_addr.s_addr = inet_addr(ip.c_str());
            socklen_t len = sizeof(struct sockaddr_in);
            int ret = bind(_sock, (struct sockaddr*)&addr, len);
            if (ret < 0) {
                perror("bind error");
                return -1;
            }
            return true;
        }
        bool Recv(std::string &buf, sockaddr_in *_addr = NULL){
            char tmp[1024] = {0};
            struct sockaddr_in addr;
            socklen_t len = sizeof(struct sockaddr_in);
            int ret = recvfrom(_sock, tmp, 1024, 0,
                    (struct sockaddr*)&addr, &len);
            if (ret < 0) {
                perror("recvfrom error");
                return false;
            }
            buf.assign(tmp, ret);
            if (_addr != NULL) {
                memcpy(_addr, &addr, len);
            }
            return true;
        }
        bool Send(std::string &buf, struct sockaddr_in &addr){
            socklen_t len = sizeof(struct sockaddr_in);
            int ret = sendto(_sock, buf.c_str(), buf.size(), 0,
                    (struct sockaddr*)&addr, len);
            if (ret < 0) {
                perror("sendto error");
                return false;
            }
            return true;
        }
        bool Close(){
            close(_sock);
            _sock = -1;
            return true;
        }
    private:
        int _sock;
};

服务端:

#include "udpsocket.hpp"

int main(int argc, char *argv[])
{
    if (argc != 3) {
        printf("./udp_srv ip port\n");
        return -1;
    }
    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    UdpSocket sock;
    CHECK_RET(sock.Socket());
    CHECK_RET(sock.Bind(ip, port));

    while(1) {
        std::string buf;
        struct sockaddr_in cli_addr;
        sock.Recv(buf, &cli_addr);
        printf("client say:%s\n", buf.c_str());

        std::cout<<"server say:";
        fflush(stdout);
        std::cin >> buf;
        sock.Send(buf, cli_addr);
    }
    sock.Close();
    return 0;
}

客户端:

#include "udpsocket.hpp"

int main (int argc, char *argv[])
{
    if (argc != 3){
        printf("./udp_cli ip port\n");
        return -1;
    }
    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    struct sockaddr_in srv_addr;
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons(port);
    srv_addr.sin_addr.s_addr = inet_addr(ip.c_str());

    UdpSocket sock;
    CHECK_RET(sock.Socket());

    while(1){
       std::string buf;
       std::cout<<"client say:";
       fflush(stdout);
       std::cin>>buf;
       sock.Send(buf, srv_addr);

       buf.clear();
       sock.Recv(buf);
       std::cout<<"server say:"<<buf<<std::endl;
    }
    sock.Close();
    return 0;
}