设计思路
考虑到只是一个简易版本的UDP聊天服务,所以很多不完善的地方
服务器服务器我是开了一个父子进程,分别负责的
- 接受客户端的消息&&发送某一个客户端的信息
- 服务器的命令终端(只不过没有实现,只写了广播功能)
然后我封装了一个数据结构,每当客户端发一个包过来,这个包由三部分组成
- 消息类型(login、cheat、end)
- 发送者的姓名
- 发送者的消息构成
然后我们分三种情况来解析这三种消息就行,具体实现就是一个简单的链表结构,详情可以看代码。
CODE/*
*
* 缺点:
* 1.以奇怪的方式下线的用户不能清理
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define false 0
#define true 1
#define N 1024
struct MSG{//消息封装
char type;
char name[20];
char text[N];
};
struct Node {//用户节点信息
struct sockaddr_in addr;
struct Node * next;
};
struct Node * init() {//用户节点初始化
struct Node * p = (struct Node *)malloc(sizeof(struct Node));
memset(p,0,sizeof(struct Node));
p->next = NULL;
return p;
}
const char *ip = "127.0.0.1";
const int port = 8000;
struct Node *head;
void login(int sockfd,struct MSG msg,struct sockaddr_in clientaddr) {//登陆消息
struct Node *p = head;
strcpy(msg.text,msg.name);
msg.text[strlen(msg.text) - 1] = '\0';
strcat(msg.text," 已经上线欢迎来聊^_^~");
//puts(msg.text);
int cnt = 0;
while(p->next) {//发给已经上线的用户
if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&p->next->addr,sizeof(p->next->addr)) next;
cnt++;
}
struct Node *k = (struct Node *)malloc(sizeof(struct Node));
k->addr = clientaddr;
k->next = NULL;
p->next = k;
printf("当前总用户数:%d \n上线用户的port: %u \n",cnt,ntohl(clientaddr.sin_port));
}
void chat(int sockfd,struct MSG msg,struct sockaddr_in clientaddr) {//聊天消息处理函数
struct Node *p = head;
char str[N]= {0};
//puts(msg.name);
//puts(msg.text);
sprintf(str,"%s : %s",msg.name,msg.text);
strcpy(msg.text,str);
puts(msg.text);//输出到服务器终端
while(p->next) {
if (memcmp(&clientaddr, &p->next->addr, sizeof(clientaddr)) != 0) {
if (sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *) &p->next->addr, sizeof(p->next->addr)) next;
}
}
void quit(int sockfd,struct MSG msg,struct sockaddr_in clientaddr) {//退出函数
struct Node *p = head;
struct Node *q = NULL;
sprintf(msg.text,"%s用户已下线",msg.name);
puts(msg.text);
while(p->next) {//因为头号节点是服务器
if(memcmp(&clientaddr,&p->next->addr,sizeof(clientaddr)) == 0) {
q = p->next;
p->next = q->next;
free(q);
q=NULL;//free后将指针赋为NULL
}
else {
sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&p->next->addr,sizeof(p->next->addr));
}
p = p->next;
}
}
int socketfd;
void my_end(int sign_no) {//这个是注册一个ctr+c退出时候的一个信号处理
close(socketfd);
exit(1);
}
int main() {
signal(SIGINT,my_end);
head = init();
struct sockaddr_in serveraddr,clientaddr;
bzero(&serveraddr,sizeof(serveraddr));
bzero(&clientaddr,sizeof(clientaddr));
struct MSG msg;
socklen_t addrlen = sizeof(struct sockaddr);
char buf[N] = "";
if((socketfd = socket(AF_INET, SOCK_DGRAM, 0))
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?