最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++基于UDP協議的群聊服務器開發(fā)實現

 更新時間:2025年04月24日 08:33:40   作者:敲上癮  
本文主要介紹了C/C++基于UDP協議的群聊服務器開發(fā),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

服務器

在服務器架構設計中,模塊解耦是保障系統(tǒng)可維護性的核心準則。本方案采用分層架構將核心功能拆解為通信層業(yè)務處理層兩大模塊。值得注意的是,當使用TCP協議時,開發(fā)者往往需要額外設計協議抽象層來解決其字節(jié)流特性導致的消息邊界模糊問題(如粘包/拆包處理),并通過重傳機制強化傳輸可靠性。而UDP的面向報文特性天然規(guī)避了消息邊界問題,其無狀態(tài)傳輸模型大幅簡化了基礎通信層的設計復雜度——這正是我們選擇UDP構建輕量化實時群聊系統(tǒng)的關鍵原因。話不多說,我們直接開始!

框架設計

創(chuàng)建核心文件:UdpServer.hpp、UdpServer.cc、UdpClient.cc,當然不止于這些,在完成這些文件的過程中會延伸出更多文件,如果在這里寫顯得有些突兀。

  • UdpServer.hpp:服務器相關的類以及類方法的實現——主要完成通信功能。
  • UdpServer.cc:服務器主函數(main)的實現——對服務器接口的調用,即啟動服務器。
  • UdpClient.cc:客戶端主函數(main)的實現——啟動客戶端,與服務器通信。

一、通信

上來直接創(chuàng)建一個class UdpServer類,而對于成員變量和成員函數的設定。我們得理一理進行通信需要完成什么,它完全是套路式的,模板化的。即:打開網絡文件,綁定端口收數據_處理數據_發(fā)數據。(針對UDP協議通信)

根據這三點我們設計成員函數:

  • int  _socketfd:網絡文件描述符。
  • uint16_t  _port:端口號。對于IP地址我們不期望從外部傳入,所以暫且不用設。
  • 數據處理函數:這個成員到后文數據處理再設計。

對于成員函數

  • void Init():完成打開網絡文件,綁定端口。
  • void Start():啟動服務,完成收數據_處理數據_發(fā)數據,其中處理數據以回調的方式完成(為了讓模塊解耦,方便模塊之間的拼接和替換)。

如下:

class UdpServer
{
public:
    UdpServer(uint16_t port)
        : _socketfd(-1), _port(port)
    {
    }
    void Init();
    void Start();
private:
    int _socketfd;
    uint16_t _port;
    //......
};

void Init ()

1.打開網絡文件

socket的使用

socket函數的聲明:

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

功能:打開網絡文件(套接字)。

參數domain:確定IP地址類型,如IPv4還是IPv6。

  • AF_INET: IPv4。
  • AF_INET6:IPv6。

參數type:確定數據的傳輸方式。

  • SOCK_STREAM: 流式套接字(TCP)。
  • SOCK_DGRAM: 數據報套接字(UDP)。

參數protocol:確定協議類型,如果前面type已經能確定了,這里傳入0即可。

返回值:

  • 成功:文件描述符。
  • 失?。阂粋€小于0的數。

代碼示例:

// 打開網絡文件 IPv4 數據包 udp
_socketfd = socket(AF_INET, SOCK_DGRAM, 0);
if (_socketfd < 0)
{
    LOG(Level::ERROR) << "socket() fail";
    exit(1);
}
else
{
    LOG(Level::INFO) << "socket() succee _socketfd:" << _socketfd;
}

說明:LOG是我寫的一個打印日志的接口, 大家把它當作cout理解就行,當然需要日志源碼的可以私信我。

2.綁定ip地址與端口號

sockaddr_in結構

首先我們需要了解sockaddr_in結構,IP地址和端口號等信息要包裝在這個結構里面,然后使用bind函數綁定。

sockaddr_in是用于 IPv4 網絡編程 的一個核心數據結構,用于存儲套接字地址信息(IP地址和端口號),除此之外還有sockaddr_in6(IPv6),sockaddr_un(本地通信),sockaddr(用來屏蔽包括但不止于以上三種結構的底層實現)。

sockaddr_in結構如下:

#include <netinet/in.h>

struct sockaddr_in {
    sa_family_t    sin_family;   // 地址族(Address Family)
    in_port_t      sin_port;     // 端口號(Port Number)
    struct in_addr sin_addr;     // IPv4 地址(IP Address)
    char           sin_zero[8];  // 填充字段(Padding)
};

// IPv4 地址結構(嵌套在 sockaddr_in 中)
struct in_addr {
    in_addr_t s_addr;  // 32位IPv4地址(網絡字節(jié)序)
};

創(chuàng)建 sockaddr_in 對成員進行設定:

  • sin_family:我們設為AF_INET,即IPv4。
  • sin_port:使用成員變量_port,但需要使用函數htons轉為網絡字節(jié)序(即大端)。
  • sin_addr:IP地址通常都是點分十進制的字符串,所以需要把IP轉成4字節(jié),然后4字節(jié)轉成網絡序列,庫提供了inet_addr函數,可以完成這個功能。不過這里我們把它直接設為INADDR_ANY,表示本主機上的所有IP都綁定到服務器,這樣的話外部客戶端連接任意IP都能連接到該主機。
  • 最后一個成員暫且用不著,不用管。

bind函數的使用

bind聲明

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

功能:用來綁定端口。

參數sockfd:要綁定的套接字描述符(由 socket() 函數創(chuàng)建)。

參數sockaddr:指向地址結構體的指針,包含綁定的IP地址和端口號。

參數addrlen:地址結構體的長度(單位:字節(jié))。

返回值:

  • 0:成功。
  • 非0:失敗。

代碼示例:

sockaddr_in sd;
bzero(&sd, sizeof(sd));//初始化為0
sd.sin_family = AF_INET;
sd.sin_port = htons(_port);
// sd.sin_addr.s_addr = inet_addr(_ip.c_str());
sd.sin_addr.s_addr = INADDR_ANY;
//  綁定ip地址與端口號
int n = bind(_socketfd, (const sockaddr *)&sd, sizeof(sd));
if (n != 0)
{
    LOG(Level::FATAL) << "bind fial";
    exit(1);
}
else
{
    LOG(Level::INFO) << "bind success";
}

由于后面會對sockaddr_in頻繁操作,所以在這里封裝一個InetAddr類放在InetAddr文件里,這里就不講解具體的細節(jié)了,如下:

class InetAddr
{
public:
    InetAddr(){}
    InetAddr(sockaddr_in &peer)
        : _addr(peer)
    {
        _port = ntohs(peer.sin_port);//網絡序列轉為主機序列
        _ip = inet_ntoa(peer.sin_addr);//4字節(jié)轉為點分十進制
    }
    InetAddr(uint16_t port, string ip)
        : _port(port), _ip(ip)
    {
        _addr.sin_family = AF_INET;
        _addr.sin_port = htons(_port);
        _addr.sin_addr.s_addr = inet_addr(_ip.c_str());
    }
    string tostring_port()
    {
        return to_string(_port);
    }
    string tostring_ip()
    {
        return _ip;
    }
    bool operator==(InetAddr addr)
    {
        return _port == addr._port && _ip == addr._ip;
    }
    sockaddr_in &getaddr()
    {
        return _addr;
    }
private:
    uint16_t _port;
    string _ip;
    sockaddr_in _addr;
};

void start ()

3.接收信息

UDP協議數據的接收使用的是recvfrom函數,recvfrom函數的使用:

recvfrom函數聲明:

ssize_t recvfrom(
    int sockfd,          // 套接字描述符
    void *buf,           // 接收數據的緩沖區(qū)
    size_t len,          // 緩沖區(qū)最大長度
    int flags,           // 標志位(通常設為0)
    struct sockaddr *src_addr, // 發(fā)送方的地址信息(可選)
    socklen_t *addrlen   // 地址結構體的長度(輸入輸出參數)
);
sockfdUDP套接字的網絡文件描述符(需已綁定端口)。
buf指向接收數據的緩沖區(qū),用于存儲接收到的數據。
len緩沖區(qū)的最大容量(單位:字節(jié)),防止數據溢出。
flags控制接收行為的標志位,常用值:
0(默認阻塞)、MSG_DONTWAIT(非阻塞)。
src_addr指向 struct sockaddr 的指針,用于存儲發(fā)送方的地址信息(IP和端口)。若不需要可設為 NULL。
addrlen輸入時為 src_addr 結構體的長度,輸出時為實際地址長度。需初始化為 sizeof(struct sockaddr)。
返回值含義
>0成功接收的字節(jié)數。
0(僅TCP有意義,UDP一般不會返回0)。
-1發(fā)生錯誤,檢查 errno 獲取具體原因

注:千萬不要把時間花在記函數參數列表上,這么多函數你是記不了的,只需要看懂就行,函數的參數列表在編譯器上通常都會有提示的。只需要把鼠標指針停留在對應的函數名上,如下:

代碼示例: 

while (true)
{
    // 收各個客戶端發(fā)來的消息
    sockaddr_in client;
    socklen_t len = sizeof(client);
    char buffer[1024];
    int n = recvfrom(_socketfd, buffer, sizeof(buffer), 0, (sockaddr *)&client, &len);
    buffer[n] = '\0';
    //回調函數處理數據
    //......
}

到這里為止通信問題就解決了,只需要靜等客戶端發(fā)數據就行。接下來就是數據處理。 

二、數據處理

別忘了我們要做的是群聊服務器,剛才我們不管三七二十一先把通信問題解決,這個的做法是很正確的,因為通信本來就是一個模板化的問題,其次它和其他模塊是解耦的,在編寫過程中并不用考慮數據怎么處理。

群聊服務器如何實現?

原理很簡單,把一個客戶發(fā)來的消息再發(fā)送給與它連接的所有客戶。

我們需要做什么呢?

把與它連接的所有客戶的IP和端口號(InetAddr)都存儲起來,當有客戶給它服務器發(fā)信息,服務器再把信息轉發(fā)給所有客戶。

這個功能我們單獨做一個類Route,用來做消息路由,放在新建頭文件Route.hpp里。

Route的實現很簡單,只需要一個成員函數用來收發(fā)數據,一個成員變量用來存儲與它連接的客戶端信息。如下:

class Route
{
public:
    Route(){}
    void Handler(int socketfd,string message,InetAddr client);
private:
    vector<InetAddr> _data;
};

因為收數據的功能在通信模塊已經做了,直接讓它把網絡文件描述符,數據和客戶端信息傳給Handler就行。其次做兩個小函數,Push:把客戶端信息插入數組,Pop:把客戶端信息移除數組。

代碼示例:

class Route
{
private:
    void Push(InetAddr peer)
    {
        for (auto val : _data)
        {
            //如果已經有了,就直接退出
            if (val == peer) return;
        }
        _data.push_back(peer);
        LOG(Level::INFO)<<peer.tostring_ip()<<'|'<<peer.tostring_port()<<" online";
    }
    bool Pop(InetAddr peer)
    {
        //用戶退出連接后,把它移除數組
        _data.erase(peer);
        return true;
    }

public:
    Route(){}
    void Handler(int socketfd,string message,InetAddr client)
    {
        Push(client);
        // 誰發(fā)的信息要知道吧?所以添加客戶的信息
        string send_message = client.tostring_ip() + " | " + client.tostring_port() + ": ";
        send_message += message;
        // 發(fā)給所有在線的客戶端
        for (auto val : _data)
        {
            if(val == client) continue;
            sendto(socketfd, send_message.c_str(), send_message.size(), 0, (sockaddr *)&val.getaddr(), sizeof(val.getaddr()));
        }
    }
private:
    vector<InetAddr> _data;
};

sendto接口和recvfrom很類似,如下:

sendto的聲明:

ssize_t sendto(
    int sockfd,                // 套接字描述符
    const void *buf,           // 待發(fā)送數據的緩沖區(qū)
    size_t len,                // 數據長度(字節(jié))
    int flags,                 // 控制標志(通常設為0)
    const struct sockaddr *dest_addr, // 目標地址(IP和端口)
    socklen_t addrlen          // 目標地址結構體的長度
);
參數詳解
sockfdUDP 套接字的描述符(無需提前連接)。
buf指向待發(fā)送數據的緩沖區(qū)(如字符串、二進制數據)。
len數據的實際長度(單位:字節(jié))。
flags控制發(fā)送行為的標志位,常用值:
0(默認阻塞)、MSG_DONTWAIT(非阻塞)。
dest_addr指向目標地址的結構體(如 sockaddr_in),需強制轉換為 sockaddr*。
addrlen目標地址結構體的長度(如 sizeof(struct sockaddr_in))。

返回值

返回值含義
>0成功發(fā)送的字節(jié)數(可能與 len 不同,需檢查是否完全發(fā)送)。
-1發(fā)送失敗,檢查 errno 獲取具體錯誤原因。

這里有個很尷尬的事,服務器并不知道客戶端什么時候退出,可以說是UDP協議的特點吧,所以Pop函數什么時候調用并不知道,除非客戶在退出時給服務器發(fā)一條特殊信息表明客戶要退出。這里先這樣。

現在為止服務器相關的通信接口和數據處理方法已經準備好了,接下來實現UdpClient.cc文件,即main函數,把服務器調用起來。

主要實現以下幾點:

  • 要給服務器設定端口號,需要從程序外部傳入,即命令行參數
  • 創(chuàng)建數據處理的類(Route)。
  • 創(chuàng)建服務器,把端口號和數據處理方法(即回調方法)傳入,啟動服務器。

1,2比較簡單,接下來講解第3點。

還記得開頭在UdpServer里我們缺少的成員變量數據處理函數嗎?現在我們知道它是誰了,即:

  • void Handler(int socketfd,string message,InetAddr client)

這里我們寫規(guī)范一點,聲明一個類型:

  • using funcType = function<void(int, string, InetAddr)>;

然后添加成員變量funcType _func,并在構造函數的參數列表進行初始化

最后在Start中調用_func函數(即回調),如下:

void Start()
{
    while (true)
    {
        // 收各個客戶端發(fā)來的消息
        sockaddr_in client;
        socklen_t len = sizeof(client);
        char buffer[1024];
        int n = recvfrom(_socketfd, buffer, sizeof(buffer), 0, (sockaddr *)&client, &len);
        buffer[n] = '\0';
        _func(_socketfd,string(buffer),InetAddr(client));

    }
}

可優(yōu)化點:把_func當作任務,推入線程池。

現在創(chuàng)建UdpServer兩個參數,一個是port(端口號),另一個是func(數據處理方法),對于func我們可以以lambda表達式的方式傳入。如下:

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cerr << "Usage" << argv[0] << "port" << std::endl;
        return 1;
    }
    std::string port = argv[1];
    // 路由
    Route rt;
    // 通信
    unique_ptr<UdpServer> us = make_unique<UdpServer>(stoi(port), [&](int socketfd, string meassge, InetAddr client)
                                                      { rt.Handler(socketfd, meassge, client); });
    us->Init();
    us->Start();
    return 0;
}

 客戶端 

框架設計

客戶端將來是要連接服務器的,所以需要傳入服務器IP和端口,而且是從程序外部出入。即給main函數傳入命令行參數。注意判斷參數是否合法。

然后和服務器一樣需要打開網絡文件。如下:

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cerr << "Usage: " << argv[0] << " server_op server_port" << std::endl;
        return 1;
    }
    int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socketfd < 0)
    {
        LOG(Level::ERROR) << "socket() fail";
        exit(1);
    }
    else
    {
        LOG(Level::INFO) << "socket() succee _socketfd:" << socketfd;
    }
    //包裝客戶端信息
    InetAddr addr(std::stoi(argv[2]), argv[1]);
    //信息收發(fā)
    //......
    return 0;
}

三、端口綁定

客戶端的實現可比服務器簡單多了,因為它不需要我們手動綁定IP和端口號,系統(tǒng)幫我們做了。但要清楚我們是可以自己綁定的,不過會有很多問題,比如主機里有很多進程,可能端口號會綁重,讓系統(tǒng)自動分配比較安全。

那服務器的端口號為什么不也讓系統(tǒng)動態(tài)分配呢?我們自己綁多麻煩。其實是這樣的,服務器是需要供給很多客戶去使用,需要客戶端填寫服務器端口。所以服務器端口一定要明確,系統(tǒng)動態(tài)分配的話,在程序外部就無法知道服務器端口號了。

四、收發(fā)信息

收發(fā)信息是一個不斷重復的操作,所以寫成一個死循環(huán),但要注意不要把收信息和發(fā)信息寫在一起,要不然發(fā)信息阻塞時就收不到信息,收信息阻塞時也發(fā)不了信息。

所以它們應該并發(fā)地進行,即使用兩個子線程。

代碼示例:

void Write(int socketfd, InetAddr &addr)
{
    //提前發(fā)一條信息告訴服務器我已經上線
    string str="online";
    sendto(socketfd, str.c_str(), sizeof(str), 0, (const sockaddr *)&addr.getaddr(), sizeof(addr.getaddr()));
    while (true)
    {
        std::string message;
        cout<<"Please Enter# ";
        std::getline(std::cin, message);
        sendto(socketfd, message.c_str(), sizeof(message), 0, (const sockaddr *)&addr.getaddr(), sizeof(addr.getaddr()));
    }
}
void Read(int socketfd)
{
    while (true)
    {
        sockaddr_in sd;
        char buffer[1024];
        socklen_t len = sizeof(sd);
        int n = recvfrom(socketfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&sd, &len);
        buffer[n] = '\0';
        std::cout << buffer << std::endl;
    }
}

main函數中

thread td_read([&](){
    Write(socketfd,addr);
});
thread td_write([&](){
    Read(socketfd);
});
td_read.join();
td_read.join();

到這里這個工程就完成了,下面是運行結果。

效果展示:

五、源碼

UdpServer.hpp

// 用條件編譯,防止頭文件重復包含
#ifndef UDP_SERVER
#define UDP_SERVER
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <strings.h>
#include <string>
#include <assert.h>
#include <arpa/inet.h>
#include <functional>
#include "InetAddr.hpp"
#include "Log.hpp"
using namespace std;
using namespace my_log;
using funcType = function<void(int, string, InetAddr)>;
class task
{
public:
    task() {}
    task(funcType func, int socketfd, string message, InetAddr client)
        : _func(func), _socketfd(socketfd), _message(message), _client(client)
    {
    }
    void operator()()
    {
        assert(_socketfd != -1);
        _func(_socketfd, _message, _client);
    }

private:
    funcType _func;
    int _socketfd;
    string _message;
    InetAddr _client;
};
class UdpServer
{
public:
    UdpServer(uint16_t port, const funcType &func)
        : _socketfd(-1), _port(port), _func(func)
    {
    }
    void Init()
    {
        // 打開網絡文件 IPv4 數據包 udp
        _socketfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_socketfd < 0)
        {
            LOG(Level::ERROR) << "socket() fail";
            exit(1);
        }
        else
        {
            LOG(Level::INFO) << "socket() succee _socketfd:" << _socketfd;
        }
        sockaddr_in sd;
        bzero(&sd, sizeof(sd));
        sd.sin_family = AF_INET;
        sd.sin_port = htons(_port);
        // sd.sin_addr.s_addr = inet_addr(_ip.c_str());
        sd.sin_addr.s_addr = INADDR_ANY;

        //  綁定ip地址與端口號
        int n = bind(_socketfd, (const sockaddr *)&sd, sizeof(sd));
        if (n < 0)
        {
            LOG(Level::FATAL) << "bind fial";
            exit(1);
        }
        else
        {
            LOG(Level::INFO) << "bind success";
        }

    }
    void Start()
    {
        while (true)
        {
            // 收各個客戶端發(fā)來的消息
            sockaddr_in client;
            socklen_t len = sizeof(client);
            char buffer[1024];
            int n = recvfrom(_socketfd, buffer, sizeof(buffer), 0, (sockaddr *)&client, &len);
            buffer[n] = '\0';

            _func(_socketfd,string(buffer),InetAddr(client));

        }
    }
    ~UdpServer()
    {
    }

private:
    int _socketfd;
    uint16_t _port;
    funcType _func;
};
#endif

UdpServer.cc

#include <iostream>
#include <memory>
#include "UdpServer.hpp"
#include "InetAddr.hpp"
#include "Route.hpp"
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        // LOG(Level::FATAL)<<"input error";
        std::cerr << "Usage" << argv[0] << "port" << std::endl;
        return 1;
    }
    std::string port = argv[1];
    // 路由
    Route rt;
    // 通信
    unique_ptr<UdpServer> us = make_unique<UdpServer>(stoi(port), [&](int socketfd, string meassge, InetAddr client)
                                                      { rt.Handler(socketfd, meassge, client); });
    us->Init();
    us->Start();
    return 0;
}

UdpClient.cc

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <strings.h>
#include <arpa/inet.h>
#include <thread>
#include "InetAddr.hpp"
#include "Log.hpp"
using namespace my_log;
void Write(int socketfd, InetAddr &addr)
{
    string str="online";
    sendto(socketfd, str.c_str(), sizeof(str), 0, (const sockaddr *)&addr.getaddr(), sizeof(addr.getaddr()));
    while (true)
    {
        std::string message;
        cout<<"Please Enter# ";
        std::getline(std::cin, message);
        sendto(socketfd, message.c_str(), sizeof(message), 0, (const sockaddr *)&addr.getaddr(), sizeof(addr.getaddr()));
    }
}
void Read(int socketfd)
{
    while (true)
    {
        sockaddr_in sd;
        char buffer[1024];
        socklen_t len = sizeof(sd);
        int n = recvfrom(socketfd, buffer, sizeof(buffer) - 1, 0, (sockaddr *)&sd, &len);
        buffer[n] = '\0';
        std::cout << buffer << std::endl;
    }
}

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        std::cerr << "Usage: " << argv[0] << " server_op server_port" << std::endl;
        return 1;
    }
    int socketfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (socketfd < 0)
    {
        LOG(Level::ERROR) << "socket() fail";
        exit(1);
    }
    else
    {
        LOG(Level::INFO) << "socket() succee _socketfd:" << socketfd;
    }
    InetAddr addr((uint16_t)std::stoi(argv[2]), argv[1]);
    thread td_read([&](){
        Write(socketfd,addr);
    });
    thread td_write([&](){
        Read(socketfd);
    });
    td_read.join();
    td_read.join();
    return 0;
}

InteAddr.hpp

#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
using namespace std;
class InetAddr
{
public:
    InetAddr(){}
    InetAddr(sockaddr_in &peer)
        : _addr(peer)
    {
        _port = ntohs(peer.sin_port);
        _ip = inet_ntoa(peer.sin_addr);
    }
    InetAddr(uint16_t port, string ip)
        : _port(port), _ip(ip)
    {
        _addr.sin_family = AF_INET;
        _addr.sin_port = htons(_port);
        _addr.sin_addr.s_addr = inet_addr(_ip.c_str());
    }
    string tostring_port()
    {
        return to_string(_port);
    }
    string tostring_ip()
    {
        return _ip;
    }
    bool operator==(InetAddr addr)
    {
        return _port == addr._port && _ip == addr._ip;
    }
    sockaddr_in &getaddr()
    {
        return _addr;
    }

private:
    uint16_t _port;
    string _ip;
    sockaddr_in _addr;
};

Route.hpp

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include "Log.hpp"
#include "InetAddr.hpp"
using namespace std;
using namespace my_log;
class Route
{
private:
    void Push(InetAddr peer)
    {
        for (auto val : _data)
        {
            if (val == peer) return;
        }
        _data.push_back(peer);
        LOG(Level::INFO)<<peer.tostring_ip()<<'|'<<peer.tostring_port()<<" online";
    }
    bool Pop()
    {
        return true;
    }

public:
    Route(){}
    void Handler(int socketfd,string message,InetAddr client)
    {
        Push(client);
        // 處理信息
        string send_message = client.tostring_ip() + " | " + client.tostring_port() + ": ";
        send_message += message;
        // 發(fā)給所有在線的客戶端
        for (auto val : _data)
        {
            if(val == client) continue;
            sendto(socketfd, send_message.c_str(), send_message.size(), 0, (sockaddr *)&val.getaddr(), sizeof(val.getaddr()));
        }
    }
private:
    vector<InetAddr> _data;
};

到此這篇關于C++基于UDP協議的群聊服務器開發(fā)實現的文章就介紹到這了,更多相關C++ UDP 群聊內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • C++中Boost的智能指針shared_ptr

    C++中Boost的智能指針shared_ptr

    這篇文章介紹了C++中Boost的智能指針shared_ptr,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • C++控制權限關鍵字protected

    C++控制權限關鍵字protected

    這篇文章主要介紹了C++控制權限關鍵字protected,protected和private類似,而對于派生類來說,protected與public類似,下面來一起倆姐更多詳細內容吧,需要的小伙伴可以參考一下
    2022-01-01
  • Qt實現自定義日志類的示例代碼

    Qt實現自定義日志類的示例代碼

    這篇文章主要為大家詳細介紹了使用 qInstallMessageHandler() 實現一個簡單的日志工具,文中的示例代碼講解詳細,感興趣的小伙伴可以學習一下
    2023-12-12
  • pcre函數詳細解析

    pcre函數詳細解析

    PCRE提供了19個接口函數,為了簡單介紹,使用PCRE內帶的測試程序(pcretest.c)示例用法
    2013-09-09
  • c語言printf實現同一位置打印輸出的實例

    c語言printf實現同一位置打印輸出的實例

    下面小編就為大家?guī)硪黄猚語言printf實現同一位置打印輸出的實例。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • C++控制臺循環(huán)鏈表實現貪吃蛇

    C++控制臺循環(huán)鏈表實現貪吃蛇

    這篇文章主要為大家詳細介紹了C++控制臺循環(huán)鏈表實現貪吃蛇,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • C++17之std::visit的具體使用

    C++17之std::visit的具體使用

    本文主要介紹了C++17之std::visit的具體使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • c++ vector模擬實現代碼

    c++ vector模擬實現代碼

    vector是C++ STL中一個非常重要的容器,了解 vector 的底層實現原理,可以很好的幫助我們更加熟練的使用vector。這篇文章通過實例代碼給大家介紹c++ vector模擬實現,感興趣的朋友跟隨小編一起看看吧
    2020-02-02
  • C語言實現繪制余弦曲線

    C語言實現繪制余弦曲線

    這篇文章主要為大家詳細介紹了C語言實現繪制余弦曲線的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-01-01
  • C++中sprintf使用的方法與printf的區(qū)別分析

    C++中sprintf使用的方法與printf的區(qū)別分析

    這篇文章主要介紹了C++中sprintf使用的方法與printf的區(qū)別,實例分析了sprintf與printf的具體用法及相關注意事項,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-01-01

最新評論

溧阳市| 托克逊县| 平远县| 陇川县| 夹江县| 全州县| 陵水| 叶城县| 松原市| 巴里| 佛冈县| 阳谷县| 莱阳市| 安吉县| 阿拉善右旗| 乳源| 来凤县| 衡东县| 柏乡县| 新蔡县| 曲沃县| 抚州市| 南皮县| 扶沟县| 阿鲁科尔沁旗| 清新县| 盐边县| 双柏县| 武穴市| 沐川县| 仙游县| 白水县| 岫岩| 阿合奇县| 青龙| 牙克石市| 巴彦淖尔市| 民县| 台东市| 时尚| 泾源县|