基于UDP实现的网络聊天室

news/2024/5/18 13:38:40 标签: 网络, udp, 网络协议

服务器:

#include <myhead.h>
struct msg
{
	char type;
	char name[20];
	char text[1024];
};

int main(int argc, const char *argv[])
{
	if(argc!=3)
	{
		printf("input error\n");
		printf("./a.out IP地址 端口号\n");
		return -1;
	}

    //1、创建用于通信的套接字
    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;                                                                     
        }  

    //2、绑定IP地址和端口号
    //2.1填充地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;          //地址族
	short port=(short)atoi(argv[2]);
    sin.sin_port = htons(port);        //端口号
    sin.sin_addr.s_addr = inet_addr(argv[1]);   //IP地址


    //2,2绑定
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

	 //定义容器接收对端地址信息结构体
    struct sockaddr_in cin;
    socklen_t socklen = sizeof(cin);

	//定义存储客户端地址信息结构体信息
	struct sockaddr_in scin[1024];
	for(int i=0;i<1024;i++)
	{
		scin[i].sin_family=AF_INET;
	}

	 //1、创建文件描述符容器
    fd_set readfds, tempfds;
    //2、清空容器内容
    FD_ZERO(&readfds);
    //3、将sfd和0号文件描述符放入容器中
    FD_SET(0, &readfds);
    FD_SET(sfd, &readfds);

	//定义结构体变量,用于接收和发送,和客户端进行通信
	struct msg send;
	struct msg in;
	
	int res;
	char buf[128]="";
	char address[128]="";
	int n=0;

	while(1)
	{
		bzero(buf,sizeof(buf));
		bzero(address,sizeof(address));
		tempfds = readfds;      //将要检测的容器复制保存一份
	    res = select(sfd+1, &tempfds, NULL, NULL, NULL);  //阻塞等待集合中事件产生
		if(res == -1)
		{
			perror("select error");
			return -1;
		}else if(res == 0)
		{
			printf("time out\n");
			return -1;
		}

		//接收从客户端发来的消息
		if(FD_ISSET(sfd,&tempfds))
		{
			res=recvfrom(sfd,&in,sizeof(in),0,(struct sockaddr*)&cin,&socklen);
			if(res==-1)
			{
				perror("recvfrom error");
				return -1;
			}
			if(in.type=='L')
			{
				scin[n]=cin;
				n++;
			//	sprintf(buf,"---%s 已上线---",in.name);
				sprintf(address,"---%s[%s:%d]登录成功---",in.name,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
				printf("%s\n",address);
				for(int i=0;i<n;i++)
				{
					if(scin[i].sin_port==cin.sin_port)
					{
						continue;
					}
					sendto(sfd,&in,sizeof(in),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));
				}
				bzero(address,sizeof(address));
			}
			if(in.type=='C')
			{
				sprintf(address,"---%s[%s:%d]chat成功---",in.name,inet_ntoa(cin.sin_addr),ntohs(cin.sin_port));
				printf("%s\n",address);

				//群聊
				send.type=in.type;
				strcpy(send.name,in.name);
				strcpy(send.text,in.text);
				for(int i=0;i<n;i++)
				{
					if(scin[i].sin_port==cin.sin_port)
					{
						continue;
					}
					sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));


				}
			}
			if(in.type=='Q')
			{
				//下线
				send.type=in.type;
				strcpy(send.name,in.name);
				strcpy(send.text,in.text);

				for(int i=0;i<n;i++)
				{
					bzero(buf,sizeof(buf));
					sprintf(buf,"---%s已下线---\n",send.name);
					printf("%s\n",buf);
					//删除用户
					if(scin[i].sin_port==cin.sin_port)
					{
						int t=i;
						for(int j=i;j<=n;j++)
						{
							scin[t]=scin[t+1];
							t++;
						}
					}
					n--;
					sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));
				}
			
			}
		}
		if(FD_ISSET(0,&tempfds))
		{
			//用于接收系统的消息
			bzero(&send,sizeof(send));
			strcpy(send.name,"系统消息");
			fgets(send.text,sizeof(send.text),stdin);
			send.text[strlen(send.text)-1]=0;
			send.type='C';
			for(int i=0;i<=n;i++)
			{
				sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&scin[i],sizeof(scin[i]));
			}
		}


	}

	close(sfd);



	return 0;
}

客户端:

#include <myhead.h>
struct msg
{
	char type;
	char name[20];
	char text[1024];
};

int main(int argc, const char *argv[])
{
	if(argc!=3)
	{
		printf("input error\n");
		printf("usage:./a.out IP地址 端口号\n");
		return -1;
	}

	//1、创建用于通信的套接字
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }

	//2、绑定IP地址和端口号
    //2.1填充服务器端地址信息结构体
    struct sockaddr_in sin;
	short port=(short)atoi(argv[2]);
    sin.sin_family = AF_INET;          //地址族
    sin.sin_port = htons(port);        //端口号
    sin.sin_addr.s_addr = inet_addr(argv[1]);   //IP地址
	
	//定义容器接收服务器端地址信息结构体
    socklen_t socklen = sizeof(sin);



	//定义结构体变量,用于发送和接收
	struct msg send;
	struct msg in;
	printf("请输入用户名>>>");
	fgets(send.name,sizeof(send.name),stdin);
	send.name[strlen(send.name)-1]=0;
	send.type='L';
	//将登录的用户名发送给服务器
	sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&sin,socklen);
	
    //1、创建文件描述符容器
    fd_set readfds, tempfds;
    //2、清空容器内容
    FD_ZERO(&readfds);
    //3、将sfd和0号文件描述符放入容器中
    FD_SET(0, &readfds);
    FD_SET(sfd, &readfds);

	int res1;
	char buf[128]="";

	while(1)
	{
		 tempfds = readfds;      //将要检测的容器复制保存一份
        int res = select(sfd+1, &tempfds, NULL, NULL, NULL);  //阻塞等待集合中事件产生
        if(res == -1)
        {
            perror("select error");
            return -1;
        }else if(res == 0)
        {
            printf("time out\n");
            return -1;
        }

		//接收从服务器发来的消息
		if(FD_ISSET(sfd,&tempfds))
		{
			res1=recvfrom(sfd,&in,sizeof(in),0,(struct sockaddr*)&sin,&socklen);
			if(res1==-1)
			{
				perror("recvfrom error");
				return -1;
			}
			if(in.type=='L')
			{
				printf("---%s上线了---\n",in.name);
			}
			if(in.type=='C')
			{
				printf("%s: %s\n",in.name,in.text);
			}
			if(in.type=='Q')
			{
				printf("---%s已下线---\n",in.name);
			}

		}
		//如果程序执行到此,说明检测的集合中有事件产生
        if(FD_ISSET(0, &tempfds))       //表示sfd触发了事件
		{
			//判断是群聊还是退出
			bzero(send.text,sizeof(send.text));
			fgets(send.text,sizeof(send.text),stdin);
			send.text[strlen(send.text)-1]=0;
			if(strcmp(send.text,"quit")==0)
			{
				send.type='Q';
				sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&sin,socklen);
				break;
			}else
			{
				send.type='C';
				sendto(sfd,&send,sizeof(send),0,(struct sockaddr*)&sin,socklen);
			}
		}

	}
	close(sfd);
	return 0;
}

运行结果:


http://www.niftyadmin.cn/n/5414375.html

相关文章

使用 Docker 部署 File Browser 文件管理系统

1&#xff09;File Browser 介绍 官网&#xff1a;https://filebrowser.org/ GitHub&#xff1a;https://github.com/filebrowser/filebrowser 今天为大家分享一款开源的私有云盘项目&#xff1a;File Browser&#xff0c;简单实用、轻量级、跨平台&#xff0c;安装部署简单快…

2024中国(浙江)环保产业与水科技博览会

汇聚科技智慧力量&#xff0c;助力美丽中国建设 浙江省环境服务业高质量发展大会 2024中国&#xff08;浙江&#xff09;环保产业与水科技博览会 China (Zhejiang) environmental protection industry and Water Technology Expo I 环保产业 I 水利科技 I 智慧水务 I 泵管阀…

pair,二叉搜索树(BST),set,map,AVL树,红黑树,B树,B+树

堆 Huffman 树 并查集 字典 跳表 散列 pair&#xff0c;二叉搜索树&#xff08;BST&#xff09;&#xff0c;set&#xff0c;map&#xff0c;t红黑树&#xff0c;AVL树&#xff0c;B树&#xff0c;B树 AVL树的定义&#xff1a; In computer science, an AVL tree is a…

Wireshark——获取捕获流量的前N个数据包

1、问题 使用Wireshark捕获了大量的消息&#xff0c;但是只想要前面一部分。 2、方法 使用Wireshark捕获了近18w条消息&#xff0c;但只需要前5w条。 选择文件&#xff0c;导出特定分组。 输入需要保存的消息范围。如&#xff1a;1-50000。 保存即可。

Redis冲冲冲——Redis分布式锁如何实现

目录 引出Redis分布式锁如何实现Redis入门1.Redis是什么&#xff1f;2.Redis里面存Java对象 Redis进阶1.雪崩/ 击穿 / 穿透2.Redis高可用-主从哨兵3.持久化RDB和AOF4.Redis未授权访问漏洞5.Redis里面安装BloomFilte Redis的应用1.验证码2.Redis高并发抢购3.缓存预热用户注册验证…

Go编译报错 link: running gcc failed: exit status 1(已解决)

背景 在对一个开源的Go程序二次开发 重新编译时 &#xff0c; 报错截图如下 报错文字如下&#xff1a;关键信息 link: running gcc failed: exit status 1 $ go build -o orchestrator-didi -i go/cmd/orchestrator/main.go go build: -i flag is deprecated # command-li…

ftp几个常见错误问题及解决办法

1、无法上传网页&#xff0c;FTP故障&#xff0d;提示“无法连接服务器”错误。 问题出现原因&#xff1a;FTP客户端程序设置问题&#xff0c;客户上网线路问题&#xff0c;ftp服务器端问题。 处理方法&#xff1a;建议客户使用CUTPFTP软件来上传客户的网页&#xff0c;在“F…

LInux-多线程基础概念

文章目录 前言预备页表详解缺页中断页表的映射 一、多线程是什么&#xff1f;轻量级进程 二、Pthread库pthread_create 前言 从本章的多线程开始&#xff0c;我们开始进入Linux系统的尾声&#xff0c;所以&#xff0c;在学习多线程的过程中&#xff0c;我们也会逐步对之前的内…