网络240526 UDP网络聊天室

基于UDP的网络聊天室(2024.5.26号上交)

项目需求:

  1. 如果有用户登录,其他用户可以收到这个人的登录信息
  2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
  3. 如果有人下线,其他用户可以收到这个人的下线信息
  4. 服务器可以发送系统信息

服务器

#include <myhead.h>
 
#define SER_IP "192.168.124.38"
#define SER_PORT 8888
#define RBUFSIZE 128
#define WBUFSIZE 128
 
 
typedef struct node{
	char usrName[20];
	struct sockaddr_in cli_sockaddr;
	struct node* next;
}node, *node_p;
 
 
node_p create_head_node();
node_p create_node(char* usrName, struct sockaddr_in cli_sockaddr);
void insert_tail(node_p H, char* usrName, struct sockaddr_in cli_sockaddr);
char* find(node_p H, struct sockaddr_in cli_sockaddr);
void delete(node_p H, struct sockaddr_in cli_sockaddr);
 
int main(int argc, const char *argv[]){
 
	int sockfd;
	struct sockaddr_in ser_sockaddr;
	struct sockaddr_in cli_sockaddr;
	char wbuf[WBUFSIZE]={0};
	char rbuf[RBUFSIZE]={0};
	socklen_t addrlen = sizeof(struct sockaddr);
	struct pollfd pfds[2];
 
	// 创建头结点
	node_p H = create_head_node();
 
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd == -1){
		perror("socket");
		return -1;
	}
 
	ser_sockaddr.sin_family = AF_INET;
	ser_sockaddr.sin_port = htons(SER_PORT);
	ser_sockaddr.sin_addr.s_addr = inet_addr(SER_IP);
	if(bind(sockfd, (struct sockaddr*)&ser_sockaddr, sizeof(struct sockaddr)) == -1){
		perror("bind");
		return -1;
	}
 
	pfds[0].fd = 0;
	pfds[0].events = POLLIN;
	pfds[1].fd = sockfd;
	pfds[1].events = POLLIN;
 
	puts("Waiting for client connection...");
 
	//开始通信
	while (1) {
		poll(pfds, 2, -1);
 
		// 接受客户端发送的消息,并转发给其他客户端
		if (pfds[1].revents == POLLIN) {
 
			memset(rbuf, 0, sizeof(rbuf));
			recvfrom(sockfd, rbuf, RBUFSIZE, 0, (struct sockaddr*)&cli_sockaddr, &addrlen);
 
			// 判断客户端是否在链表内,即客户端是否已经连接
			char* ret = find(H, cli_sockaddr);
			
			// 用户未存在于链表中
			if (ret == NULL) {
 
				// 在客户端首次连接时,利用链表尾插存储客户端的用户名和地址信息
				insert_tail(H, rbuf, cli_sockaddr);
 
				// 服务器端输出客户端加入消息
				printf("%s [%s:%d] 加入聊天室\n", rbuf, inet_ntoa(cli_sockaddr.sin_addr), ntohs(cli_sockaddr.sin_port));
 
				// 将客户端上线的消息,发送给出了除了刚刚加入的客户端的所有主机
				node_p temp = H->next;
 
				//格式化客户端登录的消息
				char str[1024]={0};
				sprintf(str, "----%s加入聊天室----", rbuf);
				while (temp->next != NULL) {
					if (sendto(sockfd, str, strlen(str), 0, (struct sockaddr*)&temp->cli_sockaddr, sizeof(struct sockaddr)) == -1) {
						perror("sendto");
					}
					temp = temp->next;
				}
			}
			
			// 用户存在于链表中
			else if (ret > 0) {
 
				// 已登录客户端发送了退出信号,通知服务器以及其他所有用户
				if(strcmp(rbuf, "quit") == 0){
					
					// 格式化客户端退出消息
					char str[1024]={0};
					sprintf(str, "----%s退出聊天室----", ret);
					
					// 服务器端输出客户端退出消息
					printf("%s [%s:%d] 退出聊天室\n", ret, inet_ntoa(cli_sockaddr.sin_addr), ntohs(cli_sockaddr.sin_port));
 
					// 通知其他人
					node_p temp = H->next;
					while (temp != NULL) {
						if (sendto(sockfd, str, strlen(str), 0, (struct sockaddr*)&temp->cli_sockaddr, sizeof(struct sockaddr)) == -1) {
							perror("sendto");
						}
						temp = temp->next;
					}
 
					// 将退出的客户端从链表中删除
					delete(H, cli_sockaddr);
				}
				else{
					// 将客户端发送的消息转发给所有人(除了发送消息的客户端)
					// 格式化用户发送的消息
					char str[1024]={0};
					sprintf(str, "%s: %s", ret, rbuf);
					
					node_p temp = H->next;
					while (temp != NULL) {
						if (temp->cli_sockaddr.sin_addr.s_addr == cli_sockaddr.sin_addr.s_addr && temp->cli_sockaddr.sin_port == cli_sockaddr.sin_port){
							temp = temp->next;
							continue;
						}
						if (sendto(sockfd, str, strlen(str), 0, (struct sockaddr*)&temp->cli_sockaddr, sizeof(struct sockaddr)) == -1) {
							perror("sendto");
						}
						temp = temp->next;
					}
					printf("%s [%s:%d] 发送了一条消息\n", ret, inet_ntoa(cli_sockaddr.sin_addr), ntohs(cli_sockaddr.sin_port));
				}
			}
		}
 
		// 服务器向所有客户端发送消息
		if (pfds[0].revents == POLLIN) {
 
			memset(wbuf, 0, sizeof(wbuf));
			fgets(wbuf, sizeof(wbuf), stdin);
			wbuf[strlen(wbuf)-1] = 0;
			
			// 格式化服务器发送的消息
			char str[1024]={0};
			sprintf(str, "server: %s", wbuf);
			
			node_p temp = H->next;
			while (temp != NULL) {
				if (sendto(sockfd, str, strlen(str), 0, (struct sockaddr*)&temp->cli_sockaddr, sizeof(struct sockaddr)) == -1) {
					perror("sendto");
				}
				temp = temp->next;
			}
			printf("服务器 [%s:%d] 发送了一条消息\n",inet_ntoa(ser_sockaddr.sin_addr), ntohs(ser_sockaddr.sin_port));
		}
	}
	//关闭socket
	close(sockfd);
	return 0;
}
 
node_p create_head_node(){                                    
	node_p H=(node_p)malloc(sizeof(node));
	if(H==NULL){
		printf("空间申请失败\n");
		return NULL;
	}
 
	H->next=NULL;
	return H;
}
 
 
node_p create_node(char* usrName, struct sockaddr_in cli_sockaddr){
	node_p p=(node_p)malloc(sizeof(node));
	if(p==NULL){
		printf("空间申请失败\n");
		return NULL;
	}
	strcpy(p->usrName, usrName);
	p->cli_sockaddr = cli_sockaddr;
	return p;
}
 
 
void insert_tail(node_p H, char* usrName, struct sockaddr_in cli_sockaddr){
	if(H==NULL){
		printf("入参为空\n");
		return;
	}
	
	node_p p=H;
	while(p->next!=NULL){
		p=p->next;
	}
	node_p new = create_node(usrName, cli_sockaddr);
 
	new->next=p->next;
	p->next=new;
}
 
/*   
  @brief  判断客户端信息是否已经保存在链表内
  @param  H 头结点
  		  cli_sockaddr 待判断的客户端信息
  @retval NULL 客户端信息不在链表内
  		  非空 客户端的用户名字符串指针
*/
 
char* find(node_p H, struct sockaddr_in cli_sockaddr){
	if (H == NULL){
		printf("入参为空\n");
		return NULL;
	}
	if (H->next == NULL){
		// 链表为空
		return NULL;
	}
	node_p temp = H->next;
	while (temp != NULL){
		if (temp->cli_sockaddr.sin_addr.s_addr == cli_sockaddr.sin_addr.s_addr && temp->cli_sockaddr.sin_port == cli_sockaddr.sin_port){
			return temp->usrName;
		}
		temp = temp->next; 
	}
	return NULL;
}
 
/*   
  @brief  删除链表中指定的客户端信息
  @param  H 头结点
  		  cli_sockaddr 待删除的客户端信息
  @retval 无
*/
 
void delete(node_p H, struct sockaddr_in cli_sockaddr){
	if (H == NULL){
		printf("入参为空\n");
		return;
	}
 
	node_p temp = H;
	while (temp->next->cli_sockaddr.sin_addr.s_addr != cli_sockaddr.sin_addr.s_addr || \
			temp->next->cli_sockaddr.sin_port != cli_sockaddr.sin_port)temp = temp->next;
	node_p p = temp->next;
	temp->next = temp->next->next;
	free(p);
}

客户端

#include <myhead.h>
 
#define SER_PORT 8888
#define SER_IP "192.168.124.38"
#define CLI_PORT 6666
#define CLI_IP "192.168.125.117"
#define WBUFSIZE 128
#define RBUFSIZE 128
 
int main(int argc, const char *argv[])
{
	int sockfd;
	char wbuf[WBUFSIZE] ={0};
	char rbuf[RBUFSIZE] ={0};
	struct sockaddr_in ser_sockaddr;
	struct pollfd pfds[2];
 
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd == -1)
	{
		perror("socket");
		return -1;
	}
 
	ser_sockaddr.sin_family = AF_INET;
	ser_sockaddr.sin_port = htons(SER_PORT);
	ser_sockaddr.sin_addr.s_addr = inet_addr(SER_IP);
 
	pfds[0].fd = 0;
	pfds[0].events = POLLIN;
	pfds[1].fd = sockfd;
	pfds[1].events = POLLIN;
 
	printf("请输入聊天昵称->");
	fflush(stdout);
 
	//开始通信
	while(1)
	{
		// 通信缓冲区
		memset(wbuf, 0, sizeof(wbuf));
		memset(rbuf, 0, sizeof(rbuf));
 
		// 阻塞等待事件发生
		poll(pfds, 2, -1);
 
		// 向服务器端发送消息
		if(pfds[0].revents == POLLIN){
			fgets(wbuf, sizeof(wbuf), stdin);
			wbuf[strlen(wbuf)-1] = 0;
			sendto(sockfd, wbuf, strlen(wbuf), 0, (struct sockaddr*)&ser_sockaddr, sizeof(struct sockaddr));
			if(strcmp(wbuf, "quit") == 0){
				close(sockfd);
				return 0;
			}
		}
 
		// 接收服务器消息
		if(pfds[1].revents == POLLIN){
			recvfrom(sockfd, rbuf, sizeof(rbuf), 0,NULL, NULL);
			printf("%s\n", rbuf);
		}
	}
 
	return 0;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值