网络聊天室项目

服务器端

#include <myhd.h>

//传输结构体
struct cli
{
	char type;    //  L/C/Q
	char name[20];
	char text[128];
};
int main(int argc, const char *argv[])
{
	if(argc!=3)
	{
		printf("请输入ip地址和端口号\n");
		return -1;
	}
	//1、创建用于通信的套接字文件描述符
	int sfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sfd==-1)
	{
		perror("socket error");
		return -1;
	}
	printf("socket success sfd=%d\n",sfd);

	//设置端口号快速重用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)
	{
		perror("setsockopt error");
		return -1;
	}
	printf("设置端口重用成功\n");

	//2、绑定
	//2.1、填充服务器信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(atoi(argv[2]));
	sin.sin_addr.s_addr=inet_addr(argv[1]);
	//2.2、绑定工作
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
	{
		perror("bind error");
		return -1;
	}
	printf("bind success\n");



	//填充客户端信息结构体
	struct sockaddr_in cin[1024];
	struct sockaddr_in cintemp;
	cintemp.sin_family=AF_INET;
	socklen_t socklen=sizeof(cintemp);


	//定义用于检测文件描述符的集合
	fd_set readfds,tempfds;
	//清空容器中的内容
	FD_ZERO(&readfds);
	//将要检测的文件描述符放入集合中
	FD_SET(sfd,&readfds);
	FD_SET(0,&readfds);

	//用于存放客户昵称等信息
	struct cli msg[1024];
	struct cli temp;

	//清空结构体数组
	for(int i=0;i<1024;i++)
	{
		bzero(msg[i].name,sizeof(msg[0].name));
		bzero(msg[i].text,sizeof(msg[0].text));
	}

	char buf[128]="";
	int res=0;  //接收select的返回值

	int maxi=-1;  //群聊成员个数


	while(1)
	{
		//复制一份
		tempfds=readfds;
		//使用select阻塞等待集合中的文件描述符
		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))
		{
			//清空内容
			temp.type=0;
			bzero(temp.name,sizeof(temp.name));
			bzero(temp.text,sizeof(temp.text));
			recvfrom(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cintemp,&socklen);
			if(temp.type=='l'||temp.type=='L')  //登录
			{
				printf("%s[%s:%d]:登录成功\n",temp.name,inet_ntoa(cintemp.sin_addr),ntohs(cintemp.sin_port));
				maxi++;
				strcpy(msg[maxi].name,temp.name);
				cin[maxi].sin_port=cintemp.sin_port;
				cin[maxi].sin_addr.s_addr=cintemp.sin_addr.s_addr;
				for(int j=0;j<=maxi;j++)
				{
					if(cintemp.sin_port==cin[j].sin_port)
					{
						continue;
					}
					sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[j],sizeof(cin[j]));
				}
			}
			if(temp.type=='c'||temp.type=='C')  //聊天
			{
				printf("%s发送了一条消息\n",temp.name);
				for(int j=0;j<=maxi;j++)
				{
					if(cintemp.sin_port==cin[j].sin_port)
					{
						continue;
					}
	
					sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[j],sizeof(cin[j]));
				}
			}
			if(temp.type=='q'||temp.type=='Q')  //退出协议
			{
				int i=0;
				if(strcmp(temp.text,"quit")==0)
				{
					strcpy(temp.name,msg[i].name);
				}
				printf("%s[%s:%d]:已离线\n",temp.name,inet_ntoa(cintemp.sin_addr),ntohs(cintemp.sin_port));

				for(i=0;i<=maxi;i++)
				{
					if(cintemp.sin_port==cin[i].sin_port)
					{
						int t=i;

						for(int j=maxi;j>=i;j--)
						{
							cin[t]=cin[t+1];
							msg[t]=msg[t+1];
							t++;
						}
						maxi--;
					}
					sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[i],sizeof(cin[i]));

				}
			}
		}
		if(FD_ISSET(0,&tempfds))
		{
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1]='\0';

			if(strcmp(buf,"quit")==0)
			{
				close(sfd);
				return 0;
			}

			temp.type='C';
			strcpy(temp.text,buf);
			strcpy(temp.name,"***system***");
			for(int i=0;i<=maxi;i++)
			{
				sendto(sfd,&temp,sizeof(temp),0,(struct sockaddr*)&cin[i],sizeof(cin[i]));
			}
		}

	}
	//5、关闭套接字文件描述符
	close(sfd);
	return 0;
}

客户端

#include <myhd.h>

//传输结构体
struct cli
{
	char type;  //  L/C/Q协议
	char name[20];
	char text[128];
};
int main(int argc, const char *argv[])
{
	if(argc!=3)
	{
		printf("请输入服务器和端口号\n");
		return -1;
	}

	//创建昵称
	char n_name[20]="";
	printf("请输入昵称:");
	scanf("%s",n_name);
	getchar();

	//1、创建客户端套接字文件描述符
	int cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(cfd<0)
	{
		perror("socket error");
		return -1;
	}
	printf("socket success cfd=%d\n",cfd);
	//填充服务器端信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(atoi(argv[2]));
	sin.sin_addr.s_addr=inet_addr(argv[1]);
	socklen_t socklen=sizeof(sin);
	char buf[128]="";
	char rbuf[128]="";

	int res=0; //检测select

	//定义用于检测文件描述符的集合
	fd_set readfds,tempfds;
	//清空容器中的内容
	FD_ZERO(&readfds);
	//将要检测的文件描述符放入集合中
	FD_SET(cfd,&readfds);
	FD_SET(0,&readfds);


	//创建客户端的同时,向服务器发送一个L协议的结构体
	struct cli cmsg;
	cmsg.type='L';
	strcpy(cmsg.name,n_name);
	sendto(cfd,&cmsg,sizeof(cmsg),0,(struct sockaddr*)&sin,sizeof(sin));

	//2、收发数据
	while(1)
	{
		//复制一份
		tempfds=readfds;
		//使用select阻塞等待集合中的文件描述符
		res=select(cfd+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(0,&tempfds))
			{
				
				bzero(buf,sizeof(buf));
				fgets(buf,sizeof(buf),stdin);
				buf[strlen(buf)-1]='\0';
				if(strcmp(buf,"quit")==0)
				{
					cmsg.type='Q';
					strcpy(cmsg.name,n_name);
					sendto(cfd,&cmsg,sizeof(cmsg),0,(struct sockaddr*)&sin,sizeof(sin));
					close(cfd);
					return 0;
				}
				else
				{
					strcpy(cmsg.name,n_name);
					strcpy(cmsg.text,buf);
					cmsg.type='C';
					sendto(cfd,&cmsg,sizeof(cmsg),0,(struct sockaddr*)&sin,sizeof(sin));
				}

			}
			if(FD_ISSET(cfd,&tempfds))
			{
				bzero(buf,sizeof(buf));
				bzero(rbuf,sizeof(rbuf));

				recvfrom(cfd,&cmsg,sizeof(cmsg),0,NULL,NULL);
				if(cmsg.type=='L')
				{
					printf("-----%s登陆成功-----\n",cmsg.name);
				}
				if(cmsg.type=='Q')
				{
					printf("-----%s已离线-----\n",cmsg.name);
				}
				if(cmsg.type=='C')
				{
					strcpy(buf,cmsg.text);
					strcpy(rbuf,cmsg.name);
					printf("%s:%s\n",rbuf,buf);
					if(strcmp(cmsg.name,"***system***"))
					{
						strcpy(cmsg.name,n_name);
					}
				}
			}
	}
	//4、关闭套接字文件描述符
	close(cfd);
	return 0;
}

结果演示

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值