插曲 网络聊天室作业

发布于:2024-08-20 ⋅ 阅读:(118) ⋅ 点赞:(0)

 

 

#ifndef HHHH
#define HHHH
#include<myhead.h>
typedef struct Node
{
	int len;
	char name[128];
	struct sockaddr_in cin;
	struct Node*next;
}No,*NodePtr;

//创建头结点
NodePtr create();

int list_empty(NodePtr L);//判空

NodePtr pos(char*name,struct sockaddr_in cin);//封装

int bianli(NodePtr L,struct sockaddr_in cin);//按信息返回位置

int add(NodePtr L,char*name,struct sockaddr_in cin);//添加节点

int kan(NodePtr L);//打印在线人数信息

int list_sendto(NodePtr L,int sfd,char*rbuf);//向在线人员发送信息

int list_send(NodePtr L,int sfd,char*rbuf);//下线信息发送

NodePtr find(NodePtr L,struct sockaddr_in cin);//按数据查找地址

NodePtr wei(NodePtr L,int len);//按位置查找地址

int list_delete(NodePtr L,struct sockaddr_in cin);//删除节点

int tou(NodePtr L);//全部删除
#endif
#include"work1.h"

NodePtr create()//创建头结点
{
	NodePtr L=(NodePtr)malloc(sizeof(No));//申请空间
	if(NULL==L)
	{
		printf("申请失败\n");
		return NULL;
	}
	bzero(L,sizeof(No));//初始化结点
	L->len=0;
	L->next==NULL;
	return L;
}
int list_empty(NodePtr L)
{
	return L==NULL;//返回值为判断条件
}
NodePtr pos(char*name,struct sockaddr_in cin)//封装
{
	NodePtr L=(NodePtr)malloc(sizeof(No));
	if(NULL==L)
	{
		printf("申请失败\n");
		return NULL;
	}
	bzero(L,sizeof(No));
	strcpy(L->name,name);//填充信息
	L->cin=cin;
	L->next=NULL;
	return L;
}
int bianli(NodePtr L,struct sockaddr_in cin)//按信息返回位置

{
	if(L==NULL)
	{
		printf("遍历失败\n");
		return -1;
	}
	int arr=1;
 	struct	sockaddr_in sin =cin;//填充信息
	char *p1=inet_ntoa(sin.sin_addr);//填充信息
	char *p2=NULL;
	int p3=ntohs(sin.sin_port);//填充信息
	int p4;
	NodePtr P=L->next;
	while(P!=NULL)
	{
		p2=inet_ntoa(P->cin.sin_addr);//获取信息
		p4=ntohs(P->cin.sin_port);
		if((strcmp(p1,p2)==0)&&(p3==p4))//比较内容
		{
			return arr;
		}
		arr++;
		P=P->next;
	}
	p1=NULL;//清空指针
	p2=NULL;
	return 0;
}
int kan(NodePtr L)
{
	if(L==NULL||list_empty(L))
	{
		printf("遍历失败\n");
		return -1;
	}
	NodePtr P=L->next;
	printf("[在线列表]当前在线人数:%d\n",L->len);//打印在线人数
	while(P!=NULL)
	{
		printf("[%s,%d]:%s\n",inet_ntoa(P->cin.sin_addr),ntohs(P->cin.sin_port),P->name);//打印在线人数信息
		P=P->next;
	}
	return 0;
}
int add(NodePtr L,char*name,struct sockaddr_in cin)//添加
{
	if(L==NULL)
	{
		printf("添加失败\n");
		return -1;
	}
	NodePtr l=pos(name,cin);//封装节点返回地址
	NodePtr q=L;
	while(1)
	{
		if(q->next==NULL)//循环查找最后一个节点
		{
			q->next=l;//添加地址
			break;
		}
		q=q->next;
	}
	L->len++;//链表长度增加
	return 0;
}
int list_sendto(NodePtr L,int cfd,char*rbuf)//向所有人发送信息
{
	if(NULL==L,list_empty(L))
	{
		printf("发送失败\n");
		return -1;
	}
	int sfd=cfd;
	char buf[128]="";
	strcpy(buf,rbuf);
	NodePtr P=L->next;
	while(P!=NULL)//循环进入所有节点
	{
		strcpy(buf,rbuf);
		if(sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&(P->cin),sizeof(P->cin))==-1)//发送信息
		{
			perror("send");
			return -1;
		}
		P=P->next;
	}
	return 0;
}
NodePtr find(NodePtr L,struct sockaddr_in cin)//按数据找地址
{
	if(NULL==L||list_empty(L))
	{
		printf("查找失败\n");
		return NULL;
	}
	
	int a=bianli(L,cin);//获取节点所在位置
	NodePtr P=wei(L,a);//获取位置所在地址

	return P;
}
NodePtr wei(NodePtr L,int len)//按位置查找
{
	if(NULL==L||list_empty(L)||len<1||len>L->len)
	{
		printf("查找失败\n\n");
		return NULL;
	}
	NodePtr P=L;
	for(int i=1;i<=len;i++)//按位置查找节点返回地址
	{
		P=P->next;
	}
	return P;
}
int list_delete(NodePtr L,struct sockaddr_in cin)//删除
{
	if(NULL==L||list_empty(L))
	{
		printf("删除失败\n");
		return -1;
	}
	struct sockaddr_in sin=cin;//获取信息
	int arr=bianli(L,sin);
	if(arr==1)//如果是头节点直接删除
	{
		NodePtr n=L->next;
		L->next=L->next->next;
		free(n);
		n=NULL;
	}else if(arr>1)//如果不是头节点获取地址再删除
	{
	NodePtr q=wei(L,arr-1);
	NodePtr e=q->next;
	q->next=q->next->next;
	free(e);
	}
	L->len--;//链表长度减少
	return 0;
}
int list_send(NodePtr L,int cfd,char*name)//死亡发送
{

	if(NULL==L||list_empty(L))
	{
		printf("发送失败\n");
		return -1;
	}
	int sfd=cfd;
	char buf[128]="";
	NodePtr P=L->next;
	while(P!=NULL)
	{
		strcpy(buf,name);//获取下线信息
		if(sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&(P->cin),sizeof(P->cin))==-1)//向在线用户发送下线信息
		{
			perror("send");
			return -1;
		}
		P=P->next;
	}
	return 0;
}
int tou(NodePtr L)//全部删除
{
	if(L==NULL)
	{
		printf("头删除失败\n");
	}
	NodePtr q=NULL;//定义地址指针
	while(L->next!=NULL)//循环头删
	{
		q=L->next;
		L->next=L->next->next;
		free(q);//释放空间
		q=NULL;//指针指向空
		L->len--;//减少链表长度
	}
	free(L);//释放头结点
	L=NULL;//释放指针
	return 0;
}
#include "work1.h"
struct NB//创建结构体
{
	int sfd;
	char name[128];
	char text[128];
	struct sockaddr_in sin;
	NodePtr L;
};
void* stak1(void*arg)
{
		NodePtr P=NULL;
		NodePtr Q=NULL;
		char rbuf[128]="";
		char name[128]="";
		char crr[128]="";
		char drr[128]="";
		NodePtr L=(*(struct NB*)arg).L;//获取链表头结点地址
		int sfd=(*(struct NB*)arg).sfd;//获取套接字符
		struct sockaddr_in sin=(*(struct NB*)arg).sin;//获取结构体信息
		socklen_t addrlen=sizeof(sin);
		while(1){
		bzero(rbuf,sizeof(rbuf));//清空容器
		if(recvfrom(sfd,rbuf,sizeof(rbuf),0,(struct sockaddr*)&sin,&addrlen)==-1)//获取传输信息
		{
			perror("recv");
			pthread_exit(NULL);
		}
		int a=bianli(L,sin);//判断传入地址是否第一次加入链表
		if(a==0)//是第一次
		{
		bzero(name,sizeof(name));//刷新容器
		strcpy(name,rbuf);//复制信息
		strcat(name,"上线");
		list_sendto(L,sfd,name);//上线信息发送给所有在线用户
		add(L,rbuf,sin);//加入在线列表
		kan(L);//查看在线列表
		printf("%s\n",name);//打印上线用户
		}else if(a>0)//不是第一次加入
		{
		
		if(strcmp(rbuf,"quit")==0)//判断获取信息
		{
			if(sendto(sfd,rbuf,strlen(rbuf),0,(struct sockaddr*)&sin,sizeof(sin))==-1)//发送信息
			{
				perror("send");
				return NULL;
			}
			P=find(L,sin);//获取网络地址
			char brr[128]="已经下线";
			bzero(name,sizeof(name));
			strcpy(name,P->name);
			strcat(name,brr);
			list_delete(L,sin);//删除存储网络地址的节点
			list_sendto(L,sfd,name);//发送给所有在线成员下线信息
			kan(L);//打印在线列表
			printf("%s\n",name);//打印下线用户

			P=NULL;//指针清空
		}else if(strcmp(rbuf,"quit")!=0)
			{
			Q=find(L,sin);//找到网络地址所在节点
			strcpy(crr,Q->name);
			strcat(crr,":");
			strcat(crr,rbuf);
			printf("%s\n",crr);
			list_sendto(L,sfd,crr);//向在线用户发送信息
			Q=NULL;
			}
		}
		}
		pthread_exit(NULL);
}
void* stak2(void*arg)
{
		char wrr[128]="";
		NodePtr L=(*(struct NB*)arg).L;//接收链表头结点地址
		int sfd=(*(struct NB*)arg).sfd;//接受套接字符
		struct sockaddr_in sin=(*(struct NB*)arg).sin;//接收网络结构体信息
		char buf[128]="";
		while(1)
		{
			strcpy(wrr,"服务器:");
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);//输入信息
			buf[strlen(buf)-1]=0;
			strcat(wrr,buf);
			list_sendto(L,sfd,wrr);//向全体在线人员发送信息
			if(strcmp(buf,"exit")==0)//判断信息内容是否关闭线程
			{
				tou(L);//删除链表
				L=NULL;//清空指针
				printf("服务器关闭\n");
				break;
			}
		}

		pthread_exit(NULL);//杀死线程
}

int main(int argc, const char *argv[])
{
	NodePtr L=create();//创建链表

	int sfd = socket(AF_INET,SOCK_DGRAM,0);//创建用于UDP的套接字符
	if(sfd==-1)
	{
		perror("socket");
		return -1;
	}
	
	int reuse =1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)//设置快速重新启用端口号
	{
		perror("reuse");
		return -1;
	}
	struct sockaddr_in sin;//填充信息
	sin.sin_family=AF_INET;
	sin.sin_port=htons(6666);
	sin.sin_addr.s_addr=inet_addr("192.168.2.159");

	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))==-1)//绑定信息
	{
		perror("bind");
		return -1;
	}
	printf("服务器启动\n");
	struct NB nb={sfd,"","",sin,L};//填充饥结构体信息

	pthread_t tid1,tid2;//创建两个线程
	
	if(pthread_create(&tid1,NULL,stak1,&nb)==-1)//传递线程1信息
	{	
		perror("create1");
		return -1;
	}
	if(pthread_create(&tid2,NULL,stak2,&nb)==-1)//传递线程2信息
	{	
		perror("create1");
		return -1;
	}
//	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);//阻塞等待子线程
	close(sfd);//关闭套接字
	return 0;
}
#include <myhead.h>
struct NB
{
	char type;
	char name[20];
	char text[100];
}nb;
int main(int argc, const char *argv[])
{
	
	int zf[2];
	pipe(zf);//创建无名管道通信
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd==-1)
	{
		perror("socket");
		return -1;
	}
	
	int reuse =1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))==-1)//设置端口号快速重启权限
	{
		perror("reuse");
		return -1;
	}

	struct sockaddr_in cin;//填充信息
	cin.sin_family=AF_INET;
	cin.sin_port=htons(6666);
	cin.sin_addr.s_addr=inet_addr("192.168.2.159");
	socklen_t addrlen=sizeof(cin);
	
	struct NB nb={0,"",""};

	pid_t pid =fork();//创建子进程

	if(pid>0)
	{
		char rbuf[128]="";
		while(1)
		{

		bzero(rbuf,sizeof(rbuf));
		if(recvfrom(sfd,rbuf,sizeof(rbuf),0,(struct sockaddr*)&cin,&addrlen)==-1)//读取网络信息
		{
			perror("recvfrom");
			return -1;
		}
		if(strcmp(rbuf,"quit")==0)//判断退出条件
		{
			break;
		}else if(strcmp(rbuf,"服务器:exit")==0)//判断服务器信息
		{
			pid_t bz;
			read(zf[0],&bz,4);//读取管道信息
			kill(bz,1);//杀死子进程
			printf("服务器失去连接,强制下线\n");
			break;
		}
		printf("%s\n",rbuf);//打印获取信息
		}

	}else if(pid==0)
	{
		pid_t pid1=getpid();//获取进程号
		write(zf[1],&pid1,4);//写入管道内容
		printf("请输入姓名>>>");
		fgets(nb.name,sizeof(nb.name),stdin);//输入姓名内容
		nb.name[strlen(nb.name)-1]=0;
		if(sendto(sfd,nb.name,sizeof(nb.name),0,(struct sockaddr*)&cin,sizeof(cin))==-1)//传输网络姓名内容信息
		{
			perror("sendto");
			return -1;
		}
		printf("登陆成功!\n");
		while(1)
		{
			usleep(100);//睡眠一下让父进程先载入
			bzero(nb.text,sizeof(nb.text));//刷新内容缓存
			fgets(nb.text,sizeof(nb.text),stdin);//输入信息
			nb.text[strlen(nb.text)-1]=0;
			if(sendto(sfd,nb.text,sizeof(nb.text),0,(struct sockaddr*)&cin,sizeof(cin))==-1)//传输信息
			{
				perror("sendto");
				return -1;
			}else if(strcmp(nb.text,"quit")==0)//判断终止条件
			{
				printf("退出成功\n");
				close(sfd);//关闭套接字字符
				exit(EXIT_SUCCESS);//杀死子进程

			}
			printf("发送成功\n");
		}
	}
	
	wait(NULL);//等待回收子进程
	close(sfd);//关闭套接字符

	return 0;
}