【Linux网络编程】Socket编程UDP

【Linux网络编程】Socket编程UDP

  • UDP网络编程
    • [V1版本 - EchoServer](#V1版本 - EchoServer)
    • [V2版本 - DictServer](#V2版本 - DictServer)
    • [V3版本 - 简单聊天室](#V3版本 - 简单聊天室)

UDP网络编程

V1版本 - EchoServer

EchoServer.hpp

cpp 复制代码
#ifndef __ECHO_SERVER_HPP
#define __ECHO_SERVER_HPP

#include <iostream>
#include <cstdlib>
#include <string>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Logger.hpp"

using namespace NS_LOG_MODULE;

const int default_fd = -1;
const int default_port = 6666;

enum
{
    SUCCESS = 0,
    SOCKET_ERR,
    USAGE_ERR,
    BIND_ERR
};

class UdpServer
{
public:
    // UdpServer(const std::string ip, uint16_t port = default_port)
    UdpServer(uint16_t port = default_port)
    : _sockfd(default_fd)
    , _port(port)
    {}
    ~UdpServer()
    {
        close(_sockfd);
    }
    void Init()
    {
        // 第一步:创建socket,本质:打开网卡 -- 系统特性
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 这里的AF_INET说明是要创建一个网络套接字
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create socket error";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "create socket success, sockfd: " << _sockfd;
        // 第二步:填充网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // 将local清零
        local.sin_family = AF_INET; // 这里的AF_INET说明这个结构体子类的类型是网络通信的类型
        local.sin_port = htons(_port); // h->n 主机到网络的转化
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. 字符串IP转4字节IP 2. h->n
        local.sin_addr.s_addr = INADDR_ANY; // 任意IP地址绑定

        // 第三步:bind socket 信息  绑定套接字信息
        int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind socket error";
            exit(BIND_ERR);            
        }
        LOG(LogLevel::INFO) << "bind socket success, port: " << _port;
    }
    void Start()
    {
        // 传递的是字符串,echo server
        char inbuffer[1024];
        while(true)
        {
            // 读消息
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &len);
            // inbuffer:用户提供,保存收到的数据
            // flag = 0 表示阻塞读取
            // peer:接收发送方(客户端)的socket信息,即ip和port
            // len:输入输出型参数,输入时必须设置成传入struct sockaddr_in结构体的大小,输出时返回实际结构体的大小,往往两值相等
            // 返回值:成功返回读取字节数,失败-1
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "recvfrom error";
            }
            else
            {
                uint16_t client_port = ntohs(peer.sin_port);
                std::string client_ip = inet_ntoa(peer.sin_addr);
                std::string client_address = "[" + client_ip + ":" + std::to_string(client_port) + "] say# ";

                inbuffer[n] = 0;
                LOG(LogLevel::DEBUG) << client_address << inbuffer;
                std::string echo_string = "server say# ";
                echo_string += inbuffer;
                // 把收到的字符串再返回给客户端
                sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&peer, len);
            }
        }
    }
private:
    int _sockfd;
    // std::string _ip; // "192.168.2.2"(字符串风格的点分十进制IP地址,让人看的) && 4字节IP
    uint16_t _port; // 用户设置好的,server port必须是固定的
};

#endif

EchoServerMain.cc

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

static void Usage(const std::string process)
{
    std::cerr << "Usage:\n\t" << process << " local_port" << std::endl;
}

// ./server_udp server_port
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    ENABLE_CONSOLE_LOG_STRATEGY();

    uint32_t server_port = std::stoi(argv[1]);
    std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(server_port);
    
    usvr->Init();
    usvr->Start();

    return 0;
}

EchoClient.cc

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

static void Usage(const std::string& process)
{
    std::cout << "Usage:\n\t" << process << " server_ip server_port" << std::endl;
}

// client怎么知道server的IP和Port?server被内置到了client
// ./client_udp server_ip server_port
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);

    // 1. 创建socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0)
    {
        std::cerr << "create socket error" << std::endl;
        exit(2);
    }

    // 2. 构建server端socket信息
    // client 需要有自己的IP和Port吗?需要
    // 需要显式的bind自己的IP和Port吗?不要显式bind
    // 1) 为什么不让client显式bind?client bind port 可能会出现冲突,client port只需要具有唯一性即可,具体是多少不重要
    // 2) 如何设置自己的IP和Port呢?client 一般会采用随机端口的方式,由OS自主选择
    // udp client 首次发送数据的时候,OS底层会隐式自动帮client进行获取随机端口,然后 bind + Port + IP
    
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(server_port);

    while(true)
    {
        std::string message;
        // 1. 获取用户输入
        std::cout << "Please Enter# ";
        std::getline(std::cin, message);

        // 2. client 发送数据给 server,首次发送即自动绑定
        ssize_t n = sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
        if(n > 0)
        {
            // recvfrom
            char inbuffer[1024] = {0};
            struct sockaddr_in temp;
            socklen_t len = sizeof(temp);
            ssize_t m = recvfrom(sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&temp, &len);
            if(m > 0)
            {
                inbuffer[m] = 0;
                std::cout << inbuffer << std::endl;
            }
        }
    }

    return 0;
}

V2版本 - DictServer

dict.txt

cpp 复制代码
apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天

Dict.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <unordered_map>
#include "Logger.hpp"

using namespace NS_LOG_MODULE;
static const std::string default_path = "./dict.txt";
static const std::string sep = ": ";

class Dict
{
public:
    Dict(const std::string& dict_path = default_path)
    :_dict_path(dict_path)
    {
        LoadDict();
    }
    ~Dict(){}
    void LoadDict()
    {
        std::fstream in(_dict_path);
        if(!in.is_open())
        {
            LOG(LogLevel::FATAL) << "open " << _dict_path << " error";
            exit(1);
        }
        std::string line;
        while(std::getline(in, line))
        {
            LOG(LogLevel::DEBUG) << "load: " << line << " success";
            // apple: 苹果
            auto pos = line.find(sep);
            if(pos == std::string::npos)
            {
                LOG(LogLevel::WARNING) << "Format: " << line << " error";
                continue;
            }

            std::string k = line.substr(0, pos);
            std::string v = line.substr(pos + sep.size());
            _dict.insert(std::make_pair(k, v));
        }

        in.close();
        LOG(LogLevel::INFO) << "load done...";
    }
    std::string Translate(std::string word)
    {
        auto iter = _dict.find(word);
        if(iter != _dict.end())
        {
            return iter->second;
        }
        else
        {
            return "None";
        }
    }
private:
    std::string _dict_path;
    std::unordered_map<std::string, std::string> _dict;
};

DictServer.hpp

cpp 复制代码
#ifndef __DICT_SERVER_HPP
#define __DICT_SERVER_HPP

#include <iostream>
#include <cstdlib>
#include <string>
#include <strings.h>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Logger.hpp"

using namespace NS_LOG_MODULE;

const int default_fd = -1;
const int default_port = 6666;

using callback_t = std::function<std::string(std::string)>;

enum
{
    SUCCESS = 0,
    SOCKET_ERR,
    USAGE_ERR,
    BIND_ERR
};

class UdpServer
{
public:
    // UdpServer(const std::string ip, uint16_t port = default_port)
    UdpServer(callback_t cb, uint16_t port = default_port)
    : _cb(cb)
    , _sockfd(default_fd)
    , _port(port)
    {}
    ~UdpServer()
    {
        close(_sockfd);
    }
    void Init()
    {
        // 第一步:创建socket,本质:打开网卡 -- 系统特性
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 这里的AF_INET说明是要创建一个网络套接字
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create socket error";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "create socket success, sockfd: " << _sockfd;
        // 第二步:填充网络信息
        struct sockaddr_in local;
        bzero(&local, sizeof(local)); // 将local清零
        local.sin_family = AF_INET; // 这里的AF_INET说明这个结构体子类的类型是网络通信的类型
        local.sin_port = htons(_port); // h->n 主机到网络的转化
        // local.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. 字符串IP转4字节IP 2. h->n
        local.sin_addr.s_addr = INADDR_ANY; // 任意IP地址绑定

        // 第三步:bind socket 信息  绑定套接字信息
        int n = bind(_sockfd, (struct sockaddr*)&local, sizeof(local));
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "bind socket error";
            exit(BIND_ERR);            
        }
        LOG(LogLevel::INFO) << "bind socket success, port: " << _port;
    }
    void Start()
    {
        // 传递的是字符串,echo server
        char inbuffer[1024];
        while(true)
        {
            // 读消息
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&peer, &len);
            // inbuffer:用户提供,保存收到的数据
            // flag = 0 表示阻塞读取
            // peer:接收发送方(客户端)的socket信息,即ip和port
            // len:输入输出型参数,输入时必须设置成传入struct sockaddr_in结构体的大小,输出时返回实际结构体的大小,往往两值相等
            // 返回值:成功返回读取字节数,失败-1
            if(n < 0)
            {
                LOG(LogLevel::ERROR) << "recvfrom error";
            }
            else
            {
                uint16_t client_port = ntohs(peer.sin_port);
                std::string client_ip = inet_ntoa(peer.sin_addr);
                std::string client_address = "[" + client_ip + ":" + std::to_string(client_port) + "] say# ";

                inbuffer[n] = 0;
                LOG(LogLevel::DEBUG) << client_address << inbuffer;
                // std::string echo_string = "server say# ";
                // echo_string += inbuffer;
                std::string result = _cb(inbuffer); // 单词

                // 把收到的字符串再返回给客户端
                // sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&peer, len);
                sendto(_sockfd, result.c_str(), result.size(), 0, (struct sockaddr*)&peer, len);
            }
        }
    }
private:
    int _sockfd;
    // std::string _ip; // "192.168.2.2"(字符串风格的点分十进制IP地址,让人看的) && 4字节IP
    uint16_t _port; // 用户设置好的,server port必须是固定的
    callback_t _cb; // 用回调的方式进行数据加工
};

#endif

DictServerMain.cc

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

static void Usage(const std::string process)
{
    std::cerr << "Usage:\n\t" << process << " local_port" << std::endl;
}

// ./server_udp server_port
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    ENABLE_CONSOLE_LOG_STRATEGY();

    // 1. 定义字典
    Dict dict;

    // 2. 构建网络服务,处理IO问题
    uint32_t server_port = std::stoi(argv[1]);
    // 3. 绑定上下两层
    std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>([&dict](std::string word)->std::string{
        return dict.Translate(word);
    }, server_port);
    
    usvr->Init();
    usvr->Start();

    return 0;
}

DictClient.cc

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

static void Usage(const std::string& process)
{
    std::cout << "Usage:\n\t" << process << " server_ip server_port" << std::endl;
}

// client怎么知道server的IP和Port?server被内置到了client
// ./client_udp server_ip server_port
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    std::string server_ip = argv[1];
    uint16_t server_port = std::stoi(argv[2]);

    // 1. 创建socket
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sockfd < 0)
    {
        std::cerr << "create socket error" << std::endl;
        exit(2);
    }

    // 2. 构建server端socket信息
    // client 需要有自己的IP和Port吗?需要
    // 需要显式的bind自己的IP和Port吗?不要显式bind
    // 1) 为什么不让client显式bind?client bind port 可能会出现冲突,client port只需要具有唯一性即可,具体是多少不重要
    // 2) 如何设置自己的IP和Port呢?client 一般会采用随机端口的方式,由OS自主选择
    // udp client 首次发送数据的时候,OS底层会隐式自动帮client进行获取随机端口,然后 bind + Port + IP
    
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr(server_ip.c_str());
    server.sin_port = htons(server_port);

    while(true)
    {
        std::string message;
        // 1. 获取用户输入
        std::cout << "Please Enter# ";
        std::getline(std::cin, message);

        // 2. client 发送数据给 server,首次发送即自动绑定
        ssize_t n = sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
        if(n > 0)
        {
            // recvfrom
            char inbuffer[1024] = {0};
            struct sockaddr_in temp;
            socklen_t len = sizeof(temp);
            ssize_t m = recvfrom(sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&temp, &len);
            if(m > 0)
            {
                inbuffer[m] = 0;
                std::cout << inbuffer << std::endl;
            }
        }
    }

    return 0;
}

V3版本 - 简单聊天室

UdpServer.hpp

cpp 复制代码
#ifndef __UDP_SERVER_HPP
#define __UDP_SERVER_HPP

#include <iostream>
#include <cstdlib>
#include <string>
#include <strings.h>
#include <functional>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "InetAddr.hpp"
#include "Logger.hpp"

using namespace NS_LOG_MODULE;

const int default_fd = -1;
const int default_port = 6666;

using handler_addr_t = std::function<void(const InetAddr &)>;
using handler_msg_t = std::function<void(int sockfd, std::string msg)>;

enum
{
    SUCCESS = 0,
    SOCKET_ERR,
    USAGE_ERR,
    BIND_ERR
};

class UdpServer
{
public:
    UdpServer(uint16_t port = default_port)
        : _sockfd(default_fd), _port(port)
    {
    }
    ~UdpServer()
    {
        close(_sockfd);
    }
    void Init()
    {
        // 第一步:创建socket,本质:打开网卡 -- 系统特性
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 这里的AF_INET说明是要创建一个网络套接字
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create socket error";
            exit(SOCKET_ERR);
        }
        LOG(LogLevel::INFO) << "create socket success, sockfd: " << _sockfd;
        // 第二步:填充网络信息
        InetAddr local(_port);

        // 第三步:bind socket 信息  绑定套接字信息
        int n = bind(_sockfd, (struct sockaddr *)(local.GetNetAddress()), local.Len());
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind socket error";
            exit(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind socket success, port: " << _port;
    }
    void RegisterService(handler_addr_t handler_addr, handler_msg_t handler_msg)
    {
        _handler_addr = handler_addr;
        _handler_msg = handler_msg;
    }
    void Start()
    {
        // 传递的是字符串,echo server
        char inbuffer[1024];
        while (true)
        {
            // 读消息
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&peer, &len);
            // inbuffer:用户提供,保存收到的数据
            // flag = 0 表示阻塞读取
            // peer:接收发送方(客户端)的socket信息,即ip和port
            // len:输入输出型参数,输入时必须设置成传入struct sockaddr_in结构体的大小,输出时返回实际结构体的大小,往往两值相等
            // 返回值:成功返回读取字节数,失败-1
            if (n < 0)
            {
                LOG(LogLevel::ERROR) << "recvfrom error";
            }
            else
            {
                inbuffer[n] = 0;
                // 1. 检测新用户
                InetAddr clientaddress(peer);
                std::string tips = clientaddress.ToString();
                std::string message = tips + inbuffer;
                LOG(LogLevel::DEBUG) << message;
                _handler_addr(clientaddress);
                // 2. 转发消息
                _handler_msg(_sockfd, message);
            }
        }
    }

private:
    int _sockfd;
    // std::string _ip; // "192.168.2.2"(字符串风格的点分十进制IP地址,让人看的) && 4字节IP
    uint16_t _port;               // 用户设置好的,server port必须是固定的
    handler_addr_t _handler_addr; // 处理地址
    handler_msg_t _handler_msg;   // 处理消息
};

#endif

InetAddr.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


// 对客户端进行先描述
class InetAddr
{
public:
    InetAddr(const struct sockaddr_in& address):_address(address), _len(sizeof(address))
    {
        _port = ntohs(_address.sin_port);
        _ip = inet_ntoa(_address.sin_addr);        
    }
    InetAddr(uint16_t port, const std::string& ip = "0.0.0.0"):_port(port), _ip(ip)
    {
        bzero(&_address, sizeof(_address)); // 将_address清零
        _address.sin_family = AF_INET; // 这里的AF_INET说明这个结构体子类的类型是网络通信的类型
        _address.sin_port = htons(_port); // h->n 主机到网络的转化
        _address.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. 字符串IP转4字节IP 2. h->n
        _len = sizeof(_address);
    }
    InetAddr()
    {}
    bool operator==(const InetAddr& addr)
    {
        return (this->_ip == addr._ip) && (this->_port == addr._port);
    }
    std::string ToString()
    {
        return "[" + this->_ip + ":" + std::to_string(this->_port) + "]";
    }
    struct sockaddr_in* GetNetAddress()
    {
        return &_address;
    }
    socklen_t Len()
    {
        return _len;
    }
    ~InetAddr()
    {}
private:
    // net address
    struct sockaddr_in _address;
    socklen_t _len;
    // host address
    std::string _ip;
    uint16_t _port;
};

UserManager.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <vector>
#include "InetAddr.hpp"
#include "Logger.hpp"

using namespace NS_LOG_MODULE;

// 管理用户,增删查改
class UserManager
{
public:
    UserManager(){}
    void AddUser(const InetAddr& addr)
    {
        if(SearchUser(addr))
            return;
        _users.push_back(addr);
    }
    void DelUser(const InetAddr& addr)
    {
        for(auto iter = _users.begin(); iter != _users.end(); iter++)
        {
            if(*iter == addr)
            {
                _users.erase(iter);
                break;
            }
        }
    }
    bool SearchUser(const InetAddr& addr)
    {
        for(auto& user: _users)
        {
            if(user == addr)
            {
                return true;
            }
        }
        return false;
    }
    void ModUser(const InetAddr& addr)
    {
        DelUser(addr);
        AddUser(addr);
    }
    std::vector<InetAddr>& Users()
    {
        return _users;
    }
    ~UserManager(){}
private:
    std::vector<InetAddr> _users;
};

Route.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <sys/socket.h>
#include "UserManager.hpp"
#include "Mutex.hpp"

// 路由:完成转发功能
class Route
{
public:
    Route():_uma(std::make_unique<UserManager>())
    {}
    void CheckUser(const InetAddr& addr)
    {
        LockGuard lockguard(_lock);
        _uma->AddUser(addr);
    }
    void OfflineUser(const InetAddr& addr)
    {
        LockGuard lockguard(_lock);
        _uma->DelUser(addr);
    }
    void Broadcast(int sockfd, std::string message)
    {
        LockGuard lockguard(_lock);
        auto& users = _uma->Users();
        for(auto& user : users)
        {
            sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)user.GetNetAddress(), user.Len());
        }
    }
    ~Route(){}
private:
    std::unique_ptr<UserManager> _uma;
    Mutex _lock;
};

ChatServerMain.cc

cpp 复制代码
#include "ThreadPool.hpp" // 执行处理事件
#include "Route.hpp" // 任务
#include "UdpServer.hpp" // 获取事件
#include <memory>

static void Usage(const std::string process)
{
    std::cerr << "Usage:\n\t" << process << " local_port" << std::endl;
}

using namespace NS_THREAD_POOL_MODULE;

using task_t = std::function<void()>;

// ./server_udp server_port
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERR);
    }

    ENABLE_CONSOLE_LOG_STRATEGY();
    uint16_t server_port = std::stoi(argv[1]);
    ThreadPool<task_t>* thread_pool = ThreadPool<task_t>::Instance(); // 线程池模块
    Route r; // 路由模块
    UdpServer usvr(server_port); // 网络模块

    usvr.Init();
    usvr.RegisterService(
        [&r](const InetAddr& addr){
            r.CheckUser(addr);
        }, 
        [&r, thread_pool](int sockfd, std::string msg){
            auto t = std::bind(&Route::Broadcast, &r, sockfd, msg);
            thread_pool->Enqueue(t);
        });
    usvr.Start();

    return 0;
}

ChatClient.cc

cpp 复制代码
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "Thread.hpp"
#include "InetAddr.hpp"

using namespace NS_THREAD_MODULE;

int sockfd;
std::string server_ip;
uint16_t server_port;
std::string nickname;

static void Usage(const std::string &process)
{
    std::cout << "Usage:\n\t" << process << " server_ip server_port" << std::endl;
}

static void Online(InetAddr& serveraddr)
{
    std::cout << "Plaease Set Your Nick Name# ";
    std::getline(std::cin, nickname);
    std::string online_message = nickname + " online!";
        ssize_t n = sendto(sockfd, online_message.c_str(), online_message.size(), 0, (struct sockaddr *)serveraddr.GetNetAddress(), serveraddr.Len());
        (void)n;    
}

void RecvMessage()
{
    while (true)
    {

        // recvfrom
        char inbuffer[1024] = {0};
        struct sockaddr_in temp;
        socklen_t len = sizeof(temp);
        ssize_t m = recvfrom(sockfd, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr *)&temp, &len);
        if (m > 0)
        {
            inbuffer[m] = 0;
            std::cerr << inbuffer << std::endl; // 2
        }
    }
}

void SendMessage()
{
    InetAddr serveraddr(server_port, server_ip);
    Online(serveraddr);

    while (true)
    {
        std::string message;
        // 1. 获取用户输入
        std::cout << "Please Enter# "; // 1
        std::getline(std::cin, message);

        message = nickname + "# " + message;

        // 2. client 发送数据给 server,首次发送即自动绑定
        ssize_t n = sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr *)serveraddr.GetNetAddress(), serveraddr.Len());
        (void)n;
    }
}

// client怎么知道server的IP和Port?server被内置到了client
// ./client_udp server_ip server_port
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }
    server_ip = argv[1];
    server_port = std::stoi(argv[2]);

    // 1. 创建socket
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        std::cerr << "create socket error" << std::endl;
        exit(2);
    }

    Thread recver(RecvMessage);
    Thread sender(SendMessage);

    recver.Start();
    sender.Start();

    recver.Join();
    sender.Join();

    return 0;
}
相关推荐
IP老炮不瞎唠1 小时前
SOCKS5 vs HTTP:哪种代理协议更适合你的业务场景
网络·网络协议·http
时光之源2 小时前
程序猿常用命令行(Linux、Windows、Powershell、CMD、conda、pip、apt)
linux·conda·pip
qing222222222 小时前
Linux:/var/log/journal 路径下文件不断增加导致根目录磁盘爆满
linux·运维·前端
lwx9148522 小时前
Linux-后台运行操作符&
linux·运维·服务器
智算菩萨2 小时前
【Pygame】第19章 网络多人游戏基础与局域网联机原理
网络·python·游戏·pygame
mounter6252 小时前
深度解析 Linux 内核 devlink:从硬件控制到跨功能速率调度的演进
linux·运维·服务器·网络·内核
似水এ᭄往昔2 小时前
【Linux】--进程状态
linux·运维·服务器
小跘an吻纸2 小时前
linux系统搭建hadoop环境
linux·运维·hadoop
中科三方2 小时前
HTTP劫持与DNS劫持有什么区别?如何识别和防范?
网络·网络协议·http·dns