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;
}
相关推荐
AI科技星37 分钟前
张祥前统一场论动量公式P=m(C-V)误解解答
开发语言·数据结构·人工智能·经验分享·python·线性代数·算法
海琴烟Sunshine41 分钟前
leetcode 345. 反转字符串中的元音字母 python
python·算法·leetcode
geobuilding1 小时前
将大规模shp白模贴图转3dtiles倾斜摄影,并可单体化拾取建筑
算法·3d·智慧城市·数据可视化·贴图
jghhh011 小时前
基于高斯伪谱法的弹道优化方法及轨迹仿真计算
算法
mm-q29152227293 小时前
【天野学院5期】 第5期易语言半内存辅助培训班,主讲游戏——手游:仙剑奇侠传4,端游:神魔大陆2
人工智能·算法·游戏
MoRanzhi12033 小时前
Python 实现:从数学模型到完整控制台版《2048》游戏
数据结构·python·算法·游戏·数学建模·矩阵·2048
2401_841495643 小时前
【数据结构】基于BF算法的树种病毒检测
java·数据结构·c++·python·算法·字符串·模式匹配
蒙奇D索大4 小时前
【算法】递归算法实战:汉诺塔问题详解与代码实现
c语言·考研·算法·面试·改行学it
一只鱼^_4 小时前
力扣第 474 场周赛
数据结构·算法·leetcode·贪心算法·逻辑回归·深度优先·启发式算法
叫我龙翔4 小时前
【数据结构】从零开始认识图论 --- 单源/多源最短路算法
数据结构·算法·图论