基于UDP的网络聊天室

基于UDP的网络聊天室运行结果功能:

登录:

在这里插入图片描述

群聊:

在这里插入图片描述

服务器发消息:

在这里插入图片描述

下线:

在这里插入图片描述

代码

服务器:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.31.143"
#define PORT 5896


//创建协议结构体
struct MSG
{
	char type;
	char name[20];
	char text[128];
}msg;

//创建链表结构体
typedef struct Node
{
	//数据域
	int len;
	struct sockaddr_in cin;
	//指针域
	struct Node* next;
} * Linklist;

Linklist l;

//函数声明
void *deal_cli_msg(void *arg);
Linklist create_head();
Linklist create_node(struct sockaddr_in cin);
int insert_head(Linklist l,struct sockaddr_in cin);
int search_data(Linklist l,struct sockaddr_in cin);
int delete_pos(Linklist l,int pos);
int delete_head(Linklist l);
Linklist list_free(Linklist l);


int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0){
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success\n");

	//绑定
	struct sockaddr_in sin;
	sin.sin_family  = AF_INET;
	sin.sin_port    = htons(PORT);
	sin.sin_addr.s_addr  =  inet_addr(IP);


	if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin)) < 0){
		ERR_MSG("bind");
		return -1;
	}
	printf("bing success\n");


	//接受
	ssize_t res = 0;
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	//创建链表头结点
	l = create_head();
	if(NULL == l){
		return -1;
	}

	//创建线程,主线程负责接收
	pthread_t tid;
	if(pthread_create(&tid,NULL,deal_cli_msg,&sfd) != 0){
		fprintf(stderr,"line:%d pthread_create failed\n",__LINE__);
	}

	//主线程接收数据包,根据接收到的结构体的协议不同做不同功能
	while(1){
		//清空所有缓冲区和数组
		memset(msg.name,0,sizeof(msg.name));
		memset(msg.text,0,sizeof(msg.text));
		//接受数据包结构体
		res = recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&addrlen);
		if(res < 0){
			ERR_MSG("recvfrom");
			return -1;
		}
		//判断数据包类型
		if('L' == msg.type){
			printf("[%s:%d]:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),msg.name);
			//加入到链表中(头插)
			insert_head(l,cin);
			//给其他客户端发送登录消息
			Linklist q = l;
			for(int i=0; i<l->len; i++){
				q = q->next;
				if((q->cin.sin_port != cin.sin_port) &&(q->cin.sin_addr.s_addr == cin.sin_addr.s_addr)){
					if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&(q->cin),sizeof(q->cin)) < 0){
						ERR_MSG("sendto");
						break;
					}
				}
			}
		}else if('C' == msg.type){
			printf("[%s:%d]:%s:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),msg.name,msg.text);
			//给其他客户端发送群聊消息
			Linklist q = l;
			for(int i=0; i<l->len; i++){
				q = q->next;
				if((q->cin.sin_port != cin.sin_port) &&(q->cin.sin_addr.s_addr == cin.sin_addr.s_addr)){
					if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&(q->cin),sizeof(q->cin)) < 0){
						ERR_MSG("sendto");
						break;
					}
				}
			}
		}else if('Q' == msg.type){
			printf("%s下线\n",msg.name);
			//给其他客户端发送下线消息
			msg.type = 'Q';
			Linklist q = l;
			for(int i=0; i<l->len; i++){
				q = q->next;
				if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&(q->cin),sizeof(q->cin)) < 0){
					ERR_MSG("sendto");
					break;
				}
			}
			//将该客户端从链表中删除
			int pos = search_data(l,cin);
			delete_pos(l,pos);
		}
	}
	l = list_free(l);
	close(sfd);
	return 0;
}


//子线程发送系统信息
void *deal_cli_msg(void *arg)
{
	pthread_detach(pthread_self());
	int sfd = *(int *)arg;
	struct MSG msg;
	while(1){
		msg.type = 'S';
		memset(msg.text,0,sizeof(msg.text));
		fgets(msg.text,sizeof(msg.text),stdin);
		msg.text[strlen(msg.text)-1] = '\0';
		Linklist p = l;
		//给所有客户端发送消息
		for(int i=0; i<l->len; i++){
			p = p->next;
			if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr *)&(p->cin),sizeof(p->cin)) < 0){
				ERR_MSG("sendto");
				break;
			}
			printf("__%d__\n",__LINE__);

		}
	}
}



//创建头结点
Linklist create_head()
{
	Linklist l = (Linklist)malloc(sizeof(struct Node));
	if(NULL == l)
		return NULL;
	l->len = 0;
	l->next = NULL;
	return l;
}

//创建普通节点
Linklist create_node(struct sockaddr_in cin)
{
	Linklist p = (Linklist)malloc(sizeof(struct Node));
	if(NULL == p)
		return NULL;
	p->cin = cin;
	p->next = NULL;
	return p;
}
//头插
int insert_head(Linklist l,struct sockaddr_in cin)
{
	if(NULL == l){
		printf("插入失败\n");
		return -1;
	}
	Linklist p = create_node(cin);
	p->next = l->next;
	l->next = p;
	l->len++;
	return 0;
}

//按元素查找
int search_data(Linklist l,struct sockaddr_in cin)
{
	if(NULL == l || 0 == l->next){
		printf("查找失败\n");
		return -1;
	}
	Linklist p = l;
	int count = 0;
	while(p->next != NULL){
		p = p->next;
		count++;
		if((p->cin.sin_port == cin.sin_port) && (p->cin.sin_addr.s_addr == cin.sin_addr.s_addr)){
			return count;
		}
	}
	return 0;
}

//按位置删除
int delete_pos(Linklist l,int pos)
{
	if(NULL == l || 0 == l->len || pos < 1 || pos > l->len){
		printf("删除失败\n");
		return -1;
	}
	Linklist p = l;
	for(int i=0; i<pos-1; i++){
		p = p->next;
	}
	Linklist q = p->next;
	p->next = q->next;
	free(q);
	q = NULL;
	l->len--;
	return 0;
}

//头删
int delete_head(Linklist l)
{
	if(NULL == l || 0 == l->len){
		printf("删除失败\n");
		return -1;
	}
	Linklist q = l->next;
	l->next = q->next;
	free(q);
	q = NULL;
	l->len--;
	return 0;
}

//释放链表
Linklist list_free(Linklist l)
{
	if(NULL == l){
		return NULL;
	}	
	int n = l->len;
	for(int i=0; i<n; i++){
		delete_head(l);
	}
	free(l);
	l = NULL;
	return l;
}

客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"line:%d",__LINE__);\
	perror(msg);\
}while(0)

#define IP "192.168.31.143"
#define PORT 5896

//创建协议结构体
struct MSG
{
	char type;
	char name[20];
	char text[128];
};

//创建给子线程传参的结构体
struct Node
{
	struct sockaddr_in sin;
	int cfd;
	struct MSG msg;
};

//函数声明
void *deal_cli_msg(void *arg);

int main(int argc, const char *argv[])
{
	struct Node value;
	//创建报式套接字
	value.cfd = socket(AF_INET,SOCK_DGRAM,0);
	if(value.cfd < 0){
		ERR_MSG("socket");
		return -1;
	}
	printf("socket success\n");

	//服务器地址信息
	value.sin.sin_family  = AF_INET;
	value.sin.sin_port    = htons(PORT);
	value.sin.sin_addr.s_addr  =  inet_addr(IP);

	//登录
	value.msg.type = 'L';
	printf("输入用户名:");
	scanf("%s",value.msg.name);
	while(getchar()!='\n');
	if(sendto(value.cfd,&value.msg,sizeof(value.msg),0,(struct sockaddr *)&(value.sin),sizeof(value.sin)) < 0){
		ERR_MSG("sendto");
		return -1;
	}
	printf("登录成功\n");

	//创建线程,主线程负责接收
	pthread_t tid;
	pthread_create(&tid,NULL,deal_cli_msg,&value);
	pthread_detach(tid);

	//定义接收数据包结构体的变量
	struct MSG jmsg;
	ssize_t res = 0;
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);
	while(1){
		//接收数据包
		memset(jmsg.text,0,sizeof(jmsg.text));
		res = recvfrom(value.cfd,&jmsg,sizeof(jmsg),0,(struct sockaddr*)&cin,&addrlen);
		if(res < 0){
			ERR_MSG("recvfrom");
			return -1;
		}
		if('L' == jmsg.type){
			printf("%s上线\n",jmsg.name);
		}else if('S' == jmsg.type){
			printf("服务器:%s\n",jmsg.text);
		}else if('C' == jmsg.type){
			printf("%s:%s\n",jmsg.name,jmsg.text);
		}else if('Q' == jmsg.type){
			if(strcmp(jmsg.name,value.msg.name) == 0){
				break;
			}else{
				printf("%s已下线\n",jmsg.name);
			}
		}
	}
	close(value.cfd);
	return 0;
}

void *deal_cli_msg(void *arg)
{
	struct sockaddr_in sin = (*(struct Node*)arg).sin;
	int cfd = (*(struct Node*)arg).cfd;
	struct MSG msg = (*(struct Node*)arg).msg;
	while(1){
		memset(msg.text,0,sizeof(msg.text));
		fgets(msg.text,sizeof(msg.text),stdin);
		msg.text[strlen(msg.text)-1] = '\0';
		//发送群聊信息
		msg.type = 'C';
		if(sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr *)&(sin),sizeof(sin)) < 0){
			ERR_MSG("sendto");
			break;
		}else{
		//发送下线信息
			if(strcmp(msg.text,"quit") == 0){
				msg.type = 'Q';
				if(sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr *)&(sin),sizeof(sin)) < 0){
					ERR_MSG("sendto");
					break;
				}	
				break;
			}
		}
	}
	close(cfd);
	pthread_exit(NULL);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值