UDP简单搭建网络聊天室

1.客户端

#include<myhead.h>

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

#define LOGI128 	1
#define CHAT 	2
#define QUIT 	3

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

}MSG;

typedef void (*sighandler_t)(int);

int cli_recv(int sfd);
int cli_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("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(1314);
	sin.sin_addr.s_addr = inet_addr("192.168.114.12");
	socklen_t addrlen = sizeof(sin);


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

	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("sendto");	
		return -1;
	}

	pid_t pid = fork();

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


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


int cli_chat(int sfd, MSG msg, struct sockaddr_in sin)
{
	while(1)
	{
		//从终端获取聊天信息
		bzero(msg.text, 128);
		fgets(msg.text, 128 ,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("sendto");
			return -1;
		}

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



int cli_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);
	}
}

2.服务器

#include<myhead.h>

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

#define PORT 1314
#define IP "192.168.114.12"

#define LOGIN 1
#define CHAT 2
#define QUIT 3

typedef struct
{
	int type;
	char name[20];
	char text[128];
}msg;

typedef struct Node
{
	struct sockaddr_in cin;
	struct Node* next;
}Node, *NodePtr;

int cli_login(int sfd, NodePtr head, msg rcv_msg, struct sockaddr_in cin);
int cli_quit(int sfd, NodePtr head, msg rcv_msg, struct sockaddr_in cin);
int cli_chat(int sfd, NodePtr head, msg rcv_msg, struct sockaddr_in cin);

int main(int argc, const char *argv[])
{
	//创建套接字文件
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		ERR("socket()");
		exit(1);
	}

	//填充服务器网络地址信息
	struct sockaddr_in sin = {AF_INET, htons(PORT), .sin_addr.s_addr = inet_addr(IP)};
	
	//绑定套接字文件
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR("bind()");
		exit(1);
	}

	pid_t pid;
	//创建子进程
	pid = fork();
	if(pid < 0)
	{
		ERR("fork()");
		exit(1);
	}
	else if(0 == pid)
	{

		NodePtr head = (NodePtr)malloc(sizeof(Node));
		head->next = NULL;

		//子进程负责接收数据
		msg rcv_msg;
		ssize_t ret = 0;
		struct sockaddr_in cin;
		socklen_t clilen = sizeof(cin);
		while(1)
		{
			ret = recvfrom(sfd, &rcv_msg, sizeof(rcv_msg), 0, (void*)&cin, &clilen);
			if(ret < 0)
			{
				ERR("recvfrom()");
				exit(1);
			}
			
			int type = ntohl(rcv_msg.type);
			switch(type)
			{
			case LOGIN:
				cli_login(sfd, head, rcv_msg, cin);
				break;
			case CHAT:
				cli_chat(sfd, head, rcv_msg, cin);
				break;
			case QUIT:
				cli_quit(sfd, head, rcv_msg, cin);
				break;
			}
		}
	}

	msg sys_msg = {htonl(CHAT), "系统公告"};
	while(1)
	{
		//父进程负责发送系统信息
		bzero(sys_msg.text, 128);
		fgets(sys_msg.text, 128, 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("sendto()");
			exit(1);
		}
	}

	close(sfd);
	return 0;
}

int cli_chat(int sfd, NodePtr head, msg rcv_msg, struct sockaddr_in cin)
{
	char buf[256] = "";
	sprintf(buf, "%s:%s", rcv_msg.name, rcv_msg.text);
	strcpy(rcv_msg.text, buf);
	
	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("sendto()");
				return -1;
			}
		}
	}
	return 0;
}

int cli_login(int sfd, NodePtr 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("sendto()");
			return -1;
		}
	}

	NodePtr temp = (NodePtr)malloc(sizeof(Node));
	temp->cin = cin;
	temp->next = NULL;

	head->next = temp;
	return 0;
}

int cli_quit(int sfd, NodePtr head, msg rcv_msg, struct sockaddr_in cin)
{
	sprintf(rcv_msg.text, "-----%s 已下线-----\n", rcv_msg.name);

	//发送给当前客户端外的其余客户端
	while(head->next != NULL)
	{
		if(memcmp(&cin, &head->next->cin, sizeof(cin)) == 0)
		{
			//从链表中删除该客户端信息
			//free
			NodePtr 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("sendto()");
				return -1;
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值