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;
}
相关推荐
ylscode2 小时前
OpenSSH 10.4 正式发布:后量子加密落地,多项高危漏洞得到修复
网络·安全·网络安全
DianSan_ERP3 小时前
电商架构演进:如何在高并发场景下实现多平台API的标准化履约?
运维·前端·网络·安全·架构·自动化
阿成学长_Cain3 小时前
Linux ipcs 命令超全详解:查看共享内存 / 消息队列 / 信号量,IPC 运维必备
linux·运维·服务器·网络·数据库
青瓦梦滋4 小时前
协议定制/序列化-反序列化(Linux视角)
linux·服务器·网络·c++
驭渊的小故事8 小时前
网络原理01(两千字解析)
网络
我今晚不熬夜9 小时前
Java语言实现Modbus协议通过用串口读取数据(以RTU为例)
java·网络协议
KaMeidebaby9 小时前
卡梅德生物技术快报|小 RNA 适配体合成 + 多方法亲和力表征全流程标准化操作手册
前端·网络·数据库·人工智能·算法
阿成学长_Cain11 小时前
Linux telinit 命令详解:运行级别切换|关机重启|系统维护一站式掌握
linux·运维·前端·网络
skd899912 小时前
万能查任意网络设备IP工具
服务器·网络·tcp/ip
NiceCloud喜云12 小时前
Anthropic 一周三连发:Cowork 多端、Fable 5 按需付费、J-space 论文的技术解读
java·服务器·网络·人工智能·ai