c语言 多进程实现基于UDP的网络群聊聊天室
生活随笔
收集整理的這篇文章主要介紹了
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的网络群聊聊天室的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 从RTS游戏看游戏开发-2
- 下一篇: 数据库索引与视图实验