UdpSocket

相关接口

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

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

这个是套接字函数,套接字实际上就是IP+端口,IP用于标识主机,端口用于标识主机上面的进程

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

domain:一般为AF_INET,表示网络通信

type:取值一般为SOCK_STREAM和SOCK_DGRAM,分别表示TCP和UDP,字面意思,引文tcp面向的是字节流,UDP是数据报,字节流就像水流一样,数据想取多少就取多少,数据报类似于快递,发来的是一整块数据

protocol:一般为0,然系统自动匹配默认协议

cpp 复制代码
struct sockaddr_in {
           sa_family_t     sin_family;     
           in_port_t       sin_port;       
           struct in_addr  sin_addr;       
};

这个表示IPV4,其中用

sin_family:协议家族,一般设置成为AF_INET,表示要进行网络通信

sin_port:端口号

sin_addr:ip号

这个通常和bind函数一起使用,用来绑定自己的设置自己的IP号和端口号,但是使用的时候注意,当在设置的时候需要将端口号转换成为网络字节序,网络字节序是大端,我们自己的IP地址为string类型的,使用的时候也需要将其转化整数,主要用到下面的函数

cpp 复制代码
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);         //主机转网络,且转为long long
uint16_t htons(uint16_t hostshort);        //主机转网络,且转为short
uint32_t ntohl(uint32_t netlong);          //网络转主机,且转为long long
uint16_t ntohs(uint16_t netshort);         //网络转主机,且为short

上面的函数非常好记忆:
比如:htonl 是 host to net long long 的缩写

然后就是IP地址的转化

cpp 复制代码
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp); 

成功返回合法Ip,失败返回0
比如:将字符传的IP转化成为网络序号Ip存到addr中
iner_aton("127.0.0.1",&addr.sin_addr);

in_addr_t inet_addr(const char *cp);//传入字符串的IP,返回网络序列的IP
    
in_addr_t inet_network(const char *cp);
将字符串的IP转化成为主机序列的IP,所以后又需要化成为网络序列

下面介绍bind

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

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

这个就是将我们设置好的IP和端口号绑定到操作系统,成功返回0,失败返回-1

ssokfd:通过调用socket函数返回的文件描述符

addr:就是我们的sockaddr_in,使用的时候需要强转为const struct addr

addlen:addr的大小

接受消息的函数

cpp 复制代码
 #include <sys/socket.h>
 ssize_t recvfrom(int sockfd, void buf[restrict .len], size_t len,
                        int flags,
                        struct sockaddr *_Nullable restrict src_addr,
                        socklen_t *_Nullable restrict addrlen);

成功:返回实际收到的信息的长度,失败:返回-1

sockfd:自己通过sockfd函数创建的

buf:输出型的参数,收到的消息放到这里面

len:buf的长度

flags:为0,表示阻塞

src_addr:输出型的参数,表示从那个主机收到的信息

srclen:src_addr的长度

发送消息的函数

cpp 复制代码
 #include <sys/socket.h>
ssize_t sendto(int sockfd, const void buf[.len], size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);

sockfd:自己通过sockfd函数创建的

buf:输出型的参数,发送的消息放到这里面

len:buf的长度

flags:为0,表示阻塞

src_addr:输出型的参数,表示想发给哪个主机

srclen:src_addr的长度

代码

服务端

hpp文件

cpp 复制代码
#pragma once
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "SockAddr.hpp"

uint16_t DefaultPort = 8080;
std::string DefaultIp = "127.0.0.1";

class Udp_Server
{
public:
    Udp_Server(const uint16_t Port = DefaultPort, const std::string Ip = DefaultIp)
        :_Port(Port)
        ,_Ip(Ip)
        ,_IsRuning(false)
    {

    }
    void Init()
    {
        _SockFd = socket(AF_INET,SOCK_DGRAM,0);                   //初始化sockfd
        if(_SockFd < 0)
        {
            std::cout << "sockrt error:" << strerror(errno) << std::endl; 
            exit(1);
        }
        std::cout << "socket create success" << std::endl;
        //绑定
        sockaddr_in Local;
        bzero(&Local,sizeof(Local));           //先将里面的字段全部清零,保险
        Local.sin_family = AF_INET;            //类型必须和上面socket一样才能绑定成功
        Local.sin_port = htons(_Port);         //转成网络字节流
        Local.sin_addr.s_addr = inet_addr(_Ip.c_str());
        Local.sin_addr.s_addr = INADDR_ANY;
        int n = bind(_SockFd,(const struct sockaddr*)&Local,sizeof(Local));//绑定到操作系统
        if(n < 0)
        {
            std::cout << "bind error:" << strerror(errno) << std::endl;
            exit(2);
        }
        std::cout << "bind sucess" << std::endl;
    }

    void Start()
    {
        _IsRuning = true;
        while(true)
        {
            char Buffer[1024];
            sockaddr_in src;
            socklen_t len = sizeof(src);
            int n = recvfrom(_SockFd,Buffer,sizeof(Buffer)-1,0,(sockaddr*)&src,&len);                   //接受消息
            if(n > 0)
            {
                Buffer[n] = 0;
                std::cout << "From Client: " << Buffer << std::endl;                                    //接受成功发送消息
                std::string SendMessage = "return#: ";
                SendMessage += Buffer;
                socklen_t Size = sendto(_SockFd,SendMessage.c_str(),SendMessage.size(),0,(sockaddr*)&src,sizeof(src)); 
            }
        }
    }
private:
    int _SockFd;        //套接字文件描述符号  
    uint16_t _Port;     //端口号
    std::string _Ip;    //ip地址
    bool _IsRuning;      //是否运行
};

测试文件

cpp 复制代码
#include <memory>
#include "Udp_Server.hpp"

int main()
{
    std::unique_ptr<Udp_Server> Server = std::make_unique<Udp_Server>();
    Server->Init();
    Server->Start();
    return 0;
}

客户端

cpp 复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Udp_Client.hpp"
#include "SockAddr.hpp"


int main(int argc,char *argv[])
{
    if(argc!=3)
    {
        std::cout << "Usage:./.exe Ip port" << std::endl;
        exit(1);
    }
    std::string Ip = argv[1];
    int Port = std::stoi(argv[2]);
    int SocketFd = socket(AF_INET,SOCK_DGRAM,0);
    if(SocketFd < 0)
    {
        std::cout << "ScoketFd error" << std::endl;
        exit(2);
    }
    //SockAddr SAddr(Ip,Port);
    sockaddr_in SAddr;
    SAddr.sin_family = AF_INET;
    SAddr.sin_addr.s_addr = inet_addr(Ip.c_str());
    SAddr.sin_port = htons(Port);
    while(true)
    {
        std::cout << "Please send#";
        std::string SendMessage;
        std::getline(std::cin,SendMessage);
        ssize_t n = sendto(SocketFd,SendMessage.c_str(),SendMessage.size(),0,(const struct sockaddr*)&SAddr,sizeof(SAddr));

        char Buffer[1024];
        sockaddr_in src;
        socklen_t len = sizeof(src);
        ssize_t Size = recvfrom(SocketFd,Buffer,sizeof(Buffer)-1,0,(sockaddr*)&src,&len);
        if(Size > 0)
        {
            Buffer[Size] = 0;
            std::cout << Buffer << std::endl;
        }
    }
    return 0;
}

运行结果