2024.3.7

基于UDP的简易网络聊天室

服务器:

#include <myhead.h>
 
#define SER_PORT 8888
 
struct msgTyp  //存储消息的结构体
{
	char type; 		  //消息类型
	char name[30];    //用户姓名
	char text[1024];  //消息正文
};
 
//创建链表存储客户端信息
typedef struct node
{
	//数据域
	struct sockaddr_in cin;
	//指针域,存储下一个节点的地址
	struct node *next;
}*LinkList, Node;
 
//创建链表节点
LinkList Create()
{
	LinkList s=(LinkList)malloc(sizeof(Node));
	if(s==NULL)
		return NULL;
	//创建成功
	memset(&s->cin,0,sizeof(s->cin));
	s->next=NULL;//表示创建新节点指针域为空
	return s;
}
 
 
int do_login(struct msgTyp msg, int sfd, struct sockaddr_in cin, LinkList head)
{
	
	//转发登录信息
	LinkList p = head;
	while(p != NULL)
	{
		sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->cin), sizeof(p->cin));
		p = p->next;
	}
	
	//将当前登录的用户的地址信息结构体存储起来
	LinkList s = Create();
	//新节点数据域即为它的地址信息结构体
	s->cin = cin;
 
	s->next=head->next;
	head->next=s;
 
	return 0;
}
 
int do_chat(LinkList head, int sfd, struct msgTyp msg, struct sockaddr_in cin)
{
	
	//转发群聊信息
	LinkList p = head;
	while(p != NULL)
	{
 
		if(p->cin.sin_port != cin.sin_port)   //端口号不一样说明不是同一个客户端,则将消息发给其他人
		{
			sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->cin), sizeof(p->cin));
		}
		p = p->next;
	}
 
	return 0;
}
 
int do_quit(LinkList head, int sfd, struct msgTyp msg, struct sockaddr_in cin)
{
	//转发离线信息
	LinkList p = head;
	while(p->next != NULL)
	{
		if(p->cin.sin_port != cin.sin_port)
		{
			p = p->next;
			sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(p->cin), sizeof(p->cin));
		}
		else	//将离线的客户端的地址信息结构体从链表中删除
		{
			LinkList del = p->next;
			p->next = del->next;
			free(del);
			del = NULL;
		}
	}
	return 0;
}
 
int main(int argc, const char *argv[])
{
	//判断是否外部传参
	if(argc != 2){
		printf("请输入IP地址!!!\n");
		return -1;
	}
	//创建套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		perror("socket");
		return -1;
	}
	printf("socket create success\n");
 
	
	//填充服务器自身的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	
	sin.sin_port = htons(SER_PORT); 	//服务器绑定的端口号
	sin.sin_addr.s_addr = inet_addr(argv[1]); 	//服务器绑定的IP号
 
	//绑定
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
	{
		perror("bind error");
		return -1;
	}
	printf("bind success\n");
 
	char buf[128] = "";
	int res = 0;
 
	struct sockaddr_in cin; 	//存储对端地址信息结构体
	socklen_t addrlen = sizeof(cin);
 
	LinkList head = Create();  //创建链表头结点,存储客户端信息
 
	struct msgTyp msg;   //接收数据的结构体存储位置
 
	pid_t pid = fork();  //创建子进程
 
	if(pid > 0)  //父进程
	{
		//接收数据
		while(1)
		{
			//接收客户端的消息
			res = recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cin, &addrlen);
			printf("[%s] %s\n",msg.name,msg.text);
			if(res < 0)
			{
				perror("recvfrom error");
				return -1;
			}
			//根据消息的类型实行不同功能
			switch(msg.type)
			{
			case 'L': 	
				do_login(msg, sfd, cin, head);
				break;
			case 'C':
				do_chat(head, sfd, msg, cin);
				break;
			case 'Q':
				do_quit(head, sfd, msg, cin);
				break;
			default:
				printf("error\n");break;
			}
		}
	}
	else if(0 == pid)  //子进程
	{
		//服务器可以发送系统消息
		msg.type = 'C';
		strcpy(msg.name, "系统消息");
		//发送数据
		while(1)
		{
		
			//清空消息正文
			memset(msg.text, 0, sizeof(msg.text));
			//从终端获取数据
			fgets(msg.text, sizeof(msg.text), stdin);
			msg.text[strlen(msg.text) - 1] = 0;
 
			sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));	
		}
	}
	else 
	{
		perror("fork error");
		return -1;
	}
	//关闭所有文件描述符
	close(sfd);
	return 0;
}

客户端

#include <myhead.h>
 
#define SER_PORT 8888
 
struct msgTyp  //消息结构体
{
    char type;      	//消息类型
    char name[30];  	//用户姓名
    char text[1024]; 	//消息正文
};
 
int main(int argc, const char *argv[])
{
	//判断是否外部传参
	if(argc!=2){
		printf("请输入IP地址\n");
		return -1;
	}
 
	//创建套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd ==-1)
	{
		perror("socket");
		return -1;
	}
	printf("socket create success\n");
 
	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family 	= AF_INET; 	
	sin.sin_port = htons(SER_PORT); 	
	sin.sin_addr.s_addr = inet_addr(argv[1]);
 
	//输入上线的用户名称
	printf("请输入名字>>>");
	char name[20] = "";
	scanf("%s", name);
	getchar();
 
	//定义登录时的类型以及提示信息
	struct msgTyp msg_snd;
	msg_snd.type = 'L';
	strcpy(msg_snd.name, name);
	strcpy(msg_snd.text, "加入群聊");
	
	//发送登录信息
	if(sendto(sfd, &msg_snd, sizeof(msg_snd), 0, (struct sockaddr*)&sin, sizeof(sin)) ==-1)
	{
		perror("sendto error");
		return -1;
	}
 
	char buf[128] = "";
	int res = 0;
 
	//存储对端地址信息结构体
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
 
	//定义接收消息的结构体
	struct msgTyp msg_rcv;
 
	//创建子进程
	pid_t pid = fork();
 
	if(pid > 0)  //父进程
	{
		//发送数据,发送聊天信息
		while(1)
		{
			//客户端从终端获取消息并发送出去
			bzero(buf, sizeof(buf));                 //清空数组
			fgets(buf, sizeof(buf), stdin);
			buf[strlen(buf)-1] = 0;
 
			msg_snd.type = 'C';
			strcpy(msg_snd.text, buf);
 
			if(strcmp(msg_snd.text, "quit") == 0)
			{
				msg_snd.type = 'Q';
				strcpy(msg_snd.text, "已下线");
			}
 
			sendto(sfd, &msg_snd, sizeof(msg_snd), 0, (struct sockaddr*)&sin, sizeof(sin));
 
			if(strcmp(msg_snd.text, "已下线") == 0)
			{
				break;
			}
		}
		kill(pid,SIGKILL);
		wait(NULL);//等待回收子进程资源
	}
	else if(0 == pid)  //子进程
	{
		//接收数据
		while(1)
		{
			printf("\n");
			recvfrom(sfd, &msg_rcv, sizeof(msg_rcv), 0, NULL, NULL);
			printf("[%s] : %s", msg_rcv.name, msg_rcv.text);
		}
	}
	else
	{
		perror("fork error");
		return -1;
	}
 
	//关闭文件描述符
	close(sfd);
 
	return 0;
}
相关推荐
kitesxian9 分钟前
Leetcode200. 岛屿数量(HOT100)
算法·深度优先
LNTON羚通15 分钟前
算法定制LiteAIServer视频智能分析平台工业排污检测算法智控环保监管
算法·目标检测·音视频·监控·视频监控
好好学习O(∩_∩)O19 分钟前
11-23刷题记录
算法·leetcode·职场和发展
bingw011443 分钟前
华为机试HJ62 查找输入整数二进制中1的个数
数据结构·算法·华为
苏言の狗1 小时前
小R的二叉树探险 | 模拟
c语言·数据结构·算法·宽度优先
gkdpjj2 小时前
C++优选算法十四 优先级队列(堆)
开发语言·数据结构·c++·算法
几窗花鸢2 小时前
力扣面试经典 150(上)
数据结构·c++·算法·leetcode
lu_rong_qq3 小时前
决策树 DecisionTreeClassifier() 模型参数介绍
算法·决策树·机器学习
LNTON羚通8 小时前
摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现
算法·目标检测·音视频·监控·视频监控
哭泣的眼泪40810 小时前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame