5.26 基于UDP的网络聊天室

news2024/11/28 4:30:23

需求:

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

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

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

模型:

代码:

//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;
}

效果:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

php TP8 阿里云短信服务SDKV 2.0(跳大坑)

安装&#xff1a;composer require alibabacloud/dysmsapi-20170525 2.0.24 官方文档&#xff1a;短信服务_SDK中心-阿里云OpenAPI开发者门户 (aliyun.com) 特别注意&#xff1a;传入参数获得值形式 这样也不行 $sendSmsRequest new SendSmsRequest($addData); 还有一个大坑…

PLC远程调试

随着工业自动化的快速发展&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;已经成为现代工业生产线的核心控制设备。然而&#xff0c;传统的PLC调试方式往往受限于地理位置和物理连接&#xff0c;使得工程师在调试过程中面临诸多不便。在这个背景下&#xff0c;HiWoo …

SpringSecurity登录和校验流程简述

认证&#xff1a; 验证当前访问系统的是不是本系统的用户&#xff0c;并且要确认具体是哪个用户 授权&#xff1a; 经过认证后判断当前用户是否有权限进行某个操作 一、入门案例实现 搭建springboot工程后&#xff0c;创建启动类和Controller&#xff0c;引入SpringSecurity依…

C++模板——非类型模板参数、模板的特化以及模板的分离编译

目录 非类型模板参数 模板的特化 概念 函数模板特化 类模板特化 全特化 偏特化 模板的分离编译 什么是分离编译 模板的分离编译 解决方法 模板总结 非类型模板参数 模板参数可分为类型形参和非类型形参。类型形参&#xff1a; 出现在模板参数列表中&#xff0c;跟…

垂类短视频:四川鑫悦里文化传媒有限公司

垂类短视频&#xff1a;内容细分下的新媒体力量 随着移动互联网的迅猛发展和智能手机的普及&#xff0c;短视频已成为当下最受欢迎的媒介形式之一。四川鑫悦里文化传媒有限公司而在短视频领域&#xff0c;一个新兴的概念——“垂类短视频”正逐渐崭露头角&#xff0c;以其独特…

【教程】利用API接口添加本站同款【每日新闻早早报】-每天自动更新,不占用文章数量

本次分享的是给网站添加一个每日早报的文章&#xff0c;可以看到本站置顶上面还有一个日更的日报&#xff0c;这是利用ALAPI的接口完成的&#xff01;利用接口有利也有弊&#xff0c;因为每次用户访问网站的时候就会增加一次API接口请求&#xff0c;导致文章的请求会因为请求量…

nodejs中使用ffmpeg零基础教程(electron+vue3)

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、多方案对比二、ffmpeg各插件简介三、使用ffmpeg-static插件四、使用fluent-ffmpeg插件五、如果使用ai&#xff0c;可能会踩的坑5.1第一个坑5.2第二个坑5.3第三个坑 总结 前言 最近想要把自己写的一些知识点&#xff…

二十九、openlayers官网示例DeclutterGroup解析——避免矢量图层的文字重叠

官网demo地址&#xff1a; Declutter Group 这篇说的是如何设置矢量图层上多数据点文字不重叠。 主要是属性declutter &#xff0c;用于处理矢量图层上重叠的标注和符号&#xff0c;为true时启用去重叠功能。所有矢量特征的标注和符号都会被处理以避免重叠。false则与之相反。…

java nio FileChannel堆内堆外数据读写全流程分析及使用(附详细流程图)

这里是小奏,觉得文章不错可以关注公众号小奏技术 背景 java nio中文件读写不管是普通文件读写&#xff0c;还是基于mmap实现零拷贝&#xff0c;都离不开FileChannel这个类。 随便打开RocketMQ 源码搜索FileChannel 就可以看到使用频率 kafka也是 所以在java中文件读写FileCh…

hexo静态博客 部署到xxx.github.io github 静态页

hexo安装 npm install hexo-cli -g hexo init blog cd blog npm install hexo server key配置 ssh-keygen -t ed25519 -C “emaile.com” 添加key到github err gitgithub.com: Permission denied (publickey). fatal: Could not read from remote repository. 配置GitHub仓…

LabVIEW通过以太网控制PLC程序开发

在使用LabVIEW通过以太网控制PLC程序开发时&#xff0c;需要综合考虑硬件、软件和通信协议的协调工作。以下是详细步骤、注意事项、重点和难点分析&#xff0c;以及几种实现方式及其特点的概述。 实现步骤 确定硬件和软件环境&#xff1a; 确定PLC型号和品牌&#xff08;如西门…

民国漫画杂志《时代漫画》第29期.PDF

时代漫画29.PDF: https://url03.ctfile.com/f/1779803-1248635405-bf3c87?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了&#xff0c;截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

互联网的利

在互联网没发明之前&#xff0c;人类说话要近距离的说&#xff0c;玩游戏要近距离的玩&#xff0c;十分麻烦。于是&#xff0c;互联网解决了这个问题。聊天可以在电脑上聊&#xff0c;玩游戏可以用游戏软件查找玩家来玩&#xff0c;实现了时时可聊&#xff0c;时时可玩的生活。…

Euler 欧拉系统介绍

Euler 欧拉系统介绍 1 简介重要节点与版本EulerOS 特色EulerOS 与 openEuler 区别联系Euler 与 HarmonyOS 区别联系 2 openEuler特色支持 ARM&#xff0c;x86&#xff0c;RISC-V 等全部主流通用计算架构融入 AI 生态嵌入式实时能力提升引入 OpenHarmony 一些突出功能 参考 1 简…

基于51单片机简易温度计

一.硬件方案 本系统利用51单片机控制温度传感器DS18B20进行温度的实时检测并显示&#xff0c;能够实现快速测量环境温度。硬件以微控制器为核心&#xff0c;外接时钟电路、复位电路、温度测量电路、LED显示电路组成。 二.设计功能 &#xff08;1&#xff09;采用DS18B20温度…

python使用多种方法计算列表元素平方的技巧

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、使用列表推导式进行元素平方 二、使用map函数进行元素平方 三、循环遍历列表进行元素平…

HTML 页面布局

慢慢生活&#xff0c;慢慢变好 —— 24.5.28 页面布局 盒子: 页面中所有的元素(标签)&#xff0c;都可以看做是一个盒子&#xff0c;由盒子将页面中的元素包含在一个矩形区域内&#xff0c;通过盒子的视角更方便的进行页面布局 盒子模型组成: 内容区域(content)、内边距区域(pa…

大模型时代的具身智能系列专题(四)

google deepmind团队 谷歌旗下最大的两个 AI 研究机构——地处伦敦 DeepMind 与位于硅谷的 Google Brain 合并成立新部门 Google DeepMind。其将机器学习和系统神经科学的最先进技术结合起来&#xff0c;建立强大的通用学习算法。代表作有AlphaGo&#xff0c;AlphaStar&#x…

Vanna使用ollama分析本地MySQL数据库

上一章节中已经实现了vanna的本地运行&#xff0c;但是大模型和数据库都还是远程的&#xff0c;因为也就没办法去训练&#xff0c;这节一起来实现vanna分析本地mysql数据库&#xff0c;因为要使用本地大模型&#xff0c;所以开始之前需要给本地安装好大模型&#xff0c;我这里用…

Android性能优化方案

1.启动优化&#xff1a; application中不要做大量耗时操作,如果必须的话&#xff0c;建议异步做耗时操作2.布局优化&#xff1a;使用合理的控件选择&#xff0c;少嵌套。&#xff08;合理使用include,merge,viewStub等使用&#xff09;3.apk优化&#xff08;资源文件优化&#…