UDP聊天室

服务器端

#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;
}
相关推荐
网络研究院2 小时前
如何安全地大规模部署 GenAI 应用程序
网络·人工智能·安全·ai·部署·观点
limengshi1383923 小时前
通信工程学习:什么是RIP路由信息协议
网络·网络协议·学习·智能路由器·信息与通信
GodK7773 小时前
HTTPS 的加密流程
网络协议·http·https
limengshi1383927 小时前
通信工程学习:什么是TFTP简单文件传输协议
网络·网络协议·学习·信息与通信
麻辣韭菜8 小时前
网络基础 【HTTP】
网络·c++·http
Deryck_德瑞克10 小时前
Java网络通信—TCP
java·网络·tcp/ip
GodK77710 小时前
IP 数据包分包组包
服务器·网络·tcp/ip
梁诚斌10 小时前
VSOMEIP代码阅读整理(1) - 网卡状态监听
运维·服务器·网络
ZachOn1y11 小时前
计算机网络:计算机网络概述 —— 描述计算机网络的参数
网络·tcp/ip·计算机网络·考研必备
我命由我1234511 小时前
SSL 协议(HTTPS 协议的关键)
网络·经验分享·笔记·学习·https·ssl·学习方法