Linux --UDP套接字實現簡單的網絡聊天室

一、Server端的實現

1.1、服務端的初始化

①、創建套接字:

創建套接字接口:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//1. 這是一個創建套接字的接口
//2. domain: 協議家族類型,其中包括ipv4、ipv6、等等,通常設置為ipv4就行,即AF_INET
//3. type: 套接字指定類型,其中SOCK_DGRAM是UDP類型,SOCK_STREAM是TCP類型
//4. 返回值int: 創建失敗返回值小于0并設置錯誤碼

②、綁定前的準備工作:

?

③、綁定套接字:

綁定接口:

//1. 這是一個綁定套接字的接口

//2. socket: 需要綁定的套接字

//3. address: sockaddr結構體地址

//4. address_len: sockaddr結構體大小

//5. 返回值int,綁定成功返回0,失敗返回-1并設置錯誤碼

1.2、服務端運行

①、接收信息

接收信息接口:

//1. socket: 從那個套接字接收信息

//2. buffer:接收的信息存放在哪個緩沖區; length: 緩沖區的大小

//3. flags: 控制接收行為的標志,通常設為0

//4. address: sockaddr結構體的地址(輸入型參數,用來獲取對方信息)

//5. address_len: sockaddr結構體大小

//6. 返回值ssize_t 成功返回接收到的字節數,如果沒有數據可讀或者套接字關閉返回0,失敗返回-1,并設置errno錯誤碼

②、接收信息的同時獲取對方信息

③、實現兩個方法,一個用來檢測用戶登陸;一個用來把用戶發來的消息轉發給每一個用戶

到這里,服務端的基本框架完成,然后我們在實現以下客戶端吧!

二、客戶端的實現

2.1、創建套接字

2.2、準備數據并綁定(客戶端不需要自己綁定,OS自動綁定)

2.3、用兩個線程來執行收發任務

2.4、收發信息的實現

OK,以上客戶端跟服務端都已搭建完成,那我們來測試一下吧

三、測試

3.1、本地測試

客戶端:

這里我把內容重定向到另一個終端上,避免它輸入到同一個終端上

服務端:

3.2、跨平臺測試

四、最終代碼

4.1 server
#pragma once
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string>
#include"log.hpp"
#include<unordered_map>Log lg;
const uint16_t defaultport=8808;
const std::string defaultip="0.0.0.0";
enum{SOCK_ERR=1,BIND_ERR
};
class UdpServer
{private:int sock_fd;//網絡文件描述符uint16_t port_;//端口std::string ip_;//ipstd::unordered_map<std::string, sockaddr_in> online_user;public:UdpServer(const uint16_t&port=defaultport,const std::string &ip=defaultip):port_(port),ip_(ip){}void Initialize(){//1.創建套接字sock_fd=socket(AF_INET,SOCK_DGRAM,0);if(sock_fd<0){lg(FATAL,"Sock create fail!! errno is: %d, errstring is: %s",errno,strerror(errno));exit(SOCK_ERR);}lg(INFO,"Sock create sucess!! sockfd is: %d",sock_fd);//2.數據準備struct sockaddr_in server;memset(&server,0,sizeof(server));//初始化結構體inet_aton(ip_.c_str(),&server.sin_addr);//字符串轉地址server.sin_family=AF_INET;server.sin_port=htons(port_);//主機轉網絡字節序//3.bindif(bind(sock_fd,(sockaddr*)&server,sizeof(server))<0){lg(FATAL,"Bind fail!! errno is: %d,errstring is: %s",errno,strerror(errno));exit(BIND_ERR);}lg(INFO,"Bind sucess!! sockfd is: %d",sock_fd);}void CheckUser(const uint16_t&port,const std::string&ip,sockaddr_in&client){auto iter=online_user.find(ip);if(iter!=online_user.end())return;online_user.insert({ip,client});std::cout<<"["<<port<<":"<<ip<<"]"<<"# user login...."<<std::endl;}void Broadcast(const uint16_t&port,const std::string&ip,const std::string& info){std::string message="[";message+=std::to_string(port);message+=":";message+=ip;message+="]#:";std::string echo_message=message+info;for(auto &e:online_user){sendto(sock_fd,echo_message.c_str(),echo_message.size(),0,(sockaddr*)(&e.second),sizeof(e.second));}}void Start(){char buffer[1024];//讀入的緩沖區while(true){//1.讀struct sockaddr_in client;//用來獲取客戶端信息socklen_t len=sizeof(client);ssize_t n=recvfrom(sock_fd,buffer,sizeof(buffer),0,(sockaddr*)&client,&len);if(n<0){continue;//讀取失敗繼續讀}else if(n>0){buffer[n]='\0';std::string info=buffer;uint16_t clientport=ntohs(client.sin_port);//網絡轉主機字節序char ipstr[32];inet_ntop(AF_INET,&client.sin_addr,ipstr,sizeof(ipstr));//地址轉字符串std::string clientip=ipstr;CheckUser(clientport,clientip,client);//檢查用戶是否存在Broadcast(clientport,clientip,info);//轉發客戶端的信息給每個用戶}}}~UdpServer(){if(sock_fd>0)close(sock_fd);}};
#include<iostream>
#include"UdpServer.hpp"
#include<memory>void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<" serverport[1024+]"<<std::endl;
}
// server serverport
int main(int argc,char*argv[])
{if(argc!=2){Usage(argv[0]);exit(1);}uint16_t serverport=std::stoi(argv[1]);std::unique_ptr<UdpServer> ptr(new UdpServer(serverport));ptr->Initialize();ptr->Start();return 0;
}

4.2 client
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<cstring>
struct ThreadData
{int sock_fd;std::string serverip_;struct sockaddr_in server;};
void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<" serverip serverport"<<std::endl;
}
void* send_message(void*args)
{ThreadData* td=static_cast<ThreadData*>(args);std::string line;while(true){std::cout<<"Please enter@"<<std::endl;std::getline(std::cin,line);sendto(td->sock_fd,line.c_str(),line.size(),0,(sockaddr*)&td->server,sizeof(td->server));}return nullptr;
}
void* recv_message(void*args)
{ThreadData* td=static_cast<ThreadData*>(args);char buffer[1024];while(true){memset(buffer,0,sizeof(buffer));struct sockaddr_in temp;socklen_t templen=sizeof(temp);ssize_t n=recvfrom(td->sock_fd,buffer,sizeof(buffer)-1,0,(sockaddr*)&temp,&templen);if(n>0){buffer[n]='\0';std::cerr<<buffer<<std::endl;}}return nullptr;
}
//udpclient srverip server port
int main(int argc,char*argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}ThreadData td;td.serverip_=argv[1];uint16_t serverport=std::stoi(argv[2]);//1.創建套接字td.sock_fd= socket(AF_INET,SOCK_DGRAM,0);if(td.sock_fd<0){return 2;}//2.準備數據memset(&td.server,0,sizeof(td.server));//初始化inet_pton(AF_INET,td.serverip_.c_str(),&td.server.sin_addr);//串轉地址td.server.sin_family=AF_INET;td.server.sin_port=htons(serverport);//主機轉網絡字節序//3.需要綁定,OS自動綁定pthread_t send,recv;pthread_create(&send,nullptr,send_message,&td);pthread_create(&recv,nullptr,recv_message,&td);pthread_join(send,nullptr);pthread_join(recv,nullptr);return 0;
}
?4.3 makefile
.PHONY:all
all: udpserver udpclient
udpclient:UdpClient.cppg++ -o $@ $^ -std=c++11 -lpthread
udpserver:Main.cppg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f udpclient udpserver

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/83549.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/83549.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/83549.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Eureka 高可用集群搭建實戰:服務注冊與發現的底層原理與避坑指南

引言&#xff1a;為什么 Eureka 依然是存量系統的核心&#xff1f; 盡管 Nacos 等新注冊中心崛起&#xff0c;但金融、電力等保守行業仍有大量系統運行在 Eureka 上。理解其高可用設計與自我保護機制&#xff0c;是保障分布式系統穩定的必修課。本文將手把手帶你搭建生產級 Eur…

Spring Boot應用開發實戰

Spring Boot應用開發實戰&#xff1a;從零到生產級項目的深度指南 在當今Java生態中&#xff0c;Spring Boot已占據絕對主導地位——據統計&#xff0c;超過75%的新Java項目選擇Spring Boot作為開發框架。本文將帶您從零開始&#xff0c;深入探索Spring Boot的核心精髓&#xf…

yum更換阿里云的鏡像源

步驟 1&#xff1a;備份原有源配置&#xff08;重要&#xff01;&#xff09; sudo mkdir /etc/yum.repos.d/backup sudo mv /etc/yum.repos.d/CentOS-* /etc/yum.repos.d/backup/步驟 2&#xff1a;下載阿里云源配置 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https:…

【算法訓練營Day06】哈希表part2

文章目錄 四數相加贖金信三數之和四數之和 四數相加 題目鏈接&#xff1a;454. 四數相加 II 這個題注意它只需要給出次數&#xff0c;而不是元組。所以我們可以分治。將前兩個數組的加和情況使用map存儲起來&#xff0c;再將后兩個數組的加和情況使用map存儲起來&#xff0c;ke…

JS手寫代碼篇---手寫apply方法

11、手寫apply方法 apply方法的作用&#xff1a; apply 是一個函數的方法&#xff0c;它允許你調用一個函數&#xff0c;同時將函數的 this 值設置為指定的值&#xff0c;并將函數的參數作為數組&#xff08;或類數組對象&#xff09;傳遞給該函數。 與call的區別&#xff1…

冪等性:保障系統穩定的關鍵設計

冪等性&#xff08;Idempotence&#xff09; 是計算機科學和分布式系統中的核心概念&#xff0c;指同一操作重復執行多次所產生的效果與執行一次的效果相同。這一特性對系統容錯性、數據一致性至關重要&#xff0c;尤其在網絡通信&#xff08;如HTTP&#xff09;和數據庫設計中…

electron定時任務,打印內存占用情況

// 監聽更新 function winUpdate(){// 每次執行完后重新設置定時器try {// 獲取當前時間并格式化為易讀的字符串const now new Date();const timeString now.toLocaleString();console.log(當前時間: ${timeString});// 記錄內存使用情況&#xff08;可選&#xff09;const m…

華為手機開機卡在Huawei界面不動怎么辦?

遇到華為手機卡在啟動界面&#xff08;如HUAWEI Logo界面&#xff09;的情況&#xff0c;可依次嘗試以下解決方案&#xff0c;按操作復雜度和風險由低到高排序&#xff1a; &#x1f527; 一、強制重啟&#xff08;優先嘗試&#xff09; 1.通用方法? 長按 ?電源鍵 音量下鍵?…

Python爬蟲之數據提取

本章節主要會去學習在爬蟲中的如何去解析數據的方法&#xff0c;要學習的內容有&#xff1a; 響應數據的分類結構化數據如何提取非結構化數據如何提取正則表達式的語法以及使用jsonpath解析嵌套層次比較復雜的json數據XPath語法在Python代碼中借助lxml模塊使用XPath語法提取非…

并行智算MaaS云平臺:打造你的專屬AI助手,開啟智能生活新紀元

目錄 引言&#xff1a;AI助手&#xff0c;未來生活的必備伙伴 并行智算云&#xff1a;大模型API的卓越平臺 實戰指南&#xff1a;調用并行智算云API打造個人AI助手 3.1 準備工作 3.2 API調用示例 3.3 本地智能AI系統搭建 3.4 高級功能實現 并行智算云的優勢 4.1 性能卓越…

三維坐標轉換

如果坐標(x,y,z)->(y,-z,-x)可以使用坐標系&#xff1a; import mathdef mat_vec_mult(matrix, vector):"""將 3x3 矩陣與 3x1 向量相乘。參數&#xff1a;matrix: 3x3 的旋轉矩陣vector: 3x1 的向量返回&#xff1a;3x1 的結果向量"""resul…

【C++高級主題】虛繼承

目錄 一、菱形繼承&#xff1a;虛繼承的 “導火索” 1.1 菱形繼承的結構與問題 1.2 菱形繼承的核心矛盾&#xff1a;多份基類實例 1.3 菱形繼承的具體問題&#xff1a;二義性與數據冗余 二、虛繼承的語法與核心目標 2.1 虛繼承的聲明方式 2.2 虛繼承的核心目標 三、虛繼…

什么是分布式鎖?幾種分布式鎖分別是怎么實現的?

一&#xff1a;分布式鎖實現思路 1.1 基本原理與實現方式 &#xff08;1&#xff09;分布式鎖的實現方式 &#xff08;2&#xff09;基于Redis的分布式鎖 獲取鎖 長時間無人操作&#xff0c;使鎖自動過期 添加鎖與設置過期時間需原子性 釋放鎖 1.2 實例 &#xff08;1&…

Legal Query RAG(LQ-RAG):一種新的RAG框架用以減少RAG在法律領域的幻覺

人工智能正在迅速改變法律專業人士的工作方式——從起草合同到進行研究。但盡管大型語言模型&#xff08;LLM&#xff09;功能強大&#xff0c;它們在關鍵領域卻常常出錯&#xff1a;真實性。當人工智能在法律文件中“幻覺”出事實時&#xff0c;后果可能是嚴重的——問問那些無…

如何用AI高效運營1000+Tiktok矩陣賬號

在當今數字化的時代&#xff0c;Tiktok 矩陣賬號運營成為了眾多企業和個人追求流量與變現的重要手段。然而&#xff0c;面對眾多的賬號管理&#xff0c;如何高效運營成為了關鍵。此時&#xff0c;AI 工具的出現為我們提供了強有力的支持。 一、Tiktok 矩陣賬號的重要性 Tiktok…

數據結構與算法學習筆記(Acwing 提高課)----動態規劃·樹形DP

數據結構與算法學習筆記----動態規劃樹形DP author: 明月清了個風 first publish time: 2025.6.4 ps??樹形動態規劃&#xff08;樹形DP&#xff09;是處理樹結構問題的一種動態規劃方法&#xff0c;特征也很明顯&#xff0c;會有一個樹形結構&#xff0c;其實是DFS的優化。…

得物GO面試題及參考答案

動態規劃的概念是什么&#xff1f; 動態規劃&#xff08;Dynamic Programming, DP&#xff09;是一種通過將復雜問題分解為重疊子問題&#xff0c;并利用子問題的解來高效解決原問題的方法。其核心思想在于避免重復計算&#xff0c;通過存儲子問題的解&#xff08;通常使用表格…

掃地機產品--氣壓傳感器器件異常分析

掃地機產品–氣壓傳感器器件異常分析 文章目錄 掃地機產品--氣壓傳感器器件異常分析一.背景1?.1 **標準大氣壓的定義與數值**?二.分析故障2.1**萬用表如何測量二極管**2.2 不良氣壓傳感器的萬用表二極管擋位測量結果分析。2.3 不良氣壓傳感器的開蓋分析2.4 結論2.5 后續措施三…

C#基礎語法(2)

### 練習 一、變量和數據類型 - 1. 變量定義與賦值 cs using System; namespace Name { class Program { public static void Main(string[] args) { int age 20; double height 1.75; string name "張三…

連接關鍵點:使用 ES|QL 聯接實現更豐富的可觀測性洞察

作者&#xff1a;來自 Elastic Luca Wintergerst ES|QL 的 LOOKUP JOIN 現已進入技術預覽階段&#xff0c;它允許你在查詢時對日志、指標和追蹤進行豐富處理&#xff0c;無需在攝取時進行非規范化。動態添加部署、基礎設施或業務上下文&#xff0c;減少存儲占用&#xff0c;加速…