//客户端代码
#include <myhead.h>
struct msgType
{
char type; // 消息类型L:登录,Q:退出,C:聊天
char usrName[20];
char msgText[1024];
};
#define SER_PORT 6666 // 服务器端口
#define SER_IP "192.168.2.161" // 服务器IP
int main(int argc, char const *argv[])
{
int cfd = socket(AF_INET, SOCK_DGRAM, 0);
if (cfd == -1)
{
perror("socket error");
return -1;
}
// 端口号快速重用
int reuse = 1;
if (setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
perror("setsockopt 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); // IP地址
socklen_t len = sizeof(sin); // 地址长度
struct msgType msg;
msg.type = 'L';
printf("请输入用户名:");
fgets(msg.usrName, 20, stdin);
msg.usrName[strlen(msg.usrName) - 1] = '\0';
char temp[20]; // 将用户名保存起来
strcpy(temp, msg.usrName);
strcpy(msg.msgText, "");
sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);
// 创建子进程用于收消息
pid_t pid = fork();
if (pid == -1)
{
perror("fork error");
return -1;
}
if (pid == 0)
{
// 子进程
{
while (1)
{
bzero(&msg, sizeof(msg));
// 接收消息
recvfrom(cfd, &msg, sizeof(msg), 0, NULL, NULL);
switch (msg.type)
{
case 'L':
printf("%s\n", msg.msgText);
break;
case 'Q':
{
if (strcmp(temp, msg.usrName) == 0)
{
printf("退出成功\n");
exit(EXIT_SUCCESS);
}
printf("%s\n", msg.msgText);
}
break;
case 'C':
printf("[%s]:%s\n", msg.usrName, msg.msgText);
break;
}
}
}
}
else if (pid > 0)
{
// 父进程
msg.type = 'C';
while (1)
{
sleep(1);
// 发送消息
fgets(msg.msgText, 1024, stdin);
msg.msgText[strlen(msg.msgText) - 1] = '\0';
if (strcmp(msg.msgText, "q") == 0 || strcmp(msg.msgText, "Q") == 0)
{
msg.type = 'Q';
sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);
sleep(2);
goto end;
}
sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);
}
}
end:
kill(pid, SIGKILL);
wait(NULL);
close(cfd);
return 0;
}
//服务器主代码
#include "head.h"
#define SER_PORT 6666 // 服务器端口
#define SER_IP "192.168.2.161" // 服务器IP
int main(int argc, char const *argv[])
{
// 创建用于通信的服务器套接字文件描述符
int sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd == -1)
{
perror("socket error");
return 1;
}
// 将端口号快速重启
int reuse = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
{
perror("setsockopt 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)
{
perror("bind error");
return 1;
}
printf("服务器启动成功\n");
// 创建链表
NodePtr L = list_create();
// 接收客户端发送的数据
struct sockaddr_in cin;
socklen_t len = sizeof(cin);
struct msgType msg;
bzero(&msg, sizeof(msg));
while (1)
{
// 创建子进程
pid_t pid = fork();
// 接收客户端发送的数据
if (pid == 0)
{
while (1)
{
// 接收信息
recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, &len);
switch (msg.type) // 判断信息类型
{
case 'L':
{
list_insert(L, msg.usrName, &cin);
strcpy(msg.msgText, "欢迎您进入聊天室 开始聊天吧"); // 发送欢迎信息
sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr *)&cin, len);
strcpy(msg.msgText, "*******欢迎");
strcat(msg.msgText, msg.usrName);
strcat(msg.msgText, "进入聊天室*******");
NodePtr p = L->next;
while (p != NULL) // 发送所有人进入聊天室信息(除了刚进来的人 因为已经发了欢迎)
{
if (p->addr.sin_port == cin.sin_port)
{
p = p->next;
continue;
}
if (p == NULL)
{
break;
}
sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->addr, len);
p = p->next;
}
}
break;
case 'C':
{
printf("[%s]:%s\n", msg.usrName, msg.msgText); // 先在终端展示
for (NodePtr p = L->next; p != NULL; p = p->next)
{
sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->addr, len);
}
printf("发送成功\n");
}
break;
case 'Q':
{
strcpy(msg.msgText, "*********");
strcat(msg.msgText, msg.usrName);
strcat(msg.msgText, "退出聊天室*********");
for (NodePtr p = L->next; p != NULL; p = p->next)
{
sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr *)&p->addr, len);
}
sleep(1);
list_delete(L, msg.usrName);
}
break;
}
}
}
else if (pid > 0)
{
// 发送数据给客户端
while (1)
{
msg.type = 'C';
strcpy(msg.usrName, "GM");
fgets(msg.msgText, sizeof(msg.msgText), stdin);
msg.msgText[strlen(msg.msgText) - 1] = '\0';
sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr *)&sin, len);
if (strcmp(msg.msgText, "exit") == 0)
{
goto end;
}
}
}
else if (pid < 0)
{
perror("fork error");
return 1;
}
end:
kill(pid, SIGKILL);
list_destroy(L);
close(sfd);
wait(NULL);
return 0;
}
}
//头文件
#include <myhead.h>
#ifndef __HEAD_H__
#define __HEAD_H__
// 定义结构体
typedef struct Node
{
union
{
int len; // 节点长度
char name[20]; // 节点名称
};
struct sockaddr_in addr; // 地址信息结构体
struct Node *next; // 指向下一个节点
} Node, *NodePtr;
struct msgType
{
char type; // 消息类型 L:登录,C:聊天,Q;退出
char usrName[20]; // 姓名
char msgText[1024]; // 消息
};
// 创建链表
NodePtr list_create();
// 申请封装节点函数
NodePtr apply_node(char *name, struct sockaddr_in *addr);
// 链表判空
int list_empty(NodePtr L);
// 链表插入
int list_insert(NodePtr L, char *name, struct sockaddr_in *addr);
// 按名字找位置函数
int list_search_name(NodePtr L, char *name);
// 任意位置查找节点
NodePtr list_search_pos(NodePtr L, int pos);
// 任意位置删
int list_delete(NodePtr L, char *name);
// 链表销毁
void list_destroy(NodePtr L);
#endif
//函数文件
#include "head.h"
// 创建链表
NodePtr list_create()
{
// 申请头结点
NodePtr L = (NodePtr)malloc(sizeof(Node));
if (NULL == L)
{
printf("创建失败\n");
return NULL;
}
// 赋值
L->len = 0;
L->next = NULL;
printf("聊天室创建成功\n");
return L;
}
// 申请封装节点函数
NodePtr apply_node(char *name, struct sockaddr_in *addr)
{
// 申请空间
NodePtr p = (NodePtr)malloc(sizeof(Node));
if (NULL == p)
{
printf("申请失败\n");
return NULL;
}
// 赋值
strcpy(p->name, name);
p->addr.sin_family = addr->sin_family;
p->addr.sin_port = addr->sin_port;
p->addr.sin_addr.s_addr = addr->sin_addr.s_addr;
p->next = NULL;
return p;
}
// 链表判空
int list_empty(NodePtr L)
{
return L->next == NULL;
}
// 链表插入
int list_insert(NodePtr L, char *name, struct sockaddr_in *addr)
{
// 判断逻辑
if (NULL == L)
{
return -1;
}
// 申请节点
NodePtr p = apply_node(name, addr);
if (NULL == p)
{
return -1;
}
// 插入逻辑
p->next = L->next;
L->next = p;
printf("*********%s进入聊天室*********\n", p->name);
L->len++;
return 0;
}
// 按名字找位置函数
int list_search_name(NodePtr L, char *name)
{
// 判断逻辑
if (NULL == L || list_empty(L))
{
return -1;
}
// 遍历逻辑
NodePtr q = L->next;
for (int index = 1;index<=L->len; index++)
{
if(strcmp(q->name, name) == 0)
{
return index;
}
q = q->next;
}
printf("未找到该用户\n");
return -1;
}
// 任意位置查找节点
NodePtr list_search_pos(NodePtr L, int pos)
{
if (NULL == L || list_empty(L) || pos < 0 || pos > L->len)
{
return NULL;
}
//查找逻辑
//定义指针从头结点出发
NodePtr q = L;
for (int i = 0; i < pos; i++)
{
q = q->next;
}
return q; //返回找到的节点
}
// 任意明治删
int list_delete(NodePtr L, char *name)
{
// 判断逻辑)
if (NULL == L || list_empty(L))
{
return -1;
}
// 找到前一个节点
int pos = list_search_name(L, name);
NodePtr q = list_search_pos(L, pos-1);
// 删除逻辑
NodePtr p = q->next;
q->next = p->next;
// 释放空间
free(p);
p = NULL;
printf("*********%s退出聊天室*********\n", name);
return 0;
}
// 链表销毁
void list_destroy(NodePtr L)
{
// 判断逻辑
if (NULL == L)
{
return;
}
// 遍历逻辑
while (!list_empty(L))
{
list_delete(L, L->next->name);
}
// 释放头结点
free(L);
L = NULL;
}