基于UDP的网络聊天室

服务器端:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

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

#define IP "192.168.0.112"
#define PORT 6767

struct msg
{
	char flag;     //'L'登录,'C'聊天,'Q'下线
	char name[20];
	char text[256];
};

typedef struct data
{
	struct sockaddr_in cin;
	char name[20];
	struct data* next;
}*Linklist;

Linklist LinklistcreateData();
int do_quit(int sfd,Linklist *L,char name[20],struct sockaddr_in cin);
int do_login(int sfd,Linklist* L,struct sockaddr_in cin,char name[20]);
Linklist Linklistcreatehead();
int do_chat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256]);
int do_systemchat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256]);

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	
	//sendto有时候会没有权限  error:Permission denied
	//添加权限
	int on=1;
	setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR|SO_BROADCAST,&on,sizeof(on));

	//创建填充地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family   	= AF_INET;
	sin.sin_port   		= htons(PORT);
	sin.sin_addr.s_addr = inet_addr(IP);
	//绑定自身服务器端的IP和端口
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}

	//因为要接收数据,所以要定义一个结构体用来存储客户端的信息
	struct sockaddr_in cin;
	socklen_t addrlen = sizeof(cin);

	char buf[128]="";
	ssize_t res = 0;
	
	//创建进程
	pid_t cpid = fork();

	//创建一个链表头
	Linklist L = Linklistcreatehead();
	//父进程用于接收
	if(cpid > 0)
	{
		struct msg rcvbuf;
		while(1)
		{
			//接收信息
			if(recvfrom(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&cin,&addrlen) <0)
			{
				ERR_MSG("recvfrom");
				return -1;
			}
			printf("flag = %c,%s\n",rcvbuf.flag,rcvbuf.name);

			switch(rcvbuf.flag)
			{
			case 'L':
				do_login(sfd,&L,cin,rcvbuf.name);
				break;
			case 'C':
				do_chat(sfd,&L,cin,rcvbuf.name,rcvbuf.text);
				break;
			case 'Q':
				do_quit(sfd,&L,rcvbuf.name,cin);
				break;
			case 'S':
				do_systemchat(sfd,&L,cin,rcvbuf.name,rcvbuf.text);
			
			}

		}

	}

	else if(0 == cpid)
	{
		//子进程给用户发送信息
		//转发给系统,让系统发送信息给客户端

		struct msg rcvbuf;
		rcvbuf.flag = 'S';
		bzero(rcvbuf.name,sizeof(rcvbuf.name));
		strcpy(rcvbuf.name,"system");
		ssize_t res = 0;

		while(1)	
		{
			scanf("%s",rcvbuf.text);
			while(getchar() != 10);
			/*
			if(0 == strcasecmp(rcvbuf.text,"quit"))
			{
				rcvbuf.flag = 'Q';
			}
			*/
			if(sendto(sfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
			{
				ERR_MSG("sendto");
				return -1;
			}
			/*
			if(0 == strcasecmp(rcvbuf.text,"quit"))
			{
				close(cfd);
				break;
			}
			*/
		} 

	}

	else if(cpid < 0)
	{
		ERR_MSG("fork");
		return -1;
	}


	
	//关闭
	close(sfd);
	return 0;
}

//登录函数
int do_login(int sfd,Linklist* L,struct sockaddr_in cin,char name[20])
{
	//发送用户登录消息
	Linklist q = *L;
	char buf[256] = "";
	sprintf(buf,"用户%s已登录>>>\n",name);
	while(q->next != NULL)
	{
		q=q->next;

		if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
		{
			ERR_MSG("sendto");
			return -1;
		}
	}
	
	//地址信息写入链表
	//申请空间
	Linklist p=LinklistcreateData();
	
	//为节点填充数据
	p->cin=cin;
	strcpy(p->name,name);
	q->next = p;

	return 0; //成功返回0
}

//创建链表头
Linklist Linklistcreatehead()
{
	Linklist L=(Linklist)malloc(sizeof(struct data));
	if(NULL == L)
		return NULL;
	L->cin.sin_family = AF_INET;
	L->cin.sin_port 	  = htons(-1);
	L->cin.sin_addr.s_addr = inet_addr("-1.-1.-1.-1");
	L->next = NULL;
	return L;
}

//创建链表节点
Linklist LinklistcreateData()
{
	Linklist p =(Linklist)malloc(sizeof(struct data));
	if(NULL == p)
		return p;
	//创建成功
	p->cin.sin_family = AF_INET;
	p->cin.sin_port 	  = htons(-1);
	p->cin.sin_addr.s_addr = inet_addr("-1.-1.-1.-1");
	p->next = NULL;
	return p;
}

//发送聊天信息
int do_chat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256])
{
	char buf[256]="";
	Linklist q=*L;
	sprintf(buf,"%s:%s\n",name,text);
	while(q->next != NULL)
	{
		q=q->next;

		if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
		{
			ERR_MSG("sendto");
			return -1;
		}
	} 
	return 0;
}

//群发退出消息
int do_quit(int sfd,Linklist *L,char name[20],struct sockaddr_in cin)
{
	char buf[256]="";
	Linklist q=*L;
	sprintf(buf,"用户:%s已退出\n",name);
	while(q->next != NULL)
	{
			if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
			{
				ERR_MSG("sendto");
				return -1;
			}	
			if(strcasecmp(q->name,name) == 0)
			{

				if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->next->cin),sizeof(cin)) <0)
				{
					ERR_MSG("sendto");
					return -1;
				}	
				Linklist p=q->next;
				q->next = p->next;
				free(p);
				p=NULL;
			}
			else
			{
				q=q->next;
			}
	}
	return 0;
}


//发送聊天信息
int do_systemchat(int sfd,Linklist* L,struct sockaddr_in cin,char name[20],char text[256])
{
	char buf[256]="";
	Linklist q=*L;
	sprintf(buf,"%s:%s\n",name,text);
	while(q->next != NULL)
	{
		q=q->next;

		if(sendto(sfd,buf,sizeof(buf),0,(struct sockaddr*)&(q->cin),sizeof(cin)) <0)
		{
			ERR_MSG("sendto");
			return -1;
		}
	} 
	return 0;
}

客户端:

#include
#include
#include
#include
#include
#include
#include
#include


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

#define SER_IP "192.168.0.112"
#define SER_PORT 6767

#define CLI_IP "192.168.0.111"
#define CLI_PORT 7777

struct msg
{
	char flag;
	char name[20];
	char text[256];
};

int main(int argc, const char *argv[])
{
	//创建报式套接字
	int cfd = socket(AF_INET,SOCK_DGRAM,0);
	if(cfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
	
	//创建填充服务器地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family   	= AF_INET;
	sin.sin_port   		= htons(SER_PORT);
	sin.sin_addr.s_addr = inet_addr(SER_IP);
	
	struct sockaddr_in rcvaddr;
	socklen_t addrlen = sizeof(rcvaddr);
	
	//填充协议
	struct msg rcvbuf;
	rcvbuf.flag = 'L';
	printf("请输入你的名字>>>\n");
	scanf("%s",rcvbuf.name);
	while(getchar() != 10);

	ssize_t res = 0;

	//发送登录请求
	if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	printf("发送成功\n");


	char buf[256]="";

	//创建子进程,实现随时收发
	pid_t cpid=fork();
	//父进程发送消息
	if(cpid > 0)
	{
		rcvbuf.flag = 'C';
		while(1)	
		{
			scanf("%s",rcvbuf.text);
			if(0 == strcasecmp(rcvbuf.text,"quit"))
			{
				rcvbuf.flag = 'Q';
			}
			if(sendto(cfd,&rcvbuf,sizeof(rcvbuf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0)
			{
				ERR_MSG("sendto");
				return -1;
			}
			if(0 == strcasecmp(rcvbuf.text,"quit"))
			{
				close(cfd);
				break;
			}
		} 
	}

	//子进程接收消息
	else if(0 == cpid)
	{
		while(1)
		{
			bzero(buf,sizeof(buf));
			if(recvfrom(cfd,&buf,sizeof(buf),0,NULL,NULL) < 0)
			{
				ERR_MSG("fork");
				return -1;
			}
			if(1 == getppid())
			{
				printf("父进程退出,子进程准备退出>>>\n");
				break;
			}
			printf("接收信息成功\n");
			printf("%s",buf);
		}
	}

	if(cpid < 0)
	{
		ERR_MSG("fork");
		return -1;
	}

	//关闭
	close(cfd);
	close(cpid);
	return 0;
}

运行结果:

PS:接收信息成功和父进程退出两句话是测试用的,可以注掉

基于UDP的网络聊天室_第1张图片

 

你可能感兴趣的:(网络,udp,网络协议)