基于DUP的网络聊天室

基于UDP的网络聊天室的使用(select)完成的服务器端

#include<head.h>
typedef struct de
{
	char name[10];
	struct sockaddr_in cin;
	struct de* next;
}*linklist;
//创建节点
linklist a_creat()
{
	linklist p=(linklist)malloc(sizeof(struct de));
	p->next=NULL;
	bzero(p->name,sizeof(p->name));
	return p;
}
//判断是否是新用户上线
linklist panduan(char *buf,struct sockaddr_in cin,linklist head,int sfd)
{
	linklist p=head;
	while(p->next!=NULL)
	{
		if(p->cin.sin_addr.s_addr==cin.sin_addr.s_addr && p->cin.sin_port==cin.sin_port)
		{
			return head;
		}
		p=p->next;
	}
	linklist q=a_creat();
	q->next=head;
	q->cin=cin;
	strcpy(q->name,buf);
	head=q;
	char sbuf[20]="";
	strcpy(sbuf,q->name);
	strcat(sbuf,"上线");
	printf("%s\n",sbuf);
	while(q->next!=NULL)
	{
		q=q->next;
		sendto(sfd,sbuf,sizeof(sbuf),0,(struct sockaddr*)&(q->cin),sizeof(q->cin));
	}
	return head;
	
}
//释放内存,并且删除该节点
linklist free_t(linklist q,linklist head)
{
	if(q==head)
	{
		head=q->next;
		q->next=NULL;
		free(q);
		return head;
	}
	linklist p=head;
	while(p->next!=q)
	{
		p=p->next;
	}
	p->next=q->next;
	q->next=NULL;
	free(q);
	return head;

}
int main(int argc, const char *argv[])
{
	//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;
	}
	printf("端口号快速重用成功\n");

	//2、绑定
	//2.1填充地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(8888);
	sin.sin_addr.s_addr=inet_addr("192.168.122.22");

	//2.2绑定工作
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)
	{
		perror("bind error");
		return -1;
	}
	//3.定义变量存放客户端地址信息结构体
	struct sockaddr_in cin;
	socklen_t socklen=sizeof(cin);
	int newfd = -1;
	fd_set readfds,tempfds;
	FD_ZERO(&readfds);
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);
	linklist head=a_creat();
	linklist Mhead=head;
	while(1)
	{
		Mhead=head;
		tempfds=readfds;
		int res=select(sfd+1,&tempfds,NULL,NULL,NULL);
		if(res==-1)
		{
			perror("select error");
			return -1;
		}
		else if(res==0)
		{
			printf("timeout\n");
			return -1;
		}
		if(FD_ISSET(0,&tempfds))//服务器发送消息
		{
			char rbuf[128]="";
			bzero(rbuf,sizeof(rbuf));
			scanf("%s",rbuf);
			printf("发送成功\n");
			linklist p=head;
			while(p->next!=NULL)
			{
				sendto(sfd,rbuf,sizeof(rbuf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin));
				p=p->next;
			}
			if(strcmp(rbuf,"quit")==0)
			{
				close(sfd);
				printf("服务器下线\n");
				return 0;
			}
		}
		if(FD_ISSET(sfd,&tempfds))
		{
			char sbuf[128]="";
			recvfrom(sfd,sbuf,sizeof(sbuf),0,(struct sockaddr*)&cin,&socklen);
			head=panduan(sbuf,cin,head,sfd);//判断是否是有用户上线
			if(head==Mhead)//如果头指针没发生改变,说明是链表中的用户发来消息
			{
				linklist yy=head;
				while(yy->next!=NULL)
				{
					if(yy->cin.sin_addr.s_addr==cin.sin_addr.s_addr && yy->cin.sin_port==cin.sin_port)//找到用户对应的名字,并且将消息转发给其他用户
					{
						char dbuf[128]="";
						strcpy(dbuf,yy->name);
						if(strcmp(sbuf,"quit")==0)//判断该用户是否下线
						{
							strcat(dbuf,"下线");
							head=free_t(yy,head);
							linklist ss=head;
							while(ss->next!=NULL)
							{
								if(ss!=yy)
								{
									sendto(sfd,dbuf,sizeof(dbuf),0,(struct sockaddr*)&(ss->cin),sizeof(ss->cin));
								}
								ss=ss->next;
							}
							break;
						}
						strcat(dbuf,":");
						strcat(dbuf,sbuf);
						printf("[%d]%s\n",ntohs(yy->cin.sin_port),dbuf);
						linklist ss=head;
						while(ss->next!=NULL)
						{
							if(ss!=yy)
							{
								sendto(sfd,dbuf,sizeof(dbuf),0,(struct sockaddr*)&(ss->cin),sizeof(ss->cin));
							}
							ss=ss->next;
						}
						break;
					}
					yy=yy->next;
				}
			}
		}
	}


	return 0;
}

使用多进程完成的客户端

#include<head.h>
void handle(int arg)
{
	if(arg==SIGCHLD)
	{
		while(waitpid(-1,NULL,WNOHANG)>0);
	}
}
int main(int argc, const char *argv[])
{
	if(signal(SIGCHLD,handle)==SIG_ERR)
	{
		perror("signal error");
		return -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;
	}
	printf("端口号快速重用成功\n");
	//填充服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(8888);
	sin.sin_addr.s_addr=inet_addr("192.168.122.22");
	socklen_t socklen=sizeof(sin);
	char buf[128]="";
	printf("请输入登录用户名:");
	scanf("%s",buf);
	sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin));
	pid_t pid=fork();
	while(1)
	{

		if(pid>0)
		{
			//主进程输入并且发送消息
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1]='\0';
			sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin));
			if(strcmp(buf,"quit")==0)
			{
				close(sfd);
				printf("你已下线\n");
				return 0;
			}
		}else if(pid==0)
		{
			//子进程接收消
			bzero(buf,sizeof(buf));
			recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&socklen);
			printf("%s\n",buf);
		}
	}
	return 0;
}

结果

  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值