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));
// 创建客户端socket
int 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;
}
// 创建服务器socket
int ser_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (ser_fd < 0)
{
ERR_MSG("socket");
return -1;
}
// 绑定IP和port
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(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;
}
}
}