基于UDP的网络聊天室

项目需求:

  1. 如果有用户登录,其他用户可以收到这个人的登录信息
  2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
  3. 如果有人下线,其他用户可以收到这个人的下线信息
  4. 服务器可以发送系统信息

 服务器需要给多个用户发送信息,需要创建链表保存各用户的网络信息。

服务器端

代码

 

#include <myhead.h>
#define ERR_text(text) do{\
	fprintf(stderr,"__%d__:",__LINE__);\
	perror(text);\
}while(0)

//用户信息结构体
typedef struct text
{
	char type; //登录'L' ,群聊'C' ,下线'Q'
	char name[20]; //用户姓名
	char text[128]; //聊天信息
}INFO;

//客户端信息链表结构体
typedef struct Node
{
	struct sockaddr_in cliaddr;
	struct Node *next;
}List;
List *L;

struct sockaddr_in cin;
socklen_t addrlen=sizeof(cin);


List list_create();
int list_insert_head(struct sockaddr_in cliaddr);
void list_del(struct sockaddr_in cliaddr);
void request_login(int sfd,INFO info,struct sockaddr_in cin);
void request_chat(int sfd,INFO info,struct sockaddr_in cin);
void request_quit(int sfd,INFO info,struct sockaddr_in cin);
void ser_send(int sfd,struct sockaddr_in cin);
void *fun(void *arg);

int main(int argc, const char *argv[])
{
	if(argc<3){
		fprintf(stderr,"请输入IP PORT\n");
		return -1;
	}
	list_create();

	//创建报式套接字 socket
	int sfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sfd<0){
		ERR_text("socket");
		return -1;
	}
	printf("socket create success sfd=%d\n",sfd);

	//允许端口快速复用
	int reuse=1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse))<0){
		ERR_text("setsockopt");
		return -1;
	}

	int port=atoi(argv[2]);

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family 		=AF_INET;
	sin.sin_port 		=htons(port);
	sin.sin_addr.s_addr =inet_addr(argv[1]);

	//绑定地址信息结构体 bind
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0){
		ERR_text("bind");
		return -1;
	}
	printf("bind success\n");

	//创建子线程
	pthread_t tid;
	pthread_create(&tid,NULL,fun,&sfd);

	//填充对端地址信息结构体	
	while(1){
		ser_send(sfd,cin);

	}
	//关闭套接字
	close(sfd);
	return 0;
}

//创建链表
List list_create()
{
	L=(List *)malloc(sizeof(List))	;
	L->next=NULL;
}

//链表插入
int list_insert_head(struct sockaddr_in cliaddr)
{
	if(NULL==L){
		printf("所给链表不合法\n");
		return -1;
	}
	List *q=L;
	List *p=(List *)malloc(sizeof(List));
	p->cliaddr=cliaddr;
	p->next=q->next;
	q->next=p;

	return 1;

}

//删除结点
void list_del(struct sockaddr_in cliaddr)
{
	if(NULL==L){
		printf("删除失败\n");
		return ;
	}
	List *q=L;
	List *p=L->next;
	while((p->next)!=NULL){
		if(p->cliaddr.sin_port==cliaddr.sin_port){
			break;
		}
		p=p->next;
		q=q->next;
	}
	q->next=p->next;
	free(p);
}
//登录请求
void request_login(int sfd,INFO info,struct sockaddr_in cin)
{
	if(NULL==L){
		printf("登录失败\n");
		return ;
	}
	List *p=L->next;
	char buf[32]="";
	strcat(buf,info.name);
	strcat(buf,"-->>已登录");
	printf("<<--%s\n",buf);
	while(p){
		sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(p->cliaddr),sizeof(cin));
		p=p->next;
	}
	list_insert_head(cin);

}

//群聊请求
void request_chat(int sfd,INFO info,struct sockaddr_in cin)
{
	List *p=L->next;
	char buf[128]="";
	strcat(buf,info.name);
	strcat(buf,":");
	strcat(buf,info.text);
	buf[strlen(buf)-1]=0;
	while(p){
		if((p->cliaddr.sin_port!=cin.sin_port)||(p->cliaddr.sin_addr.s_addr!=cin.sin_addr.s_addr)){
			sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(p->cliaddr),sizeof(cin));
		}
		p=p->next;
	}

}

//退出请求
void request_quit(int sfd,INFO info,struct sockaddr_in cin)
{
	List *p=L->next;
	char buf[32]="";
	strcat(buf,info.name);
	strcat(buf,"退出聊天室");
	printf("%s退出聊天室\n",info.name);
	while(p){
		sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(p->cliaddr),sizeof(cin));
		p=p->next;
	}
	list_del(cin);
}

//发送系统消息
void ser_send(int sfd,struct sockaddr_in cin)
{
	List *p=L->next;
	char buf[128]="";
	char text[64]="";
	strcpy(buf,"system text:");
	fgets(text,sizeof(text),stdin);
	text[strlen(text)-1]=0;
	strcat(buf,text);
	printf("%s\n",buf);
	while(p){
		sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(p->cliaddr),sizeof(cin));
		p=p->next;

	}
	if(strcmp(text,"quit")==0){
		exit(0);
	}
}
//线程收发客户端信息
void *fun(void *arg)
{
	pthread_detach(pthread_self());
	int sfd=*(int *)arg;
	ssize_t res;
	while(1){
		INFO info;
		res=recvfrom(sfd,&info,sizeof(info),0,(struct sockaddr*)&cin,&addrlen);
		if(res<0){
			ERR_text("recvfrom");
			break;
		}

		printf("接收来自%s的信息\n",info.name);
		switch(info.type){
		case 'L':     //登录请求
			request_login(sfd,info,cin);
			break;
		case 'C':     //群聊请求
			request_chat(sfd,info,cin);
			break;
		case 'Q':     //退出请求
			request_quit(sfd,info,cin);
			break;

		}
	}
}

客户端

代码

 

#include <myhead.h>
#define ERR_text(text) do{\
	fprintf(stderr,"__%d__:",__LINE__);\
	perror(text);\
}while(0)

typedef struct
{
	char type;
	char name[20];
	char text[128];
}INFO;

void *rcv_fun(void *arg);
INFO create_text(INFO info,char *name);


int main(int argc, const char *argv[])
{
	INFO info;
	if(argc<3){
		fprintf(stderr,"请输入 IP port\n");
		return -1;
	}
	int port=atoi(argv[2]);
	if(port<1024||port>49151){
		fprintf(stderr,"port %d input error(1024~49151)\n",port);
		return -1;
	}
	//创建报式套接字 socket
	int cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(cfd<0){
		ERR_text("socket");
		return -1;
	}
	printf("socket create success cfd=%d\n",cfd);

	//填充服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family 		=AF_INET;
	sin.sin_port 		=htons(port);
	sin.sin_addr.s_addr =inet_addr(argv[1]);

	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,rcv_fun,&cfd);
	pthread_create(&tid2,NULL,rcv_fun,&cfd);

	char name[20]="";
	printf("请输入用户名>>>");
	fgets(name,sizeof(name),stdin);
	name[strlen(name)-1]=0;
	strcpy(info.name,name);
	info.type='L'; //登录请求
	sendto(cfd,&info,sizeof(info),0,(struct sockaddr*)&sin,sizeof(sin));
	printf("*****************************\n");
	char buf[128]="";
	while(1){
		INFO info;
		char text[128]="";
		fgets(text,sizeof(text),stdin);
		strcpy(info.name,name);
		strcpy(info.text,text);
		if(strcmp(text,"quit\n")==0){
			info.type='Q';
		}else{
			info.type='C';
		}

		//发送数据
		if((sendto(cfd,&info,sizeof(info),0,(struct sockaddr*)&sin,sizeof(sin)))<0){
			ERR_text("sendto");
			return -1;
		}
		if(info.type=='Q'){
			break;
		}
		memset(&info,0,sizeof(info));

	}
	//关闭套接字
	close(cfd);
	return 0;
}

INFO create_text(INFO info,char *name)
{
	char text[64]="";
	fgets(text,sizeof(text),stdin);
	strcpy(info.name,name);
	strcpy(info.text,text);
	if(strcmp(text,"quit\n")==0){
		info.type='Q'; //退出请求
	}else{
		info.type='C';
	}
	return info;
}

void *rcv_fun(void *arg)
{
	pthread_detach(pthread_self());
	int cfd=*(int *)arg;
	while(1){
		char buf[128]="";
		if(recvfrom(cfd,buf,sizeof(buf),0,NULL,NULL)<0){
			ERR_text("recvfrom");
			return NULL;
		}
		if(strcmp(buf,"system text:quit")==0){
			printf("服务器下线\n");
			exit(0);
		}
		printf("%s\n",buf);
	}
}

测试结果

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值