网络聊天室
客户端
main.c
#include "include/errorAndHead.h"void hander(int sig)
{while (waitpid(-1, NULL, WNOHANG) > 0);
}int main(int argc, const char *argv[])
{/* 捕获 SIGCHLD 信号 hander 处理*/if (signal(SIGCHLD, hander) == SIG_ERR){ERR_MSG("socket");return -1;}msg Msg = {};Msg.type = 'L';strcpy(Msg.name, loginUI(Msg.name));// 创建客户端socketint cli_fd = socket(AF_INET, SOCK_DGRAM, 0);if (cli_fd < 0){ERR_MSG("socket");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); // 点分十进制转网络字节序socklen_t sin_len = sizeof(sin);/* 该进程只服务的用户名 *//* char name[20] = "";strcpy(name, Msg.name); */// 将聊天用户的基本信息发送给服务器if (sendto(cli_fd, &Msg, sizeof(Msg), 0, (struct sockaddr *)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}// 创建子进程pid_t pid = fork();if (pid == 0){char name[20] = "";strcpy(name, Msg.name);// 子进程while (1){memset(&Msg, 0, sizeof(Msg));fgets(Msg.text, sizeof(Msg.text), stdin);Msg.text[strlen(Msg.text) - 1] = 0;if (strcmp(Msg.text, "quit") == 0){/* 如果输入的是quit那么 通知下线 并且退出进程,并通知父进程死亡 */exit_chat(cli_fd, Msg, name, sin);kill(getppid(), SIGKILL);break;}msg_chat(cli_fd, Msg, name, sin);}exit(EXIT_SUCCESS);}else if (pid > 0){// 父进程用来接收数据while (1){memset(&Msg, 0, sizeof(Msg));if (recvfrom(cli_fd, &Msg, sizeof(Msg), 0, NULL, NULL) < 0){ERR_MSG("recvfrom");return -1;}switch (Msg.type){case 'L':// printf("system msg : %s\n", Msg.text);rec_login(Msg);break;case 'Q':// printf("system msg : %s\n", Msg.text);quitc_login(Msg);break;case 'C':rec_chat(Msg);break;default:break;}}exit(EXIT_SUCCESS);}close(cli_fd);return 0;
}
函数库
#include "include/errorAndHead.h"char *loginUI(char *username)
{memset(username, 0, sizeof(username));char e;printf("————————欢迎来到多人聊天室————————\n");printf("键入ENTRE继续...\n");scanf("%c", &e);if (e != '\n'){exit(EXIT_SUCCESS);}printf("请输入姓名->>>");scanf("%s", username);return username;
}int rec_login(msg Msg)
{if (Msg.type == 'L'){printf("system msg : %s\n", Msg.text);}return 0;
}int exit_chat(int cli_fd, msg Msg, char *name, struct sockaddr_in sin)
{strcpy(Msg.name, name);Msg.type = 'Q';if (sendto(cli_fd, &Msg, sizeof(Msg), 0, (struct sockaddr *)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}return 0;
}int quitc_login(msg Msg)
{if (Msg.type == 'Q'){printf("system msg : %s\n", Msg.text);}return 0;
}int msg_chat(int cli_fd, msg Msg, char *name, struct sockaddr_in sin)
{strcpy(Msg.name, name);Msg.type = 'C';if (sendto(cli_fd, &Msg, sizeof(Msg), 0, (struct sockaddr *)&sin, sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}return 0;
}int rec_chat(msg Msg)
{if (Msg.type == 'C'){printf("%s\n",Msg.text);}return 0;
}
服务器
main.c
#include "include/errorAndHead.h"void hander(int sig)
{while (waitpid(-1, NULL, WNOHANG) > 0);
}int main(int argc, const char *argv[])
{/* 捕获 SIGCHLD 信号 hander 处理*/if (signal(SIGCHLD, hander) == SIG_ERR){ERR_MSG("socket");return -1;}// 创建服务器socketint ser_fd = socket(AF_INET, SOCK_DGRAM, 0);if (ser_fd < 0){ERR_MSG("socket");return -1;}// 绑定IP和portstruct 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(ser_fd, (struct sockaddr *)&sin, sizeof(sin)) != 0){ERR_MSG("bind");return -1;}// 创建进程int pid = fork();if (pid > 0){// 父进程, 接收消息user_linklist *head = (user_linklist *)malloc(sizeof(user_linklist));head->len = 0;head->next = NULL;// 发送方接收容器struct sockaddr_in rec_cin;socklen_t rec_cin_len = sizeof(rec_cin);msg Msg;while (1){memset(&Msg, 0, sizeof(Msg));if (recvfrom(ser_fd, &Msg, sizeof(Msg), 0, NULL,NULL) < 0){ERR_MSG("recvfrom");return -1;}switch (Msg.type){case 'L':rec_login(ser_fd, rec_cin, Msg, head);break;case 'Q':quit_login(ser_fd, rec_cin, Msg, head);break;case 'C':msg_chat(ser_fd, rec_cin, Msg, head);break;default:break;}}free(head);head = NULL;exit(EXIT_SUCCESS);}else if (pid == 0){// 终端获取文本exit(EXIT_SUCCESS);}close(ser_fd);return 0;
}
函数库
#include "include/recive.h"int rec_login(int ser_fd, struct sockaddr_in rec_cin, msg Msg, user_linklistPtr head)
{/* 自己的终端打印成功登录 */printf("成功登录---IP:%s PORT:%d 用户名:%s\n", inet_ntoa(rec_cin.sin_addr), ntohs(rec_cin.sin_port), Msg.name);Msg.type = 'L';strcpy(Msg.text, "");sprintf(Msg.text, "——————%s 成功上线——————\n", Msg.name);/* 成功登录后,向所有人发送某人成功 上线 */user_linklistPtr ptr = head;while (ptr->next != NULL){ptr = ptr->next;if (sendto(ser_fd, &Msg, sizeof(Msg), 0, (struct sockaddr *)&(ptr->data), sizeof(ptr->data)) < 0){ERR_MSG("sendto");return -1;}}// 将登录的用户放入内存user_linklist *node = (user_linklist *)malloc(sizeof(user_linklist));node->next = NULL;node->data = rec_cin;head->next = node;head->len++;return 0;
}int quit_login(int ser_fd, struct sockaddr_in rec_cin, msg Msg, user_linklistPtr head)
{printf("退出成功---IP:%s PORT:%d 用户名:%s\n", inet_ntoa(rec_cin.sin_addr), ntohs(rec_cin.sin_port), Msg.name);strcpy(Msg.text, "");sprintf(Msg.text, "——————%s 下线了——————\n", Msg.name);user_linklistPtr ptr = head;while (ptr->next != NULL){ptr = ptr->next;if (sendto(ser_fd, &Msg, sizeof(Msg), 0, (struct sockaddr *)&(ptr->data), sizeof(ptr->data)) < 0){ERR_MSG("sendto");return -1;}}/* 在链表中删除下线的用户 */// 后续优化不影响程序运行
}int msg_chat(int ser_fd, struct sockaddr_in rec_cin, msg Msg, user_linklistPtr head)
{if (Msg.type != 'C'){return 0;}user_linklistPtr ptr = head;while (ptr->next != NULL){ptr = ptr->next;if (sendto(ser_fd, &Msg, sizeof(Msg), 0, (struct sockaddr *)&(ptr->data), sizeof(ptr->data)) < 0){ERR_MSG("sendto");return -1;}}
}