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;
}
相关推荐
千羽星弦7 分钟前
Kubernetes 集群搭建(一):k8s 从环境准备到 Calico 网络插件部署(1.16版本)
网络·容器·kubernetes
Lorin 洛林25 分钟前
修复SSL证书链不完整问题certificate verify failed unable to get local issuer certificate
数据库·网络协议·ssl
微风轻吟挽歌31 分钟前
HTTP 308 错误永久重定向 (Permanent Redirect)
网络·网络协议·http
Double Point1 小时前
(三十七)Dart 中使用 Pub 包管理系统与 HTTP 请求教程
网络·网络协议·http
Aa美少女战士3 小时前
企业如何选择通配符 SSL 证书?
网络协议·https·ssl
写代码的小王吧8 小时前
【安全】Web渗透测试(全流程)_渗透测试学习流程图
linux·前端·网络·学习·安全·网络安全·ssh
GalaxyPokemon9 小时前
Muduo网络库实现 [七] - Connection模块
linux·服务器·网络
sniper_fandc10 小时前
网络编程—Socket套接字(TCP)
网络·tcp/ip·javaee
the_nov10 小时前
19.TCP相关实验
linux·服务器·网络·c++·tcp/ip
林中伊人11 小时前
家庭路由器wifi设置LAN2LAN和LAN2WAN
网络·路由器