9.11網編項目——UDP網絡聊天

服務器端

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <25061head.h>
#define SER_IP "192.168.144.128"
#define SER_PORT 8888// 鏈表節點結構定義
typedef struct Node
{char usrName[30];           // 用戶名struct sockaddr_in cin;     // 用戶的地址信息struct Node *next;          // 指針域
}*linklist;// 消息結構定義
struct msgTyp
{char type;char usrName[30];char msgText[50];
};int main(int argc, const char *argv[])
{// 前期配置int sfd = socket(AF_INET, SOCK_DGRAM, 0);if(sfd == -1){ERR_MSG("socket error");return -1;}struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1){ERR_MSG("bind error");close(sfd);return -1;}printf("綁定成功\n");struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);struct msgTyp recv_msg;  // 接收的信息// 創建頭節點并初始化linklist head = (linklist)malloc(sizeof(struct Node));if(head == NULL){ERR_MSG("malloc error");close(sfd);return -1;}head->next = NULL;  // 初始化頭節點pid_t pid=fork();if(pid>0){// 核心操作while(1){// 清空接收緩沖區memset(&recv_msg, 0, sizeof(recv_msg));// 接收消息ssize_t recv_len = recvfrom(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&cin, &addrlen);if(recv_len == -1){ERR_MSG("recvfrom error");continue;}switch(recv_msg.type){case 'L':  // login登錄printf("%s登錄成功\n", recv_msg.usrName);// 創建新用戶的節點并初始化linklist temp = (linklist)malloc(sizeof(struct Node));if(temp == NULL){ERR_MSG("malloc error");break;} //存儲新用戶結點的相關信息strcpy(temp->usrName, recv_msg.usrName);temp->cin = cin;//頭插temp->next = head->next;head->next = temp;// 廣播登錄消息linklist s = head->next;char sbuf[128] = "";snprintf(sbuf, sizeof(sbuf)-1, "%s已上號!", recv_msg.usrName);strcpy(recv_msg.msgText,sbuf);//放入recv.msg中,發給其他人//recv_msg.msgText[sizeof(recv_msg.msgText)-1] = '\0';while(s != NULL){// 跳過發送者自己(用IP和端口區分,后面的廣播都是這種方法排除自己)if (s->cin.sin_port == cin.sin_port && s->cin.sin_addr.s_addr == cin.sin_addr.s_addr){s = s->next;continue;}sendto(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&(s->cin), sizeof(s->cin));s = s->next;}break;case 'C':  // chat聊天if(strcmp(recv_msg.usrName,"系統")!=0){printf("%s:chat成功\n", recv_msg.usrName);}char cbuf[128] = "";snprintf(cbuf, sizeof(cbuf)-1, "%s說:%s", recv_msg.usrName, recv_msg.msgText);strcpy(recv_msg.msgText, cbuf);//recv_msg.msgText[sizeof(recv_msg.msgText)-1] = '\0';s = head->next;//從第一個用戶開始while(s != NULL){// 跳過發送者自己if (s->cin.sin_port == cin.sin_port && s->cin.sin_addr.s_addr == cin.sin_addr.s_addr){s = s->next;continue;}sendto(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&(s->cin), sizeof(s->cin));s = s->next;}break;case 'Q':  // quit退出printf("%s退出聊天室\n", recv_msg.usrName);char qbuf[128] = "";snprintf(qbuf, sizeof(qbuf)-1, "%s退出聊天室", recv_msg.usrName);strcpy(recv_msg.msgText, qbuf);//recv_msg.msgText[sizeof(recv_msg.msgText)-1] = '\0';// 先廣播退出消息s = head->next;while(s != NULL){// 跳過發送者自己if (s->cin.sin_port == cin.sin_port && s->cin.sin_addr.s_addr == cin.sin_addr.s_addr){s = s->next;continue;}sendto(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr*)&(s->cin), sizeof(s->cin));s = s->next;}           // 然后從鏈表中刪除該用戶節點linklist prev = head;linklist curr = head->next;int found = 0;//判斷找沒找到結點while(curr != NULL){if(strcmp(curr->usrName, recv_msg.usrName) == 0){//頭刪prev->next = curr->next;free(curr);curr=NULL;found = 1;printf("已從鏈表中移除用戶: %s\n", recv_msg.usrName);break;}prev = curr;curr = curr->next;}if(found==0){printf("警告: 未在鏈表中找到用戶 %s\n", recv_msg.usrName);}break;default:printf("發送格式錯誤\n");}}}//服務器廣播系統信息else if(pid==0){struct msgTyp sys_msg;sys_msg.type='C';strcpy(sys_msg.usrName, "系統");while(1){bzero(sys_msg.msgText,50);fgets(sys_msg.msgText,50, stdin);sys_msg.msgText[strlen(sys_msg.msgText)-1] = 0;//核心操作,因為是進程的原因,鏈表在子進程用不了,不能循環廣播//那我直接向主進程發消息,讓主進程廣播sendto(sfd, &sys_msg, sizeof(sys_msg), 0, (struct sockaddr*)&sin, sizeof(sin));}printf("系統消息發送成功");}close(sfd);return 0;
}

客戶端

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <25061head.h>
#define SER_IP "192.168.144.128"
#define SER_PORT 8888struct msgTyp
{char type;char usrName[30];char msgText[50];
};int cfd;                  // 客戶端socket
struct sockaddr_in sin;   // 服務器的相關配置
char usrName[30] = "";   // 用戶名
//雖然是線程,但有多個阻塞函數,所以用多線程
//該線程主要功能是接收消息
void *recv_msg_thread(void *arg)
{struct msgTyp recv_msg;socklen_t addrlen = sizeof(sin);while(1){// 清空接收緩沖區memset(&recv_msg, 0, sizeof(recv_msg));// 接收服務器發送的消息ssize_t recv_len = recvfrom(cfd, &recv_msg,sizeof(recv_msg),0,(struct sockaddr*)&sin, &addrlen);// 打印接收的消息printf("%s\n", recv_msg.msgText);fflush(stdout);  // 刷新輸出緩沖區}// 接收線程退出時關閉socketclose(cfd);pthread_exit(0);return NULL;
}int main(int argc, const char *argv[])
{// 獲取用戶名printf("請輸入你的用戶名: ");fgets(usrName, sizeof(usrName)-1, stdin);// 去除換行符usrName[strcspn(usrName, "\n")] = '\0';// 創建socketcfd = socket(AF_INET, SOCK_DGRAM, 0);if(cfd == -1){ERR_MSG("socket error");return -1;}    // 初始化服務器相關配置sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);// 配置登錄消息struct msgTyp send_msg;send_msg.type = 'L';strcpy(send_msg.usrName,usrName);//strcpy(send_msg.msgText, "");if(sendto(cfd, &send_msg, sizeof(send_msg), 0,(struct sockaddr*)&sin, sizeof(sin)) == -1){ERR_MSG("sendto error");close(cfd);return -1;}printf("登錄成功!輸入'quit'退出聊天室...\n");// 創建接收消息的線程pthread_t recv_tid;if(pthread_create(&recv_tid, NULL, recv_msg_thread, NULL) != 0){ERR_MSG("pthread_create error");close(cfd);return -1;}// 分離線程,系統自動回收pthread_detach(recv_tid);// 主線程用于發送消息while(1){char input[50] = "";fgets(input, sizeof(input)-1, stdin);// 去除換行符input[strcspn(input, "\n")] = '\0';//輸入quit,消息類型則設置從Q類型if(strcmp(input, "quit") == 0){send_msg.type = 'Q';strcpy(send_msg.usrName, usrName); sendto(cfd, &send_msg, sizeof(send_msg),0,(struct sockaddr*)&sin, sizeof(sin));printf("已退出聊天室\n");close(cfd);return 0;}// 發送聊天消息send_msg.type = 'C';strcpy(send_msg.usrName, usrName);strcpy(send_msg.msgText, input);if(sendto(cfd, &send_msg, sizeof(send_msg),0,(struct sockaddr*)&sin, sizeof(sin)) == -1){ERR_MSG("sendto error");close(cfd);return -1;}}//關閉socketclose(cfd);return 0;
}

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

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

相關文章

第3節-使用表格數據-數據庫設計

摘要: 在本教程中&#xff0c;你將學習如何為自己的應用程序設計 PostgreSQL 數據庫。 業務需求 我們將為一個簡單的庫存管理系統設計數據庫。 讓我們從業務需求開始&#xff1a; “我們的庫存管理系統使倉庫用戶能夠高效管理多個倉庫的庫存。” 它簡化了產品管理&#xff0c;使…

Linux下清理磁盤空間——df 磁盤占用100%,du占用很少空間的原因

背景 一臺測試服務器&#xff0c;/data磁盤大小為300G&#xff0c;時不時就滿了&#xff0c;通過df命令查看300G基本全用了&#xff0c;use 100%。但是進到/data目錄中通過du 命令查看&#xff0c;也就用了20個G左右&#xff0c;怎么都對不上。如何清理都沒有釋放太多空間。查看…

分鐘級長視頻生成迎來“記憶革命”,7倍成本降低,2.2倍端到端生成速度提升!|斯坦福字節

論文鏈接&#xff1a;https://arxiv.org/pdf/2508.21058 項目鏈接&#xff1a;https://primecai.github.io/moc/亮點直擊提出了一種自適應上下文混合&#xff08;Adaptive Mixture of Contexts&#xff0c;MoC&#xff09;框架&#xff0c;該框架學習將每個查詢路由到視頻序列中…

JavaScript 設計模式概覽

1. 設計模式是什么? 設計模式是開發中解決常見問題的經典方案。設計模式并非具體代碼&#xff0c;而是解決問題的通用解決方案&#xff0c;幫助開發者避免重復造輪子&#xff0c;提升代碼的可維護性、可擴展性。 2. 設計模式的歷史 設計模式起源于建筑領域&#xff0c;由克…

(九)Spring Cloud Alibaba 2023.x:微服務接口文檔統一管理與聚合

目錄 前言 準備 實踐 網關服務配置 1.pom.xml 引入 webflux 版本 springboc 依賴 2.application-dev.yml 配置 springboc 多服務地址 3.application-dev.yml 配置springboc 文檔路由 4.網關過濾器AuthFilter.class 中放行 springboc 訪問路徑 業務服務配置 1.pom.xml…

在Cursor里安裝極其好用的Mysql Database Client 插件

&#x1f4f8; 插件界面展示 圖片1&#xff1a;插件主界面和連接配置圖片2&#xff1a;數據編輯和查詢結果展示&#x1f3af; 核心優勢 1. 直接編輯數據 - 像DataGrip一樣強大 ? 點擊即編輯: 直接雙擊數據單元格&#xff0c;立即進入編輯模式? 實時保存: 編輯完成后按 Enter …

Cursor 不香了?替代與組合實踐指南(Windsurf、Trae、Copilot、MCP)

當你感覺 Cursor 的產出質量和穩定性不如從前&#xff0c;未必一定要“全盤換掉”。本文從“替代”與“組合”兩個維度給出可落地的工具編排方案&#xff0c;并附帶決策矩陣與常見工作流&#xff0c;幫助你在不同場景獲得穩定、可控的產出。0. 適用讀者 正在使用或評估 Cursor&…

【MFC】對話框屬性:X Pos(X位置),Y Pos(Y位置)

前言 本文介紹對話框屬性中的X Pos(X位置)、Y Pos(Y位置)&#xff0c;同時給出相關示例便于理解。 目錄1 位置2 詳解3 示例1 位置 首先介紹一下這個屬性在哪里。 在資源視圖中雙擊對話框節點&#xff0c;打開該對話框&#xff1b; 鼠標右鍵工作區空白處&#xff0c;單擊屬性&am…

Java面試小冊(1)

1【Q】&#xff1a;序列化和反序列化【A】&#xff1a;序列化是將Java對象轉化為字節流&#xff0c;用于網絡傳輸&#xff0c;持久化或緩存。Java提供了java.io.Serializable接口實現序列化。反序列化是將字節流轉為為對象。2【Q】&#xff1a; Java中Exception和Error有什么區…

html獲取16個隨機顏色并不重復

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>16個不重復隨機顏色</title><style>…

Redis 緩存穿透、擊穿、雪崩:防御與解決方案大全

&#x1f6e1;? Redis 緩存穿透、擊穿、雪崩&#xff1a;防御與解決方案大全 文章目錄&#x1f6e1;? Redis 緩存穿透、擊穿、雪崩&#xff1a;防御與解決方案大全&#x1f9e0; 一、緩存穿透&#xff1a;防御不存在數據的攻擊&#x1f4a1; 問題本質與危害&#x1f6e1;? 解…

量子計算機的發展對傳統密碼學的打擊

量子計算機的發展對傳統密碼學的核心威脅&#xff0c;源于其能高效解決傳統計算機“計算不可行”的數學問題——而這些問題正是當前主流密碼算法保障安全的基石。這種影響并非“全面摧毀”&#xff0c;而是針對傳統密碼學的不同分支&#xff08;非對稱密碼、對稱密碼、哈希函數…

《var, let, const:現代JS聲明指南》

文章目錄JavaScript 中 var、let、const 的差異1. 作用域&#xff08;Scope&#xff09;2. 變量提升&#xff08;Hoisting&#xff09;3. 重復聲明4. 變量值是否可變對比表5. 示例代碼總結JavaScript 中 var、let、const 的差異 1. 作用域&#xff08;Scope&#xff09; var 函…

在 Docker 中安裝 MySQL 教程

拉取 MySQL 鏡像docker pull mysql:8.0創建并啟動 MySQL 容器docker run -d \--name mysql8 \-p 3306:3306 \-e MYSQL_ROOT_PASSWORD123456 \-v mysql_data:/var/lib/mysql \mysql:8.0命令說明&#xff1a;-d&#xff1a;后臺運行容器 --name mysql8&#xff1a;給容器起個名字…

C#線程理解

目錄 一.線程類 1.基礎線程類&#xff08;Thread&#xff09; 2.線程池類&#xff08;Threadpool&#xff09; 3.任務并行庫&#xff08;Task&#xff09; 4.并行循環&#xff08;Parallel&#xff09; 二.線程池(threadPool)和Thread/Task之間的聯系 1.ThreadPool和Thr…

Java入門級教程16——JUC的安全并發包機制

目錄 1.JUC的安全并發包機制 1.1 包含 1.2 Barrier(柵欄)機制——CyclicBarrier&#xff08;循環屏障&#xff09; 1.2.1 定義 1.2.2 特性 1.2.1 模擬包車 1.2.2 模擬學生到齊上課 1.2.3 計算任務總耗時 1.3 CountDownLatch(閉鎖)機制 1.3.1 定義 1.3.2 特性 1.3.3…

【網絡通信】全面解析MAC地址:網絡設備的唯一標識

【網絡通信】全面解析MAC地址&#xff1a;網絡設備的唯一標識 文章目錄【網絡通信】全面解析MAC地址&#xff1a;網絡設備的唯一標識前言一、MAC 地址的定義&#xff1a;設備的 “網絡身份證”?二、MAC 地址的格式與組成&#xff1a;48 位的 “數字編碼”?三、MAC 地址的工作…

Perforce Klocwork 2025.2版本更新:默認啟用現代分析引擎、支持 MISRA C:2025 新規、CI構建性能提升等

Perforce Klocwork 現已更新至2025.2版本&#xff01;該版本增強了對 C/C的分析能力&#xff0c;提升了現代 C 分析的準確性&#xff0c;并改進了對源文件編碼的支持。該版本還為 MISRA C:2025 標準引入了新的分類體系&#xff0c;并增強了 Visual Studio Code 插件的可用性。 …

機器人馭風而行:低空經濟如何開啟智能新紀元【科普類】

新晉碼農一枚&#xff0c;小編會定期整理一些寫的比較好的代碼和知識點&#xff0c;作為自己的學習筆記&#xff0c;試著做一下批注和補充&#xff0c;轉載或者參考他人文獻會標明出處&#xff0c;非商用&#xff0c;如有侵權會刪改&#xff01;歡迎大家斧正和討論&#xff01;…

Java學習筆記四(繼承)

1 繼承繼承的實現&#xff1a;public class 子類 extends 父類 {… }注釋&#xff1a;子類可直接使用&#xff0c;父類&#xff08;保護&#xff0c;公開&#xff09;的屬性和方法優點&#xff1a;減少重復代碼&#xff0c;缺點&#xff1a;只能單繼承// 父類 public class Tes…