基于UDP完成的网络聊天室 2023.11.5

服务器端

//服务器端
//服务器端
//服务器端

#include<head.h>

#define ERR_MSG(MSG) do\
{printf("报错行号是:%d\n",__LINE__);\
perror(MSG);\
return -1;}\
while(0)
//结构体
typedef struct __MSG
{
	char type;//L登录,C群聊,Q退出
	char name[30];//用户名字
	char text[256];//发送的消息
}msg_t;

//创建保存客户端链表
typedef struct __CLIADDR
{
	struct sockaddr_in cliaddr;
	struct __CLIADDR * next;
}node_t;

//登录的函数
void do_login(int sfd,msg_t msg,node_t * addr,struct sockaddr_in cliaddr)
{
	node_t * p = addr;
	//遍历链表
	while(p->next != NULL)
	{
		p = p->next;
		if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&p->cliaddr,sizeof(p->cliaddr))) == -1)
		{
			printf("sendto error");
		}
	}
	//头插
	node_t * newp = NULL;
	if(((newp = ((node_t *)malloc(sizeof(node_t)))) == NULL))
	{
		printf("malloc error");
		return;
	}
	newp->cliaddr = cliaddr;
	newp->next = addr->next;
	addr->next = newp;
	printf("[%s]登录成功\n",msg.name);
	return;
}

//群聊操作的函数
void do_chat(int sfd,msg_t msg,node_t * addr,struct sockaddr_in cliaddr)
{
	node_t * p = addr;
	//遍历链表
	while(p->next != NULL)
	{
		p = p->next;
		if(memcmp(&cliaddr,&(p->cliaddr),sizeof(cliaddr)))
		{
			//说明不是自己,就发送数据
			if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(p->cliaddr),sizeof(p->cliaddr)) == -1)
			{
				printf("sendto error");
			}
		}
	}
	//发送用户聊天成功的消息
	printf("[%s]CHAT成功\n",msg.name);
	return;
}
//退出操作的函数
void do_quit(int sfd,msg_t msg,node_t * addr,struct sockaddr_in cliaddr)
{
	node_t * p = addr;
	//遍历链表 是自己就将自己在链表中删除
	//不是自己就发送退出群聊的数据
	node_t * del = NULL;
	while(p->next != NULL)
	{
		//判断addr结构体是否相等
		if(memcmp(&(p->next->cliaddr),&cliaddr,sizeof(cliaddr)))
		{
			//不是自己
			p = p->next;
			if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->cliaddr),sizeof(p->cliaddr)) == -1)
			{
				printf("sendto error");
			}
			
		}
		else
		{
			//是自己
			//删除对应的链表结点
			del = p->next;
			p->next = del->next;
			free(del);
			del = NULL;
		}
	}
	//发送用户退出的消息
	printf("[%s]退出\n",msg.name);
	return;
}
int main(int argc, const char *argv[])
{
	//判断终端输入进来的ip和端口是否正确
	if(3 != argc)
	{
		printf("输入错误\n");
		return -1;
	}

	//创建套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd == -1)
	{
		ERR_MSG("socket");
	}
	
	//填充服务器结构体
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(atoi(argv[2]));
	seraddr.sin_addr.s_addr = inet_addr(argv[1]);
	socklen_t ser_len = sizeof(seraddr);

	//将套接字绑定到结构体
	if(bind(sfd,(struct sockaddr*)&seraddr,ser_len) == -1)
	{
		ERR_MSG("bind");
	}

	//填充客户端结构体
	struct sockaddr_in cliaddr;
	cliaddr.sin_family = AF_INET;
	socklen_t cli_len = sizeof(cliaddr);

	//定义接受消息的msg
	msg_t msg;
	
	//创建进程
	pid_t pid = fork();
	
	//进程错误消息
	if(pid == -1)
	{
		printf("fork error");
	}

	//子进程
	if(pid == 0)
	{
		//创建保存客户端的链表头节点
		node_t * addr;
		addr = ((node_t*)malloc(sizeof(node_t)));
		if(addr == NULL)
		{
			printf("malloc error");
			return -1;
		}
		//节点清空
		memset(addr,0,sizeof(addr));
		addr->next = NULL;
		while(1)
		{
			//清空结构体
			memset(&msg,0,sizeof(msg));
			memset(&cliaddr,0,sizeof(cliaddr));
			//接受客户端发送的消息,存放msg
			if((recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cliaddr,&cli_len)) == -1)
			{
				ERR_MSG("recvfrom");
			}
		//判断type
		switch(msg.type)
		{
		case 'L': //登录操作
				do_login(sfd,msg,addr,cliaddr);
				break;
		case 'C'://群聊
				do_chat(sfd,msg,addr,cliaddr);
				break;
		case 'Q'://退出
				do_quit(sfd,msg,addr,cliaddr);
				break;
		}

		}
	}
	else//父进程
	{
		//把名字改为系统消息
		strcpy(msg.name,"系统消息");
		//把类型改为聊天类型
		msg.type = 'C';
		while(1)
		{
			//清空消息
			memset(msg.text,0,sizeof(msg.text));
			//接受终端的消息
			fgets(msg.text,sizeof(msg.text),stdin);
			//把\n换成\0
			msg.text[strlen(msg.text)-1] = 0;
			//发送系统消息
			if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&seraddr,ser_len)) == -1)
			{
				printf("sendto error");
			}
		}
	}
	//关闭套接字
	close(sfd);
	return 0;
}

客户端

//客户端
//客户端
//客户端


#include<head.h>
#define ERR_MSG(MSG) do\
{printf("报错行号是:%d\n",__LINE__);\
perror(MSG);\
return -1;}\
while(0)
//结构体
typedef struct __msg
{
	//L是登录,C表示群聊,Q表示退出,S表示系统消息
	char type;
	char name[30];
	char text[256];
}msg_t;



int main(int argc, const char *argv[])
{
	//判断终端输入ip port是否正确
	if(argc != 3)
	{
		printf("输入错误\n");
		return -1;
	}
	//创建套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd == -1)
	{
		ERR_MSG("socket");
	}
	
	//填充结构体
	struct sockaddr_in seraddr;
	seraddr.sin_family = AF_INET;
	seraddr.sin_port = htons(atoi(argv[2]));
	seraddr.sin_addr.s_addr = inet_addr(argv[1]);
	socklen_t seraddr_len = sizeof(seraddr);
	
	//定义结构体
	msg_t msg;
	memset(&msg,0,sizeof(msg));
	
	printf("请输入用户名-->");
	//从终端输入用户名
	fgets(msg.name,sizeof(msg.name),stdin);
	msg.name[strlen(msg.name)-1] = 0;
	//使type变成登录
	msg.type = 'L';
	strcpy(msg.text,"加入群聊");
	//给服务器发送登录的消息
	if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&seraddr,seraddr_len)) == -1)
	{
		ERR_MSG("sendto");
	}
	//创建父子进程
	pid_t pid = fork();
	
	//子进程
	if(pid == 0)
	{
		while(1)
		{
			//结构体清空
			bzero(&msg,sizeof(msg));
			//接收消息
			if((recvfrom(sfd,&msg,sizeof(msg),0,NULL,NULL)) == -1)
			{
				printf("recvfrom error");
			}
			//打印名字和消息
			printf("[%s]:%s\n",msg.name,msg.text);
		}
	}
	//父进程
	else if(pid > 0)
	{
		while(1)
		{
			//清空消息
			memset(msg.text,0,sizeof(msg.text));
			//终端输入消息
			fgets(msg.text,sizeof(msg.text),stdin);
			//使\n变成\0
			msg.text[strlen(msg.text)-1] = 0;
			//判断退出消息
			if(!strcmp(msg.text,"quit"))
			{
				msg.type = 'Q';
				strcpy(msg.text,"退出群聊");
			}
			else
			{
				//否则为聊天
				msg.type = 'C';
			}

			//发送消息
			if((sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&seraddr,seraddr_len)) == -1)
			{
				printf("sendto error");
			}
			if(!strcmp(msg.text,"退出群聊"))
			{
				break;
			}
		}
		//给子进程发送
		kill(pid,SIGKILL);
		//回收子进程资源
		wait(NULL);
	}
	//关闭套接字
	close(sfd);

	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值