5.26 基于UDP的网络聊天室

需求:

如果有人发送消息,其他用户可以收到这个人的群聊信息

如果有人下线,其他用户可以收到这个人的下线信息

服务器可以发送系统信息实现模型

模型:

代码:

//chatser.c -- 服务器端实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdbool.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <unistd.h>
#include <poll.h>

#define SER_IP "192.168.2.25"
#define SER_PORT 8888

char tm[64];

typedef enum msgType{
	LOGIN,
	LOGOUT,
	CHAT,
}msgType;

typedef struct usrMsg{
	msgType type;
	char usrName[32];
	char text[1024];
}usrMsg;

typedef struct cliNode{
	char usrName[32];
	struct sockaddr_in cin;
	struct cliNode* next;
}cnode_t, *pcnode_t;

typedef struct cliHead{
	unsigned curUsrNum;
	pcnode_t next;
}chead_t, *pchead_t;


const char* getTime();
void createNode(pcnode_t* p, const char* name, const struct sockaddr_in* pcin);
void clinkInit(pchead_t* h);
bool isEmpty(const pchead_t h);
void clinkHeadInsert(pchead_t h, pcnode_t p);
void clinkRemoveByVal(pchead_t h, const struct sockaddr_in* pcin);
void clinkDestory(pchead_t* h);
void printErr(const char* s);

int main(int argc, const char *argv[])
{
	pchead_t h = NULL;
	clinkInit(&h);




	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(-1 == sfd){
		printErr("socket");
		return 1;
	}
	printf("[%s]main: Socket created(sfd = %d)\n", getTime(), sfd);
	
	struct sockaddr_in sin = {
		.sin_family = AF_INET,
		.sin_port = htons(SER_PORT),
		.sin_addr = { inet_addr(SER_IP) },
	};
	if(-1 == bind(sfd, (struct sockaddr*)&sin, sizeof(sin))){
		printErr("bind");
		return 1;
	}
	printf("[%s]main: Success to bind(IP:%s PORT:%hu)\n", getTime(),
			SER_IP, SER_PORT);
	
	struct pollfd pfd[2] = {
		{ STDIN_FILENO, POLLIN },
		{ sfd, POLLIN },
	};


	struct sockaddr_in cin;
	socklen_t cin_len = sizeof(cin);


	char buf[2048] = {};
	while(1){
		int ret = poll(pfd, 2, -1);
		if(-1 == ret){
			printErr("poll");
			return 1;
		}
		else if(0 == ret){
			fprintf(stderr, "[%s]poll: Time out\n", getTime());
			return 1;
		}
		
		if(POLLIN == pfd[0].revents){
			char input[512] = {};
			bzero(buf, sizeof(buf));
			sprintf(buf, "[%s]System:", getTime());
			fgets(input, sizeof(input), stdin);
			if(!strcmp(input, "quit\n")){
				printf("[%s]System: Start to shutdown...\n", getTime());
				clinkDestory(&h);	
				break;
			}

			if(isEmpty(h)){
				printf("[%s]System: No user online now\n", getTime());
				continue;
			}
			else{
				strcat(buf, input);
				pcnode_t tmp = h->next;
				while(tmp){
					sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));
					tmp = tmp->next;
				}
				printf("[%s]System: send msg: %s", getTime(), input);
			}
		}
		
		if(POLLIN == pfd[1].revents){
			bzero(buf, sizeof(buf));
			struct usrMsg msg = {};
			recvfrom(sfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cin, &cin_len);
			switch(msg.type){
			case LOGIN:
				pcnode_t p = NULL;
				createNode(&p, msg.usrName, &cin);
				printf("[%s] %s:%hu has connected\n", getTime(), inet_ntoa(cin.sin_addr),
						ntohs(cin.sin_port));
				if(!isEmpty(h)){
					bzero(buf, sizeof(buf));
					sprintf(buf, "[%s] %s is online now!\n", getTime(), msg.usrName);
					pcnode_t tmp = h->next;
					while(tmp){
						sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));
						tmp = tmp->next;
					}
				}
				clinkHeadInsert(h, p);
				break;
			case LOGOUT:
				printf("[%s] %s:%hu has disconnected\n", getTime(), inet_ntoa(cin.sin_addr),
						ntohs(cin.sin_port));
				if(!isEmpty(h)){
					bzero(buf, sizeof(buf));
					sprintf(buf, "[%s] %s is offline\n", getTime(), msg.usrName);
					pcnode_t tmp = h->next;
					while(tmp){
						sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));
						tmp = tmp->next;
					}
				}
				clinkRemoveByVal(h, &cin);
				break;
			case CHAT:
				printf("[%s] %s:%hu is chatting\n", getTime(), inet_ntoa(cin.sin_addr),
						ntohs(cin.sin_port));
				sprintf(buf, "[%s] %s:\n%s\n", getTime(), msg.usrName, msg.text);
				pcnode_t tmp = h->next;
				while(tmp){
					const char* ip_1 = inet_ntoa(tmp->cin.sin_addr);
					const char* ip_2 = inet_ntoa(cin.sin_addr);
					if(!strcmp(ip_1, ip_2) && tmp->cin.sin_port == cin.sin_port){
						tmp = tmp->next;
						continue;
					}
					sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&tmp->cin, sizeof(tmp->cin));
					tmp = tmp->next;
				}
				break;
			default:
				fprintf(stderr, "[%s]System: Unknown msg type\n", getTime());
				break;
			}
		}

	}

	close(sfd);
	return 0;
}



void printErr(const char* s){
	fprintf(stderr, "[%s]", getTime());
	perror(s);
}



const char* getTime(){
	bzero(tm, sizeof(tm));
	time_t sec;
	time(&sec);
	struct tm* loc_tm = localtime(&sec);
	const char* fmt = "%Y-%m-%d %H:%M:%S";
	strftime(tm, sizeof(tm), fmt, loc_tm);
	return tm;
}

void createNode(pcnode_t* p, const char* name, const struct sockaddr_in* pcin){
	if(NULL == p || NULL == name || NULL == pcin){
		fprintf(stderr, "[%s]createNode: Invalid argument\n", getTime());
		return;
	}
	*p = (pcnode_t)malloc(sizeof(cnode_t));
	if(NULL == *p){
		fprintf(stderr, "[%s]createNode: Fail to malloc\n", getTime());
		return;
	}
	else{
		strcpy((*p)->usrName, name);
		(*p)->cin = *pcin;
		(*p)->next = NULL;
		return;
	}
};

void clinkInit(pchead_t* h){
	if(NULL == h){
		fprintf(stderr, "[%s]clinkInit: Invalid argument\n", getTime());
		return;
	}
	*h = (pchead_t)malloc(sizeof(chead_t));
	if(NULL == *h){
		fprintf(stderr, "[%s]clinkInit: Fail to malloc\n", getTime());
		return;
	}
	else{
		(*h)->curUsrNum = 0;
		(*h)->next = NULL;
		return;
	}
}

bool isEmpty(const pchead_t h){
	if(NULL == h){
		fprintf(stderr, "[%s]isEmpty: Invalid argument\n", getTime());
		return false;
	}
	if(0 == h->curUsrNum)
		return true;
	else
		return false;
}

void clinkHeadInsert(pchead_t h, pcnode_t p){
	if(NULL == h || NULL == p){
		fprintf(stderr, "[%s]clinkHeadInsert: Invalid argument\n", getTime());
		return;
	}
	else{
		p->next = h->next;
		h->next = p;
		h->curUsrNum++;
		return;
	}
}

void clinkRemoveByVal(pchead_t h, const struct sockaddr_in* pcin){
	if(NULL == h || NULL == pcin){
		fprintf(stderr, "[%s]clinkRemoveByVal: Invalid argument\n", getTime());
		return;
	}
	else if(isEmpty(h))
		return;
	else{
		void* i = h;
		pcnode_t j = h->next;
		while(j){
			const char* ip_1 = inet_ntoa(j->cin.sin_addr);
			const char* ip_2 = inet_ntoa(pcin->sin_addr);
			if(!strcmp(ip_1, ip_2) && j->cin.sin_port == pcin->sin_port){
				pcnode_t tmp = NULL;
				if(i == h)
					((pchead_t)i)->next = j->next;
				else
					((pcnode_t)i)->next = j->next;
				tmp = j;
				j = tmp->next;
				printf("[%s]clinkRemoveByVal: Cleaning the node[%s:%hu]...\n", getTime(),
					inet_ntoa(tmp->cin.sin_addr), ntohs(tmp->cin.sin_port));
				free(tmp);
				h->curUsrNum--;
			}
			else{
				i = j;
				j = j ->next;
			}
		}
	}
}

void clinkDestory(pchead_t* h){
	if(NULL == h || NULL == *h){
		fprintf(stderr, "[%s]clinkDestory: Invalid argument\n", getTime());
		return;
	}
	else{
		pcnode_t tmp = (*h)->next;
		while(tmp){
			printf("[%s]clinkDestory: Cleaning the node[%s:%hu]...\n", getTime(),
					inet_ntoa(tmp->cin.sin_addr), ntohs(tmp->cin.sin_port));
			(*h)->next = tmp->next;
			free(tmp);
			tmp = (*h)->next;
		}
		free(*h);
		printf("[%s]clinkDestory: Cleaning the head...\n", getTime());
		*h = NULL;
		return;
	}
}
//charcli.c -- 客户端实现
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <poll.h>
#include <sys/socket.h>

#define SER_IP "192.168.2.25"
#define SER_PORT 8888
#define CLI_IP "192.168.2.82"

char tm[64];

typedef enum msgType{
	LOGIN,
	LOGOUT,
	CHAT,
}msgType;

typedef struct usrMsg{
	msgType type;
	char usrName[32];
	char text[1024];
}usrMsg;

const char* getTime();
void printErr(const char* s);

int main(int argc, const char *argv[])
{

	char usrName[32] = {};
	puts("Please enter your name: ");
	fgets(usrName, sizeof(usrName), stdin);
	usrName[strlen(usrName) - 1] = '\0';
	printf("Welcome, %s!\n", usrName);

	int cfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(-1 == cfd){
		printErr("socket");
		return 1;
	}
	printf("[%s]main: Socket created(cfd = %d)\n", getTime(), cfd);

	struct sockaddr_in sin = {
		.sin_family = AF_INET,
		.sin_port = htons(SER_PORT),
		.sin_addr = { inet_addr(SER_IP) },
	};

	struct pollfd pfd[2] = {
		{STDIN_FILENO, POLLIN},
		{cfd, POLLIN},
	};
	

	struct usrMsg msg = {};

	msg.type = LOGIN;
	strcpy(msg.usrName, usrName);
	sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));
	printf("[%s]main: Start to login...\n", getTime());
	while(1){
		int ret = poll(pfd, 2, -1);
		if(-1 == ret){
			printErr("poll");
			return 1;
		}
		else if(0 == ret){
			fprintf(stderr, "[%s]poll: Time out\n", getTime());
			return 1;
		}

		if(POLLIN == pfd[0].revents){
			bzero(msg.text, sizeof(msg.text));
			fgets(msg.text, sizeof(msg.text), stdin);
			msg.text[strlen(msg.text) - 1] = '\0';
			if(!strcmp(msg.text, "quit")){
				msg.type = LOGOUT;
				sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));
				break;
			}

			msg.type = CHAT;
			sendto(cfd, &msg, sizeof(msg), 0, (struct sockaddr*)&sin, sizeof(sin));
		}
		if(POLLIN == pfd[1].revents){
			char buf[2048] = {};
			recv(cfd, buf, sizeof(buf), 0);
			printf("%s", buf);
		}
	}

	close(cfd);

	return 0;
}

void printErr(const char* s){
	fprintf(stderr, "[%s]", getTime());
	perror(s);
}
const char* getTime(){
	bzero(tm, sizeof(tm));
	time_t sec;
	time(&sec);
	struct tm* loc_tm = localtime(&sec);
	const char* fmt = "%Y-%m-%d %H:%M:%S";
	strftime(tm, sizeof(tm), fmt, loc_tm);
	return tm;
}

效果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/653991.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FastAPI - 组织模块2

FastAPI没有强制指定某种格式来组织项目结构&#xff0c;开发者可以根据自己喜好和项目需要来定制自己的项目结构。 https://fastapi.tiangolo.com/zh/tutorial/bigger-applications/ 在项目根目录创建python包routers&#xff0c;然后创建member.py文件 member.py文件内容 …

互联网应用主流框架整合之AOP

SpringAOP基本概念和约定流程 在实际的开发中有些内容并不是面向对象能够解决的&#xff0c;比如数据库事务&#xff0c;对于企业级应用非常重要&#xff0c;比如在电商系统中订单模块和交易模块&#xff0c;一边是交易记录一边是账户数据&#xff0c;就需要对这两者有统一的事…

权限维持--linux

隐藏文件/夹&-开头文件 如何创建: 在文件名之前加.即可 touch .1.s 如何清除、查找&#xff1a; ls -al rm -fr -文件 已-开头的文件直接读取是不行的需要带目录 隐藏时间戳 ①用其他文件的时间 touch -r zww.php testq.txt 如何清除、查看&#xff1a; stat test…

大模型基础知识

文章目录 1. 位置编码1.1 绝对位置编码1.2 相对位置编码1.3 旋转位置编码2. 注意力机制2.1 MHA(muti head attention)2.2 MQA(muti query attention)2.3 GQA(grouped query attention)3. 大模型分类4. 微调方法4.1 Prompt Tuning4.2 Prefix Tuning4.3 Lora4.4 QLora5. La…

探索机器人智能设备:开启智慧生活新篇章

机器人智能设备作为科技创新的代表&#xff0c;正以其独特的魅力吸引着越来越多的关注。它们不仅具备高度的智能化和自主化能力&#xff0c;还能在各种场景下发挥出强大的功能。 机器人智能设备的张总说&#xff1a;在智能家居领域&#xff0c;机器人智能设备可以帮助我们实现家…

改进rust代码的35种具体方法-类型(十九)-避免使用反射

上一篇文章 从其他语言来到Rust的程序员通常习惯于将反思作为工具箱中的工具。他们可能会浪费很多时间试图在Rust中实现基于反射的设计&#xff0c;却发现他们所尝试的事情只能做得不好&#xff0c;如果有的话。这个项目希望通过描述Rust在反思方面做什么和不做什么&#xff0c…

2024年【西式面点师(中级)】新版试题及西式面点师(中级)考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年【西式面点师&#xff08;中级&#xff09;】新版试题及西式面点师&#xff08;中级&#xff09;考试试卷&#xff0c;包含西式面点师&#xff08;中级&#xff09;新版试题答案和解析及西式面点师&#xff08;…

手动安装maven依赖到本地仓库

使用mvn install命令安装jar包到指定的仓库。 命令如下&#xff1a; mvn install:install-file -Dmaven.repo.localC:\Users\liyong.m2\repository -DgroupIdcom.aspose -DartifactIdwords -Dversion18.4 -Dpackagingjar -DfileC:\Users\liyong\Desktop\jar\words-18.4.jar 解释…

JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(MES与ERP系统的区别和联系)

MES与ERP系统的区别和联系 MES制造执行系统&#xff0c;是一套面向制造公司车间执行层的生产信息化管理系统。MES 可觉得公司提供涉及制造数据管理、计划排产管理、生产调度管理、库存管理、质量管理、人力资源管理、工作中心、设备管理、工具工装管理、采购管理、成本管理、项…

谷歌忙于手动删除自己搜索引擎中奇怪的人工智能答案

该公司确认正在“迅速采取行动”消除人工智能工具的一些奇怪反应。 社交媒体上充斥着谷歌新的人工智能概述产品的例子&#xff0c;这些产品说了一些奇怪的话&#xff0c;从告诉用户在披萨上涂胶水到建议他们吃石头。混乱的推出意味着&#xff0c;随着各种表情包的发布&#xf…

DNS 解析过程

文章目录 简介特点查询方式⚡️1. 浏览器缓存2. 系统缓存&#xff08;hosts文件&#xff09;3. 路由器缓存4. 本地域名服务器5. 根域名服务器6. 顶级域名服务器7. 权限域名服务器8. 本地域名服务器缓存并返回9. 操作系统缓存并返回10. 浏览器缓存并访问流程图 总结 简介 DNS&a…

双向带头链表实现

目录 一. 逻辑结构图解 1. 节点中存储的值 2.逻辑实现 二. 各种功能实现 1. 创建节点函数 2. 初始化哨兵位 3. 尾插 4. 头插 5. 尾删 6. 头删 7. 打印链表值 8. 查找数据&#xff0c;返回节点地址 9. 指定地址后插入节点 10. 删除指定地址节点 11. 销毁链表 三.…

vue实现附件下载 获取到接口的图片,设置宽度100%样式

一、vue实现附件下载&#xff0c;使用a链接 <a class"btn flex_center" target"_blank" :href"localImgSrc(goodsDetail.attachment)" :download"localImgSrc(goodsDetail.attachment)" >立即下载 </a>二、 获取到接口…

七大获取免费https的方式

想要实现https访问最简单有效的的方法就是安装SSL证书。只要证书正常安装上以后&#xff0c;浏览器就不会出现网站不安全提示或者访问被拦截的情况。下面我来教大家怎么去获取免费的SSL证书&#xff0c;又如何安装证书实现https访问。 一、选择免费SSL证书提供商 有多家机构提…

vue3(一):Vue3简介、创建vue3工程、Vue3中的响应式

目录 一.Vue3简介 1.性能提升 2.源码升级 3.拥抱ts 4.新特性 &#xff08;1&#xff09;Composition API&#xff08;组合API&#xff09;&#xff1a; &#xff08;2&#xff09;新的内置组件&#xff1a; &#xff08;3&#xff09;其他改变&#xff1a; 二.创建vue…

【计算机视觉(4)】

基于Python的OpenCV基础入门——色彩空间转换 色彩空间简介HSV色彩空间GRAY色彩空间色彩空间转换 色彩空间转换代码实现: 色彩空间简介 色彩空间是人们为了表示不同频率的光线的色彩而建立的多种色彩模型。常见的色彩空间有RGB、HSV、HIS、YCrCb、YUV、GRAY&#xff0c;其中最…

BLE蓝牙模块在车联网中的智能开锁、数据监控应用

随着科技的不断发展&#xff0c;车联网已经成为了汽车行业的一个热门话题。在这个领域中&#xff0c;BLE蓝牙模块发挥着重要的作用&#xff0c;特别是在智能开锁和数据监控方面的应用。本文将详细介绍BLE蓝牙模块在这两个方面的应用及其优势。   一、智能开锁   1.车辆远程…

从华为云OBS到AWS云上S3:迁移及相关事项

随着云计算的快速发展&#xff0c;企业越来越倾向于将数据存储和管理移到云端。华为云的对象存储服务&#xff08;OBS&#xff09;和亚马逊云服务&#xff08;AWS&#xff09;上的简单存储服务&#xff08;S3&#xff09;是两个备受欢迎的选择。对于那些考虑从华为云OBS迁移到A…

工商银行异地卡兑换泰铢的流程

本文介绍在国内的工商银行&#xff0c;通过现金或银行卡兑换泰国铢等外国货币的纸币或硬币的方法。 最近&#xff0c;准备到泰国旅行&#xff0c;所以需要兑换一些泰铢&#xff0c;防止下飞机到当地后找不到汇率合适、兑换方便的换钱的地方。其中&#xff0c;因为对比发现工商银…

SQL试题使得每个学生 按照姓名的字⺟顺序依次排列 在对应的⼤洲下⾯

学⽣地理信息报告 学校有来⾃亚洲、欧洲和美洲的学⽣。 表countries 数据如下&#xff1a; namecontinentJaneAmericaPascalEuropeXiAsiaJackAmerica 1、编写解决⽅案实现对⼤洲&#xff08;continent&#xff09;列的 透视表 操作&#xff0c;使得每个学生 按照姓名的字⺟顺…