基于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;
}

运行结果:

相关推荐
sunfove7 小时前
光网络的立交桥:光开关 (Optical Switch) 原理与主流技术解析
网络
Kevin Wang72710 小时前
欧拉系统服务部署注意事项
网络·windows
min18112345610 小时前
深度伪造内容的检测与溯源技术
大数据·网络·人工智能
汤愈韬10 小时前
NAT策略
网络协议·网络安全·security·huawei
汤愈韬10 小时前
Full Cone Nat
网络·网络协议·网络安全·security·huawei
zbtlink10 小时前
现在还需要带电池的路由器吗?是用来干嘛的?
网络·智能路由器
桌面运维家11 小时前
vDisk配置漂移怎么办?VOI/IDV架构故障快速修复
网络·架构
dalerkd11 小时前
忙里偷闲叙-谈谈最近两年
网络·安全·web安全
汤愈韬11 小时前
NAT ALG (应用层网关)
网络·网络协议·网络安全·security·huawei
运维栈记13 小时前
虚拟化网络的根基-网络命名空间
网络·docker·容器