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;
}
相关推荐
千天夜1 小时前
使用UDP协议传输视频流!(分片、缓存)
python·网络协议·udp·视频流
长弓三石2 小时前
鸿蒙网络编程系列44-仓颉版HttpRequest上传文件示例
前端·网络·华为·harmonyos·鸿蒙
xianwu5432 小时前
反向代理模块
linux·开发语言·网络·git
follycat2 小时前
[极客大挑战 2019]HTTP 1
网络·网络协议·http·网络安全
earthzhang20213 小时前
《深入浅出HTTPS》读书笔记(5):随机数
网络协议·http·https
xiaoxiongip6663 小时前
HTTP 和 HTTPS
网络·爬虫·网络协议·tcp/ip·http·https·ip
JaneJiazhao3 小时前
HTTPSOK:SSL/TLS证书自动续期工具
服务器·网络协议·ssl
JaneJiazhao3 小时前
HTTPSOK:智能SSL证书管理的新选择
网络·网络协议·ssl
CXDNW3 小时前
【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
网络·笔记·http·面试·https·http2.0
无所谓จุ๊บ3 小时前
树莓派开发相关知识十 -小试服务器
服务器·网络·树莓派