UDP网络通信

UDP网络通信:

步骤1 创建套接字:

cpp 复制代码
#include <sys/types.h>        
#include <sys/socket.h>

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

参数一 domain:

AF_UNIX Local communication unix(7) 本地通信

AF_INET IPv4 Internet protocols ip(7) 网络通信

参数二 type:

SOCK_DGRAM Supports datagrams (connectionless, unreliable messages of a fixed maximum length). 无连接不可靠面向数据报文,也就是UDP

参数三 protocol:

一般省略,直接为0.

RETURN VALUE

On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errno is set appropriately.(成功返回一个文件描述符,失败返回-1)。

步骤2 绑定 将IP端口与套接字绑定起来:

绑定的过程,需要将IP和端口号关联起来,使用 sockaddr_in 对信息进行关联。

使用 sockaddr_in 需要头文件 #include <netinet/in.h> 与 #include <arpa/inet.h>

需要将本体服务端的 IP 和 port 填充进 sockaddr_in 的结构体中。

结构体包含三个需要填充的字段:

1.sin_family : 通信的类型。这个和套接字选择的类型要相同。

2.sin_port : 端口号,如果手动写入的端口号是16位主机序列,不符合网络序列,因此需要利用 htons(uint16_t) 将其转换为网络序列

3.sin_addr.s_addr : 网络地址。传入的肯定是一个字符串,但是网络中需要的是4字节 IP,需要的是网络序列的 IP,因此又需要转换。其转换函数为:inet_addr(string)。

完成上述的填充工作后,bind函数可以将填充好的字段和套接字关联起来。关联如下:

cpp 复制代码
#include <sys/types.h>        
#include <sys/socket.h>

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

RETURN VALUE

On success, zero is returned. On error, -1 is returned, and errno is set appropriately.(成功返回0,失败-1)

这里的第二个参数是 struct sockaddr* ,而填充的时候用的是struct sockaddr_in,因此需要强转如下。

完整代码如下:

步骤三 使用函数读写数据。

UDP不能使用 read 和 write,

读数据需要使用 recvfrom:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

参数分别为:fd,缓冲区和缓冲区的大小(读出的数据放在哪里,期望接受多少数据),flags设置为0表示为阻塞读取。

后两个参数为:输入型参数,这两个参数放的就是谁给我传的网络信息,放的就是对方的 IP 和 port。

返回值大于 0 读取成功。后两个参数的作用就是得到客户端是谁。

recvfrom 的返回值就是接受了多少数据。

写数据用的是 sendto:

cpp 复制代码
#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);

后两个参数为输入型参数,分别为:

const struct sockaddr *dest_addr:代表客户端。

socklen_t addrlen:代表客户端的长度。

RETURN VALUE

On success, these calls return the number of bytes sent. On error, -1 is returned, and errno is set appropriately.(成功返回值大于0,失败-1)

客户端读数据,recvfrom的后两个参数直接给一个初始的无意义的参数给上就行。

网络转主机和主机转网络序列。

代码如下:

cpp 复制代码
//udpserver.hpp
#pragma once

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <string>
#include "nocopy.hpp"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static const int gsockfd = -1;
static const uint16_t glocalport = 8888;
enum{
    SOCKET_ERROR = 1,
    BIND_ERROR,
};

// UdpServer user("192.1.1.1", 8888)
class UdpServer : public nocopy
{
public:
    UdpServer(const std::string& localip, uint16_t localport = glocalport)
        :_sockfd(gsockfd), 
         _localport(localport),
         _localip(localip),
         _isrunning(false)
    {}

    void InitServer()
    {
        //1 创建套接字
        _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            std::cout << "socket error! " << std::endl;
            exit(SOCKET_ERROR);
        }
        std::cout << "socket success, _sockfd: %d " << _sockfd << std::endl; //预计是 3

        //2 绑定
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_localport);
        local.sin_addr.s_addr = inet_addr(_localip.c_str());

        int n = ::bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if(n<0)
        {
            std::cout << "bind error! " << std::endl;
            exit(BIND_ERROR);
        }
        std::cout << "bind success" << std::endl;
    }

    void Start()
    {
        _isrunning = true;
        char inbuffer[1024];
        while(_isrunning)
        {
            struct sockaddr peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, &inbuffer, sizeof(inbuffer)-1, 0, (struct sockaddr*)&peer, &len);
            if(n > 0)
            {
                inbuffer[n] = 0;
                std::cout << "clint say# " << inbuffer << std::endl;
                std::string echo_string = "[udp_server echo] #";
                echo_string += inbuffer;

                sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&peer, len);
            }
        }
    }


    ~UdpServer()
    {
        if(_sockfd > gsockfd) ::close(_sockfd);
    }

private:
    int _sockfd;
    uint16_t _localport;
    std::string _localip;
    bool _isrunning;
};
cpp 复制代码
//udpserver.cc
#include "UdpServer.hpp"
#include  <memory>

int main()
{
    uint16_t port = 8899;
    std::string ip = "0";
    std::unique_ptr<UdpServer> user = std::make_unique<UdpServer>(ip, port);
    user->InitServer();
    user->Start();
    return 0;
}
cpp 复制代码
//udpclient.cc


#include <iostream>
#include <unistd.h>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// 客户端得知道服务器的 IP 地址和端口号才能操作
// 因此,在调用客户端的时候,可以附上服务端的IP和port。
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        std::cerr <<"Usage: " << argv[0] << "server-ip server-port " << std::endl;
        exit(0);
    }

    //拿到服务器的IP和端口
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    //1 创建套接字
    int sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0)
    {
        std::cerr << "client socket error!"<< std::endl;
        exit(1);
    }

    //2 bind 绑定客户端的IP和端口
    // client的端口不会被用户去设定,而是OS去随机选择端口

    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(serverport);
    server.sin_addr.s_addr = inet_addr(serverip.c_str());

    while(1)
    {
        std::string line;
        std::cout << "Please Enter: ";
        std::getline(std::cin, line);

        int n = sendto(sockfd, line.c_str(), line.size(), 0, (struct sockaddr*)&server, sizeof(server));
        if(n > 0)
        {
            struct sockaddr_in temp;
            socklen_t len = sizeof(temp);
            char buffer[1024];
            int m = recvfrom(sockfd, &buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&temp, &len);
            if(m>0)
            {
                buffer[m] = 0;
                std::cout << buffer << std::endl;
            }
            else
            {
                std::cout << "recvfrom error" << std::endl;
                break;
            }
        }
        else
        {
            break;
        }

    }
    

    ::close(sockfd);
    return 0;
}
相关推荐
卡戎-caryon13 分钟前
【Linux网络与网络编程】03.UDP Socket编程
linux·服务器·网络·笔记·单例模式·udp·网络通信
饕餮ing14 分钟前
C++的UDP连接解析域名地址错误
开发语言·c++·udp
Taichi呀1 小时前
PHP语言基础
android·开发语言·php
iOS技术狂热者2 小时前
Flutter 音视频播放器与弹幕系统开发实践
websocket·网络协议·tcp/ip·http·网络安全·https·udp
W说编程2 小时前
《UNIX网络编程卷1:套接字联网API》第5章 TCP客户服务器程序示例
c语言·网络·网络协议·tcp/ip·unix·tcp
网络抓包与爬虫4 小时前
flutter WEB端启动优化(加载速度,加载动画)
websocket·网络协议·tcp/ip·http·网络安全·https·udp
网络抓包与爬虫4 小时前
从头开发一个Flutter插件(二)高德地图定位插件
websocket·网络协议·tcp/ip·http·网络安全·https·udp
色的归属感5 小时前
一款功能强大的手机使用情况监控工具
websocket·网络协议·tcp/ip·http·网络安全·https·udp
iOS技术狂热者5 小时前
【Android开发基础】手机传感器信息的获取
websocket·网络协议·tcp/ip·http·网络安全·https·udp
智想天开9 小时前
8.集成模板引擎
php