day0901

网络聊天室

1.服务器

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

typedef struct msg{
	char type; //操作码'L'登录 'C'群聊 'Q'下线
	char name[20];
	char text[128];
}msg_t;

typedef struct ADDR{
	struct sockaddr_in clientaddr;
	struct ADDR* next;
}addrlist_t;

void do_login(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr);
void do_chat(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr);
void do_quit(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr);

/******************************************************************************/

int main(int argc, const char *argv[])
{
	if(3 != argc)
	{
		printf("input error\n");
		printf("usage: %s  \n",argv[0]);
		return -1;
	}

	// socket函数
	int sfd;
	if((sfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
	{
		ERR_MSG("socket");
		return -1;
	}

	//填充服务器网络信息结构体
	struct sockaddr_in serveraddr;
	serveraddr.sin_family        = AF_INET;
	serveraddr.sin_port          = htons(atoi(argv[2]));
	serveraddr.sin_addr.s_addr   = inet_addr(argv[1]);
	socklen_t serveraddr_len = sizeof(serveraddr);

	//将套接字绑定网络信息 bind函数
	if(bind(sfd, (struct sockaddr*)&serveraddr, serveraddr_len)<0)
	{
		ERR_MSG("bind");
		return -1;
	}

	//定义客户端网络信息结构体
	struct sockaddr_in clientaddr;
	socklen_t clientaddr_len = sizeof(clientaddr);

	msg_t msg; 
	pid_t pid; //进程号
	if((pid = fork())<0)
	{
		ERR_MSG("fork");
		return -1;
	}
	if(0 == pid)  //子进程,用来发送系统信息

	{
		strcpy(msg.name,"系统消息");
		msg.type = 'C';
		while(1)
		{
			memset(msg.text,0,sizeof(msg.text));
			fgets(msg.text, 128, stdin);
			msg.text[strlen(msg.text)-1] = '\0';
			if(sendto(sfd, &msg, sizeof(msg),0,\
						(struct sockaddr*)&serveraddr, serveraddr_len)<0)
			{
				ERR_MSG("sendto");
				return -1;
			}
		}
	}
	else if(0 < pid) 	//父进程,用来收发数据
	{
		//创建头结点
		addrlist_t* addr = (addrlist_t*)malloc(sizeof(addrlist_t));
		if(NULL==addr)
		{
			printf("malloc error\n");
			return -1;
		}
		memset(addr, 0, sizeof(addr));
		addr->next = NULL;

		//循环收发数据
		while(1)
		{
			//接收新数据及新用户前清空
			memset(&msg, 0, sizeof(msg)); 
			memset(&clientaddr, 0, sizeof(clientaddr));
			if(recvfrom(sfd, &msg, sizeof(msg), 0,\
						(struct sockaddr*)&clientaddr, &clientaddr_len)<0)
			{
				ERR_MSG("recvfrom");
				return -1;
			}
			//判断消息中的错误码,执行对应操作
			switch(msg.type)
			{
			case 'L': //登录操作
				do_login(sfd, msg,addr, clientaddr);
				break;

			case 'C': //群聊请求
				do_chat(sfd, msg,addr, clientaddr);
				break;

			case 'Q': //下线请求
				do_quit(sfd, msg,addr, clientaddr);
				break;
			}
		}

	}

	if(close(sfd)<0)
	{
		ERR_MSG("close");
		return -1;
	}
	return 0;
}

//登录操作的函数
void do_login(int sfd, msg_t msg, addrlist_t* addr,struct sockaddr_in clientaddr)
{
	//遍历链表,将新用户加入群聊的消息发送给所有人
	addrlist_t *temp = addr; //记录链表头结点

	while(temp->next != NULL)
	{
		temp = temp->next;
		if(sendto(sfd, &msg, sizeof(msg), 0, \
					(struct sockaddr*)&(temp->clientaddr), sizeof(temp->clientaddr))<0)
		{
			ERR_MSG("sendto");
			return ;
		}
	}
	//将新用户的网络结构体,头插入链表
	addrlist_t* new = (addrlist_t*)malloc(sizeof(addrlist_t));
	if(NULL == new)
	{
		printf("malloc error\n");
		return ;
	}
	new->clientaddr = clientaddr;
	new->next = addr->next;
	addr->next = new;
	return;
}

//群聊函数
void do_chat(int sfd, msg_t msg, addrlist_t*addr,struct sockaddr_in clientaddr)
{
	//遍历链表,将群消息发送给除了自己外的所有人
	addrlist_t* temp = addr;
	while(temp->next != NULL)
	{
		temp = temp->next;

		if(memcmp(&clientaddr, &(temp->clientaddr),sizeof(clientaddr)))
		{
			if(sendto(sfd, &msg,sizeof(msg), 0, \
						(struct sockaddr*)&(temp->clientaddr),sizeof(temp->clientaddr))<0)
			{
				ERR_MSG("sendto");
				return ;
			}
		}
	}
	return ;
}

//退出聊天
void do_quit(int sfd, msg_t msg, addrlist_t*addr, struct sockaddr_in clientaddr)
{
	//遍历链表,是自己就删除
	//不是自己,就发送退出群聊的消息
	addrlist_t* temp = addr;
	addrlist_t* del = NULL;
	while(temp->next != NULL)
	{
		if(memcmp(&(temp->next->clientaddr),&clientaddr,sizeof(clientaddr)))
		{
			//不是自己
			temp = temp->next;
			if(sendto(sfd, &msg, sizeof(msg), 0, \
						(struct sockaddr*)&(temp->clientaddr), sizeof(temp->clientaddr))<0)
			{
				ERR_MSG("sendto");
				return ;
			}
		}else
		{
			//自己
			del = temp->next;
			temp->next = del->next;
			free(del);
			del = NULL;
		}
	}
	return ;
}

2.客户端

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

typedef struct msg{
	char type; //操作码'L'登录 'C'群聊 'Q'下线
	char name[20];
	char text[128];
}msg_t;
/************************************************/
int main(int argc, const char *argv[])
{
	if(3 != argc)
	{
		printf("input error\n");
		printf("usage: %s  \n",argv[0]);
		return -1;
	}

	// socket函数
	int cfd;
	if((cfd = socket(AF_INET, SOCK_DGRAM, 0))<0)
	{
		ERR_MSG("socket");
		return -1;
	}
	struct sockaddr_in serveraddr; 
	memset(&serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family 		 = AF_INET;
	serveraddr.sin_port   		 = htons(atoi(argv[2]));
	serveraddr.sin_addr.s_addr 	 = inet_addr(argv[1]);
	socklen_t serveraddr_len = sizeof(serveraddr);

	msg_t msg;
	memset(&msg, 0, sizeof(msg));

	printf("请输入用户名:");
	fgets(msg.name, 20, stdin);
	msg.name[strlen(msg.name)-1] = '\0';
	msg.type = 'L';
	strcpy(msg.text, "加入群聊");

	//给服务器发送登录信息
	if(sendto(cfd,&msg, sizeof(msg), 0, \
				(struct sockaddr*)&serveraddr, serveraddr_len)<0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	pid_t pid = 0;
	if((pid = fork())<0)
	{
		ERR_MSG("fork");
		return -1;
	}
	if(0 == pid) 		//子进程循环在终端接收数据
	{
		while(1){
			memset(msg.text,0, sizeof(msg.text));
			fgets(msg.text, 128, stdin);  //在终端获取聊天信息
			msg.text[strlen(msg.text)-1] = '\0';
			if(!strcmp(msg.text,"quit"))
			{
				msg.type = 'Q';
				strcpy(msg.text, "退出群聊"); 
			}else
			{
				msg.type = 'C';
			}
			if(sendto(cfd,&msg, sizeof(msg), 0, \
						(struct sockaddr*)&serveraddr, serveraddr_len)<0)
			{
				ERR_MSG("sendto");
				return -1;
			}
			if(!strcmp(msg.text,"退出群聊")){
				break;
			}
		}
	}
	else if(0 < pid) //父进程,循换接收并打印接收到的数据
	{
		while(1)
		{
			if(recvfrom(cfd, &msg, sizeof(msg), 0, NULL,NULL)<0)
			{
				ERR_MSG("recvfrom");
				return -1;
			}
			printf("[%s]:%s\n",msg.name, msg.text); //打印收到的数据
		}
		kill(pid, SIGKILL);
		wait(NULL);
	}
	close(cfd);
	return 0;
}

你可能感兴趣的:(服务器,开发语言)