日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

c语言 多进程实现基于UDP的网络群聊聊天室

發布時間:2024/8/1 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c语言 多进程实现基于UDP的网络群聊聊天室 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

功能

有新用戶登錄,其他在線的用戶可以收到登錄信息

有用戶群聊,其他在線的用戶可以收到群聊信息

有用戶退出,其他在線的用戶可以收到退出信息

服務器可以發送系統信息

流程圖如下:

?

提示:

客戶端登錄之后,為了實現一邊發送數據一邊接收數據,可以使用多進程或者多線程

服務器既可以發送系統信息,又可以接收客戶端信息并處理,可以使用多進程或者多線程

服務器需要給多個用戶發送數據,所以需要保存每一個用戶的信息,使用鏈表來保存數據傳輸的時候要定義結構體,結構體中包含操作碼、用戶名以及數據。

代碼如下:

? ? ? ? 服務器代碼

//服務器代碼 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h>#define ERRLOG(msg) \do { \printf("%s %s %d:", __FILE__, __func__, __LINE__); \perror(msg); \exit(-1); \} while (0)typedef struct _MSG {char code; //操作碼 ('L' 登錄),('C' 群聊),('Q' 退出)char name[32];char txt[128]; } msg_t; typedef struct _NODE {struct sockaddr_in clientaddr; //客戶端網絡信息結構體struct _NODE* next; //指針域 } node_t;int create_node(node_t **p); int do_login(struct sockaddr_in clientaddr, node_t* phead, int sockfd, msg_t msg); int do_chat(struct sockaddr_in clientaddr, node_t* phead, int sockfd, msg_t msg); int do_quit(struct sockaddr_in clientaddr, node_t* phead, int sockfd, msg_t msg); int main(int argc, char const* argv[]) {//入參合理性檢查if (3 != argc) {printf("Usage : %s <IP> <PORT>\n", argv[0]);return -1;}// 1.創建套接字int sockfd = 0;if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0))) {ERRLOG("socket error");}// 2.填充服務器網絡信息結構體struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));serveraddr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t serveraddr_len = sizeof(serveraddr);//綁定if (-1 == bind(sockfd, (struct sockaddr*)&serveraddr, serveraddr_len)) {ERRLOG("bind error");}//用來保存客戶端網絡信息結構體struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);msg_t msg;pid_t pid = 0;if (-1 == (pid = fork())) {ERRLOG("fork error");} else if (0 == pid) {//子進程,用來接收數據并處理//創建用來保存客戶端網絡信息結構體的鏈表node_t *phead = NULL;create_node(&phead);phead->next =NULL;printf("聊天室已創建,等待用戶加入群聊!!!\n");while(1) {memset(&msg, 0, sizeof(msg));memset(&clientaddr, 0, sizeof(clientaddr));if (-1 == recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&clientaddr, &clientaddr_len)) {ERRLOG("recvfrom error");}printf("[%s]: [%s]\n", msg.name, msg.txt);switch (msg.code) {case 'L':do_login(clientaddr, phead, sockfd,msg);break;case 'C':do_chat(clientaddr, phead, sockfd, msg);break;case 'Q':do_quit(clientaddr, phead, sockfd, msg);break;}}} else if (0 < pid) {//父進程,用來發送系統消息//把父進程當作一個客戶端,以群聊的方式發送消息給所有人(系統消息)msg.code = 'C';strcpy(msg.name, "系統");while (1){fgets(msg.txt, 128, stdin);msg.txt[strlen(msg.txt) - 1] = '\0';if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, serveraddr_len)) {ERRLOG("sendto error");}}}close(sockfd);return 0; } //創建鏈表節點的函數 int create_node(node_t** p) {*p = (node_t*)malloc(sizeof(node_t));if (NULL == *p || NULL == p) {ERRLOG("malloc error");} } //登錄的函數 int do_login(struct sockaddr_in clientaddr, node_t* phead, int sockfd, msg_t msg) {//先遍歷鏈表,將xxxx登錄的消息 轉發給所有人node_t* ptemp = phead;while (ptemp->next != NULL) {ptemp = ptemp->next;if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, ((struct sockaddr*)&(ptemp->clientaddr)), sizeof(ptemp->clientaddr))) {ERRLOG("sendto error");}}//將新登錄的用戶頭插到鏈表中node_t* pnew = NULL;create_node(&pnew);pnew->clientaddr = clientaddr;pnew->next = phead->next;phead->next = pnew;return 0; }//群聊消息的函數 int do_chat(struct sockaddr_in clientaddr, node_t* phead, int sockfd, msg_t msg) {//遍歷鏈表,將群聊的數據發送給除自己外的所有人node_t* ptemp = phead;while (ptemp->next != NULL) {ptemp = ptemp->next;if (0 != memcmp(&clientaddr, &(ptemp->clientaddr), sizeof(clientaddr))) {if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, ((struct sockaddr*)&(ptemp->clientaddr)), sizeof(ptemp->clientaddr))) {ERRLOG("sendto error");}}} }int do_quit(struct sockaddr_in clientaddr, node_t* phead, int sockfd, msg_t msg) {//將xxxx退出群聊的消息發送給除自己外所有的所有客戶端,并且將自己在鏈表中刪除node_t* ptemp = phead;while (ptemp->next != NULL) {if (0 != memcmp(&clientaddr, &(ptemp->clientaddr), sizeof(clientaddr))) {//判斷是不是自己,不是自己就發送數據ptemp = ptemp->next;if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, ((struct sockaddr*)&(ptemp->clientaddr)), sizeof(ptemp->clientaddr))) {ERRLOG("sendto error");}} else {//是自己,就將自己在鏈表中刪除node_t* pdel = ptemp->next;ptemp->next = pdel->next;free(pdel);pdel == NULL;}} }

? ? ? ? 客戶端代碼

//客戶端代碼 #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #define ERRLOG(msg) \do { \printf("%s %s %d:", __FILE__, __func__, __LINE__); \perror(msg); \exit(-1); \} while (0)typedef struct _MSG {char code; //操作碼 ('L' 登錄),('C' 群聊),('Q' 退出)char name[32];char txt[128]; } msg_t; int main(int argc, const char* argv[]) {//入參合理性檢查if (3 != argc) {printf("Usage : %s <IP> <PORT>\n", argv[0]);return -1;}// 1.創建套接字int sockfd = 0;if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0))) {ERRLOG("socket error");}// 2.填充服務器網絡信息結構體struct sockaddr_in serveraddr;memset(&serveraddr, 0, sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));serveraddr.sin_addr.s_addr = inet_addr(argv[1]);socklen_t serveraddr_len = sizeof(serveraddr);msg_t msg;memset(&msg, 0, sizeof(msg));printf("請輸入用戶名: ");fgets(msg.name, 32, stdin);msg.name[strlen(msg.name) - 1] = '\0';//給服務器發送登錄的數據包msg.code = 'L';strcpy(msg.txt, "加入群聊");if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, serveraddr_len)) {ERRLOG("sendto error");}pid_t pid = 0;if (-1 == (pid = fork())) {ERRLOG("fork error");} else if (0 == pid) {//子進程//接收由服務器發來的數據并打印while (1) {memset(&msg, 0, sizeof(msg));if (-1 == recvfrom(sockfd, &msg, sizeof(msg), 0, NULL, NULL)) {ERRLOG("recvfrom error");}printf("[%s]: [%s]\n", msg.name, msg.txt);}} else if (0 < pid) {//父進程while (1) {msg.code = 'C';fgets(msg.txt, 128, stdin);printf("我: ");msg.txt[strlen(msg.txt) - 1] = '\0';//判斷是不是要退出if (!strncmp(msg.txt, "quit", 5)) {msg.code = 'Q';strcpy(msg.txt, "退出群聊");}if (-1 == sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&serveraddr, serveraddr_len)) {ERRLOG("sendto error");}if (!strcmp(msg.txt, "退出群聊")) {break;}}//先讓子進程自殺kill(pid, SIGKILL);wait(NULL);close(sockfd);}return 0; }

總結

以上是生活随笔為你收集整理的c语言 多进程实现基于UDP的网络群聊聊天室的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。