基于udp的网络聊天室

.h文件

#ifndef  __liaotian_h__
#define  __liaotian_h__

#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,"__%d__",__LINE__);\
	perror(msg);\
}while(0)

//定义节点类型
typedef struct node
{
	union
	{
		int len;
		struct sockaddr_in cin;
	};
	struct node *next;
}linklist;

//定义消息包结构体
typedef struct msg
{
	char type;
	char name[20];
	char text[128];
}LCQ_msg;


int insert(linklist *l,struct sockaddr_in cin,LCQ_msg msg);
int zhuanfa(linklist *l,struct sockaddr_in cin,LCQ_msg msg,int sfd);
int notice(linklist *l,LCQ_msg,int sfd);
int cliquit(linklist* l,struct sockaddr_in cin,LCQ_msg msg,int sfd);
		

#endif

服务器功能函数

#include "liaotian.h"


//服务器将客户端节点信息插入链表
int insert(linklist *l,struct sockaddr_in cin,LCQ_msg msg)
{
	if(NULL==l)
	{
		printf("链表不合法\n");
		return -1;
	}
	linklist *p=(linklist*)malloc(sizeof(linklist));
	if(p==NULL)
	{
		printf("节点申请失败\n");
		return -1;
	}
	//把数据放入节点
	p->cin=cin;
	p->next=NULL;
	//头插
	p->next=l->next;
	l->next=p;

	printf("[%s  %d] %s加入聊天室\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),msg.name);
}


//服务器向客户端转发信息
int zhuanfa(linklist *l,struct sockaddr_in cin,LCQ_msg msg,int sfd)
{
	linklist* q=l->next;
	while(q!=NULL)
	{
		if(memcmp(&(q->cin),&cin,sizeof(q->cin))==0)
		{
		    q=q->next;
			continue;
		}
		if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&q->cin,sizeof(q->cin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		q=q->next;
	}
	printf("[%s  %d]%s:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),msg.name,msg.text);
	printf("_%s_的消息转发成功\n",msg.name);
}


//服务器向客户端发送登录信息
int notice(linklist *l,LCQ_msg msg,int sfd)
{
	linklist *q=l->next;
	strcpy(msg.text,"加入群聊");
	while(q!=NULL)
	{
		if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&q->cin,sizeof(q->cin))<0)
		{
			ERR_MSG("sendto");
			return -1;
		}
		q=q->next;
	}
}

//服务器向客户端发送某客户端退出消息,并从链表删除
int cliquit(linklist* l,struct sockaddr_in cin,LCQ_msg msg,int sfd)
{
	linklist *q = l;
	strcpy(msg.text,"退出群聊");
	while(q->next != NULL )
	{
		if(memcmp(&(q->next->cin),&cin,sizeof(cin)))
		{
			if(sendto(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&(q->next->cin), sizeof(q->next->cin)) < 0)
			{                                                                                
				ERR_MSG("sendto");
				return -1;
			}
			q = q->next;
		}
		else 
		{
			//删除逻辑
			linklist *p = q->next;
			q->next = p->next;
			free(p);
			p = NULL;
		}
	}
	printf("[%s  %d] %s 退出聊天室\n",inet_ntoa(cin.sin_addr),cin.sin_port,msg.name);
}


服务器

#include "liaotian.h"


int port,sfd;
LCQ_msg msg;
struct sockaddr_in cin;  //存储接收到的数据包来自与哪里
socklen_t addrlen = sizeof(cin);
linklist *l=NULL;


//打印
#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%d__",__LINE__);\
	perror(msg);\
}while(0)

//#define PORT 8888 //1024-49151
//#define IP "192.168.31.197" //本机ip,ifconfig


void* torcv(void* arg) //用来处理客户端发来的数据包
{
	while(1)
	{
		bzero(&msg,sizeof(msg));
		//接收
		if(recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&addrlen)<0)
		{
			ERR_MSG("recvfrom");
			return NULL;
		}

		//判断发过来的包的类型
		if(msg.type=='L')
		{
			//调用服务器发送登录消息的函数
			notice(l,msg,sfd);
			//调用服务器将客户端节点信息插入链表函数
			insert(l,cin,msg);
		}
		else if(msg.type=='C')
			//调用服务器转发信息函数
			zhuanfa(l,cin,msg,sfd);
		else if(msg.type=='Q')
			//调用服务器告知客户端退出信息函数
			cliquit(l,cin,msg,sfd);
	}
}

void* tosnd(void* arg)   //用来发送服务器系统消息
{
	while(1)
	{
		//发送
	//	printf("请输入>>>>");
		fgets(msg.text,sizeof(msg.text),stdin);
		msg.text[strlen(msg.text)-1]=0;
		msg.type='C';
		strcpy(msg.name,"系统消息");
		linklist *q =l;
		while(q->next!=NULL)
		{
			q=q->next;
			if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&q->cin,sizeof(q->cin))<0)
			{
				ERR_MSG("sendto");
				return NULL;
			}
		}
	}
}

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

	//创建客户端链表
	 l = (linklist*)malloc(sizeof(linklist));
	if(NULL==l)
	{
		printf("创建失败\n");
		return -1;
	}
	//给节点初始化
	l->len = 0; //头结点数据域为0
	l->next = NULL;//指针域为空
	//printf("创建客户端链表成功\n");


	//将获取的字符串强转成整形
	port = atoi(argv[2]);
	if(port<1024||port>49151)
	{
		fprintf(stderr,"port error\n");
		return -1;
	}
	//创建报式套接字
	sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0 )
	{
		ERR_MSG("socket");
		return -1;
	}
	printf("socket create success\n");

	//填充地址信息结构体,真实的地址信息结构体与协议族相关
	//AF_INET,所以详情在man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);//网络字节序的端口号
	sin.sin_addr.s_addr =inet_addr(argv[1]);//网络字节序的ip地址


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


	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,torcv,NULL);
	pthread_create(&tid2,NULL,tosnd,NULL);
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	close(sfd);
	return 0;
}

客户端

#include "liaotian.h"



int port,sfd;
LCQ_msg msg;
LCQ_msg msgr;

void* torcv(void* arg)
{
	while(1)
	{
		//接收
		//bzero(&msg.text,sizeof(msg.text));
		memset(&msgr,0,sizeof(msgr));
		//接收服务器发送过来的消息包
		if(recvfrom(sfd,&msgr,sizeof(msgr),0,NULL,NULL)<0)
		{
			ERR_MSG("recvfrom");
			return NULL;
		}
		printf("%s: %s\n",msgr.name,msgr.text);
	}
}



void* tosnd(void* arg)
{
	struct sockaddr_in sin=*(struct sockaddr_in*)arg;
    //给服务器发送数据包
	while(1)
	{
		bzero(&msg.text,sizeof(msg.text));
	//	printf("请输入>>>");
		fgets(msg.text,sizeof(msg.text),stdin);
		msg.text[strlen(msg.text)-1]=0;
		//判断发送的是数据包还是退出包
		if(strcasecmp(msg.text,"quit")==0)
			msg.type='Q';
		else
			msg.type='C';
			
		//发送
		if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sizeof(sin))<0)
		{
			ERR_MSG("sendto");
			return NULL;
		}

		//发送完退出包后会退出客户端
		if(strcasecmp(msg.text,"quit")==0)
			exit(0);

		printf("sendto success\n");
	}
}





//#define PORT 8888 //1024-49151
//#define IP "192.168.31.197" //本机ip,ifconfig



int main(int argc, const char *argv[])
{
	if(argc<3)
	{
		fprintf(stderr,"请输入ip,port\n");
		return -1;
	}
	//将获取的字符串强转成整形
	port = atoi(argv[2]);
	if(port<1024||port>49151)
	{
		fprintf(stderr,"port error\n");
		return -1;
	}

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

	//填充地址信息结构体,真实的地址信息结构体与协议族相关
	//AF_INET,所以详情在man 7 ip
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);//网络字节序的端口号
	sin.sin_addr.s_addr =inet_addr(argv[1]);//网络字节序的ip地址
	socklen_t addrlen = sizeof(sin);


	printf("请先输入你的群名\n");
	scanf("%s",msg.name);
	getchar();
	msg.type='L';

    //给服务器发送登录包
	if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,addrlen) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}

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

	//	struct sockaddr_in sin;
	pthread_t tid1,tid2;
	pthread_create(&tid1,NULL,torcv,NULL);
	pthread_create(&tid2,NULL,tosnd,(void*)(&sin));
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);

	close(sfd);
	return 0;
}

效果展示

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值