【网络编程】学习成果day6。

完成网络聊天室编写
服务器:

#include 
#define N 128
//协议
#define LOGIN 	1
#define CHAT 	2
#define QUIT 	3

#define ERR_LOG(msg) do{\
	printf("%d %s %s \n", __LINE__, __func__, __FILE__);\
	perror(msg);\
}while(0)

//收发信息的结构体
typedef struct
{
	int type;
	char name[20];
	char text[N] ;

}MSG;


typedef struct node
{
	struct sockaddr_in cin;
	struct node* next;
}__linklist;

int do_recv(int sfd, __linklist* head);
int do_login(int sfd, __linklist* head, MSG rcv_msg, struct sockaddr_in cin);
int do_quit(int sfd, __linklist* head, MSG rcv_msg, struct sockaddr_in cin);
int do_chat(int sfd, __linklist* head, MSG rcv_msg, struct sockaddr_in cin);
int do_system(int sfd, struct sockaddr_in sin);

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

	//2.绑定服务器ip和端口号
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET;
	sin.sin_port 		= htons(6666);
	sin.sin_addr.s_addr = inet_addr("127.0.0.1");

	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		perror("bind");
		return -1;
	}
	
	//创建进程或线程
	int pid = 0;
	pid = fork();
	if(pid > 0)
	{
		//父进程, 接收消息
		__linklist* head = (__linklist*)malloc(sizeof(__linklist));
		head->next = NULL;
		do_recv(sfd , head);

	}
	else if(pid==0)
	{
		//子进程,发送系统消息
		do_system(sfd, sin);
	}


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

int do_system(int sfd, struct sockaddr_in sin)
{
	MSG sys_msg = {htonl(CHAT), "**system**"};

	while(1)
	{
		bzero(sys_msg.text, N);
		fgets(sys_msg.text, N, stdin);
		sys_msg.text[strlen(sys_msg.text)-1] = 0;

		//将当前进程当做客户端,父进程当做服务器,发送信息;
		if(sendto(sfd, &sys_msg, sizeof(sys_msg), 0, (void*)&sin, sizeof(sin)) < 0)
		{
			ERR_LOG("sendto");
			return -1;
		}
	}
	printf("系统消息发送成功");
	return 0;
}

int do_recv(int sfd, __linklist* head)
{
	MSG rcv_msg;
	int recv_len = 0;
	struct sockaddr_in cin;
	socklen_t clen = sizeof(cin);

	while(1)
	{
		//接收消息
		recv_len = recvfrom(sfd, &rcv_msg, sizeof(rcv_msg), 0, (void*)&cin, &clen);
		if(recv_len < 0)
		{
			ERR_LOG("recvfrom");
			return -1;
		}
		//提取出协议
		//将网络字节序转换为主机字节序
		int type = ntohl(rcv_msg.type);

		switch(type)
		{
		case LOGIN:
			do_login(sfd, head, rcv_msg, cin);
			break;
		case CHAT:
			do_chat(sfd, head, rcv_msg, cin);
			break;
		case QUIT:
			do_quit(sfd, head, rcv_msg, cin);
			break;
		}

	}
}

int do_chat(int sfd, __linklist* head, MSG rcv_msg, struct sockaddr_in cin)
{
	
	printf("%s [%s:%d]chat成功\n", rcv_msg.name,\
			(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));
	//重新拼接群聊消息: 名字+消息
	char buf[258] = "";
	sprintf(buf, "%s:%s", rcv_msg.name, rcv_msg.text);
	strcpy(rcv_msg.text, buf);

	//循环发送,除了自己以外的ip地址
	while(head->next != NULL)
	{
		head = head->next;
		if(memcmp(&cin, &head->cin, sizeof(cin)) != 0)
		{	
			if(sendto(sfd, &rcv_msg, sizeof(rcv_msg),0, (void*)&(head->cin), sizeof(head->cin))<0)
			{
				ERR_LOG("sendto");
				return -1;
			}
		}
	}
	return 0;	
}



int do_quit(int sfd, __linklist* head, MSG rcv_msg, struct sockaddr_in cin)
{
	sprintf(rcv_msg.text, "-----%s 已下线-----\n", rcv_msg.name);
	//循环遍历链表,发送"xxx一下线"的信息
	//发送给当前客户端外的其余客户端
	while(head->next != NULL)
	{
		if(memcmp(&cin, &head->next->cin, sizeof(cin)) == 0)
		{
			//从链表中删除该客户端信息
			//free
			__linklist* temp = head->next;
			head->next = temp->next;
			free(temp);
		}
		else
		{
			head = head->next;
			if(sendto(sfd, &rcv_msg, sizeof(rcv_msg),0, (void*)&head->cin, sizeof(head->cin))<0)
			{
				ERR_LOG("sendto");
				return -1;
			}
		}
	}
	return 0;
}


int do_login(int sfd, __linklist* head, MSG rcv_msg, struct sockaddr_in cin)
{
	//打印登录成功
	printf("%s [%s:%d]登录成功\n", rcv_msg.name,\
			(char*)inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

	sprintf(rcv_msg.text, "-----%s登录成功-----", rcv_msg.name);


	while(head->next != NULL)
	{
		head = head->next;
		if(sendto(sfd, &rcv_msg, sizeof(rcv_msg), 0, (void*)&(head->cin), sizeof(head->cin))<0)
		{
			ERR_LOG("sendto");
			return -1;
		}
	}

	//将登录成功的客户端信息添加到链表中;
	__linklist *temp = (__linklist*)malloc(sizeof(__linklist));
	temp->cin = cin;
	temp->next= NULL;
	
	head->next = temp;
	return 0;

}

客户端:

#include 

#define N 128
//协议
#define LOGIN 	1
#define CHAT 	2
#define QUIT 	3

#define ERR_LOG(msg) do{\
	printf("%d %s %s \n", __LINE__, __func__, __FILE__);\
	perror(msg);\
}while(0)

//收发信息的结构体
typedef struct
{
	int type;
	char name[20];
	char text[N] ;

}MSG;

typedef void (*sighandler_t)(int);

int do_recv(int sfd);
int do_chat(int sfd, MSG msg, struct sockaddr_in sin);

void handler(int sig)
{
	//回收子进程资源并退出
	while(waitpid(-1,NULL, WNOHANG)>0);
	exit(0);
}

int main(int argc, const char *argv[])
{
	//注册信号处理函数,让子进程退出后,父进程回收子进程资源并退出
	sighandler_t s = signal(SIGCHLD, handler);
	if(s == SIG_ERR)
	{
		ERR_LOG("signal");
		return -1;
	}

	//1.创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		perror("socket");
		return -1;
	}

	//2.填充服务器ip和端口号
	struct sockaddr_in sin;
	sin.sin_family 		= AF_INET;
	sin.sin_port 		= htons(6666);
	sin.sin_addr.s_addr = inet_addr("127.0.0.1");
	socklen_t addrlen = sizeof(sin);


	//登录协议
	MSG msg ;
	msg.type = htonl(LOGIN);

	printf("请输入姓名>>>");
	fgets(msg.name, 20, stdin);
	msg.name[strlen(msg.name)-1] = 0;

	//发送登录信息sendto
	if(sendto(sfd, &msg, sizeof(msg), 0, (void*)&sin, sizeof(sin)) < 0)
	{
		ERR_LOG("sendto");	
		return -1;
	}

	pid_t pid = fork();

	if(pid > 0)
	{
		//父进程获取信息
		do_recv(sfd);
	}
	else if(0 == pid)
	{
		//子进程发送信息
		do_chat(sfd, msg, sin);
	}


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


int do_chat(int sfd, MSG msg, struct sockaddr_in sin)
{
	while(1)
	{
		//从终端获取聊天信息
		bzero(msg.text, N);
		fgets(msg.text, N ,stdin);
		msg.text[strlen(msg.text)-1] = 0;

		//如果是聊天信息,则封装上 CHAT协议 
		//如果是退出信息,则封装上 QUIT协议
		if(strncasecmp(msg.text, "quit" , 4) == 0)
		{
			msg.type = htonl(QUIT);
		}
		else
		{
			msg.type = htonl(CHAT);
		}

		//发送 
		if(sendto(sfd, &msg, sizeof(msg), 0, (void*)&sin, sizeof(sin)) < 0)
		{
			ERR_LOG("sendto");
			return -1;
		}

		//如果是退出信息,则客户端要退出
		//子进程:即当前进程,需要退出
		//且父进程也要退出
		if(msg.type == htonl(QUIT))
		{
			exit(0); 		//退出子进程;
		}
	}
}



int do_recv(int sfd)
{
	MSG rcv_msg ;
	while(1)
	{
		if(recvfrom(sfd, &rcv_msg, sizeof(rcv_msg), 0, NULL, NULL) < 0)
		{
			perror("recvfrom");
			return -1;
		}
		printf("%s\n",rcv_msg.text);
	}
}

你可能感兴趣的:(网络,学习)