3.23项目:聊天室

news2024/11/15 13:25:07

1、 基于UDP的网络聊天室

项目需求:

  1. 如果有用户登录,其他用户可以收到这个人的登录信息
  2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
  3. 如果有人下线,其他用户可以收到这个人的下线信息
  4. 服务器可以发送系统信息

服务器

#include <myhead.h>

//链表结构体
typedef struct Node
{
	char name[20];
	struct sockaddr_in cin;
	struct Node *next;   //指针域
}node,*node_p;

//数据结构体
struct msgTyp
{
	char type;
	char usrName[20];
	char msgText[1024];
};

//创建链表头结点
node_p create_head()
{
	node_p L = (node_p)malloc(sizeof(node));
	if(L == NULL)
	{
		printf("空间申请失败\n");
		return NULL;
	}
	L->next = NULL;
	return L;
}

//创建结点
node_p create_node(char *name,struct sockaddr_in cin)
{
	node_p new = (node_p)malloc(sizeof(node));
	if(new == NULL)
	{
		printf("空间申请失败\n");
		return NULL;
	}
	strcpy(new->name,name);
	new->cin = cin;
	return new;
}

//头插
void insert(node_p L,char *name,struct sockaddr_in cin)
{
	if(L == NULL)
	{
		printf("入参为空\n");
		return;
	}
	node_p new = create_node(name,cin);
	new->next = L->next;
	L->next = new;
}

//按姓名删除
void delete(node_p L,char *name)
{
	if(L == NULL)
	{
		printf("入参为空\n");
		return;
	}
	node_p p = L;
	while(p != NULL)
	{
		if(strcmp(p->next->name,name) == 0)
		{
			node_p del = p->next;
			free(del);
			p->next = p->next->next;
			break;
		}
		p = p->next;
	}
}


int main(int argc, const char *argv[])
{
	//定义结构体变量
	node link;
	struct msgTyp msg;

	//创建链表
	node_p L = create_head();

	//创建套接字
	int sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd == -1)
	{
		perror("socket error");
		return -1;
	}

	//判断是否有外部传参
	if(argc < 3)
	{
		printf("请输入IP和端口号\n");
		return -1;
	}

	//端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd,SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1)
    {
        perror("setsockopt error");
        return -1;
    }

	//填写地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(atoi(argv[2]));
	sin.sin_addr.s_addr = inet_addr(argv[1]);

	//绑定IP和端口号
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1)
	{
		perror("bind error");
		return -1;
	}

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

	//定义一个文件描述符集合
	fd_set readfds,tempfds;

	//将集合清空
	FD_ZERO(&readfds);

	//将检测的文件描述符放入集合
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);

	int maxfd = sfd;

	while(1)
	{
		//将readfds备份
		tempfds = readfds;

		int res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
		if(res == -1)
		{
			perror("select error");
			return -1;
		}
		else if(res == 0)
		{
			printf("time out\n");
			return -1;
		}

		link.cin = cin;

		//数据的互通
		if(FD_ISSET(sfd,&tempfds))
		{
			recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&link.cin,&addrlen);
			//登录
			if(msg.type == 'L')
			{
				strcpy(link.name,msg.usrName);
				insert(L,link.name,link.cin);
				char arr[128] = "";
				sprintf(arr,"***%s登录成功***",msg.usrName);
				node_p p1 = L->next;
				while(p1 != NULL)
				{
					sendto(sfd,arr,sizeof(arr),0,(struct sockaddr*)&(p1->cin),sizeof(p1->cin));
					p1 = p1->next;
				}
				printf("%s [%s %d]:登录成功\n",link.name,inet_ntoa(link.cin.sin_addr),ntohs(link.cin.sin_port));
			}
			//聊天
			else if(msg.type == 'C')
			{
				strcpy(link.name,msg.usrName);
				char brr[2048] = "";
				sprintf(brr,"%s:%s",msg.usrName,msg.msgText);
				node_p p2 = L->next;
				//循环遍历
				while(p2 != NULL)
				{
					if(strcmp(p2->name,msg.usrName) != 0)
					{
						sendto(sfd,brr,sizeof(brr),0,(struct sockaddr*)&(p2->cin),sizeof(p2->cin));
					}
					p2 = p2->next;
				}
				printf("%s [%s %d]:chat成功\n",link.name,inet_ntoa(link.cin.sin_addr),ntohs(link.cin.sin_port));
			}
			//退出
			else if(msg.type == 'Q')
			{
				strcpy(link.name,msg.usrName);
				delete(L,link.name);
				char crr[128] = "";
				sprintf(crr,"***%s退出聊天***",msg.usrName);
				node_p p3 = L->next;
				while(p3 != NULL)
				{
					sendto(sfd,crr,sizeof(crr),0,(struct sockaddr*)&(p3->cin),sizeof(p3->cin));
					p3 = p3->next;
				}
				printf("%s [%s %d]:退出聊天\n",link.name,inet_ntoa(link.cin.sin_addr),ntohs(link.cin.sin_port));
			}
		}

		//系统发送信息
		if(FD_ISSET(0,&tempfds))
		{
			char sbuf[1024] = "";
			char buf[512] = "";
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = '\0';
			sprintf(sbuf,"**system**:%s",buf);
			node_p p = L->next;
			while(p != NULL)
			{
				if(sendto(sfd,sbuf,sizeof(sbuf),0,(struct sockaddr*)&(p->cin),sizeof(p->cin)) == -1)
				{
					perror("sendto error");
					return -1;
				}
				p = p->next;
			}
			printf("**system**[%s %d]:chat成功\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port));
		}
	}

	return 0;
}

客户端

#include <myhead.h>

struct msgTyp
{
	char type;  //L  C  Q
	char usrName[20];
	char msgText[1024];
};

int main(int argc, const char *argv[])
{
	struct msgTyp usr;

	int cfd = socket(AF_INET,SOCK_DGRAM,0);
	if(cfd == -1)
	{
		perror("socket error");
		return -1;
	}

	//判断是否写外部参数
	if(argc < 3)
	{
		printf("请输入IP和端口号\n");
		return -1;
	}

	//填写服务器信息
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(atoi(argv[2]));
	sin.sin_addr.s_addr = inet_addr(argv[1]);
	socklen_t addrlen = sizeof(sin);

	//输入姓名
	char name[20] = "";
	printf("请输入姓名>>>>");
	fgets(name,sizeof(name),stdin);
	name[strlen(name)-1] = '\0';

	usr.type = 'L';
	strcpy(usr.usrName,name);
	if(sendto(cfd,&usr,sizeof(usr),0,(struct sockaddr*)&sin,sizeof(sin)) == -1)
	{
		perror("sendto error");
		return -1;
	}

	//定义一个文件描述符,将检测的文件描述符放入集合
	fd_set readfds,tempfds;

	FD_ZERO(&readfds);

	FD_SET(0,&readfds);
	FD_SET(cfd,&readfds);

	int maxfd = cfd;  //记录最大的文件描述符

	while(1)
	{
		tempfds = readfds;

		int res = select(maxfd+1,&tempfds,NULL,NULL,NULL);
		if(res == -1)
		{
			perror("select error");
			return -1;
		}
		
		//收到客户端信息
		if(FD_ISSET(cfd,&tempfds))
		{
			bzero(usr.msgText,sizeof(usr.msgText));
			if(recvfrom(cfd,usr.msgText,sizeof(usr.msgText)-1,0,(struct sockaddr*)&sin,&addrlen) == -1)
			{
				perror("recvfrom error");
				return -1;
			}
			printf("%s\n",usr.msgText);
		}

		//发送数据
		if(FD_ISSET(0,&tempfds))
		{
			bzero(usr.msgText,sizeof(usr.msgText));
			fgets(usr.msgText,sizeof(usr.msgText),stdin);
			usr.msgText[strlen(usr.msgText)-1] = '\0';

			//退出操作
			if(strcmp(usr.msgText,"quit") == 0)
			{
				usr.type = 'Q';
				sendto(cfd,&usr,sizeof(usr),0,(struct sockaddr*)&sin,addrlen);
				close(cfd);
				return 0;
			}
			//聊天操作
			else 
			{
				usr.type = 'C';
				if(sendto(cfd,&usr,sizeof(usr),0,(struct sockaddr*)&sin,addrlen) == -1)
				{
					perror("sendto error");
					return -1;
				}
			}
		}
	}
	return 0;
}

 结果

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

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

相关文章

东方博宜 1469. 数的统计

东方博宜 1469. 数的统计 #include<iostream> using namespace std; int main() {int n ;cin >> n ;int x ;cin >> x ;int cnt ;cnt 0;for (int i 1 ; i < n ; i){int num ;num i ;while(num!0){int g ;g num % 10 ;if (g x)cnt 1 ;num num / 10…

springboot294基于java的火车票订票系统的设计与实现

火车票订票系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装火车票订票系统软件来发挥其高效…

ElasticSearch首次启动忘记密码,更改密码(Windows 10)

先启动ElasticSearch 启动方式cmd到lasticsearch-8.12.2\bin目录下输入elasticsearch 启动成功后新开一个窗口输入elasticsearch-reset-password -u elastic

《剑指 Offer》专项突破版 - 面试题 88 : 动态规划的基础知识(C++ 实现)

目录 前言 面试题 88 : 爬楼梯的最少成本 一、分析确定状态转移方程 二、递归代码 三、使用缓存的递归代码 四、空间复杂度为 O(n) 的迭代代码 五、空间复杂度为 O(1) 的迭代代码 前言 动态规划是目前算法面试中的热门话题&#xff0c;应聘者经常在各大公司的面试中遇到…

C++ —— 日期计算器

1. 头文件 #pragma once #include <iostream> using namespace std;class Date { public:Date(int year 1, int month 1, int day 1);int GetMonthDay();bool operator>(const Date& d) const;bool operator>(const Date& d)const;bool operator<(c…

机器学习--jupyter-matplotlib使用中无法显示中文

jupyter使用中无法显示中文 在jupyter中&#xff0c;通过matplotlib作图时可能会添加中文标题&#xff0c;但有时候会不显示中文 import numpy as np import matplotlib.pyplot as pltx np.arange(0, 6, 0.1) # 以0.1为单位&#xff0c;成0到6的数据 y1 np.sin(x) y2 np.c…

ubuntu安装多个gcc并设置可切换

测试环境&#xff1a; Ubuntu16.04 1. 查看当前有几个gcc&#xff0c;g ls /usr/bin/gcc* ls /usr/bin/g* 有两个版本&#xff0c;5和7. 2. 安装特定gcc/g 版本 可以用sudo apt install gcc-version安装&#xff0c;比如说我想安装gcc-7&#xff0c;则命令为sudo apt instal…

第5章 数据建模和设计

思维导图 5.1 引言 最常见的6种模式&#xff1a;关系模式、多维模式、面向对象模式、 事实模式、时间序列模式和NoSQL模式 每种模式分为三层模型&#xff1a;概念模型、逻辑模型和物理模型 每种模型都包含一系列组件&#xff1a;如实体、关系、事实、键和属性。 5.1.1 业务驱…

【Flink】窗口实战:TUMBLE、HOP、SESSION

窗口实战&#xff1a;TUMBLE、HOP、SESSION 1.TUMBLE WINDOW1.1 语法1.2 标识函数1.3 模拟用例 2.HOP WINDOW2.1 语法2.2 标识函数2.3 模拟用例 3.SESSION WINDOW3.1 语法3.2 标识函数3.3 模拟用例 4.更多说明 在流式计算中&#xff0c;流通常是无穷无尽的&#xff0c;我们无法…

C++第十弹---类与对象(七)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、再谈构造函数 1.1、构造函数体赋值 1.2、初始化列表 1.3、explicit关键字 2、static成员 2.1、概念 2.2、特性 2.3、面试题 总结 1、再…

鸿蒙Harmony应用开发—ArkTS(@Prop装饰器:父子单向同步)

Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的&#xff0c;但是变化不会同步回其父组件。 说明&#xff1a; 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 概述 Prop装饰的变量和父组件建立单向的同步关系&#xff1a; Prop变量…

leetcode 2617. 网格图中最少访问的格子数【单调栈优化dp+二分】

原题链接&#xff1a;2617. 网格图中最少访问的格子数 题目描述&#xff1a; 给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。 当你在格子 (i, j) 的时候&#xff0c;你可以移动到以下格子之一&#xff1a; 满足 j < k < gri…

【单元测试】一文读懂java单元测试

目录 1. 什么是单元测试2. 为什么要单元测试3. 单元测试框架 - JUnit3.1 JUnit 简介3.2 JUnit 内容3.3 JUnit 使用3.3.1 Controller 层单元测试3.3.2 Service 层单元测试3.3.3 Dao 层单元测试3.3.4 异常测试3.3.5 测试套件测多个类3.3.6 idea 中查看单元测试覆盖率3.3.7 JUnit …

Excel使用VLOOKUP函数

VLOOKUP(lookup_value,table_array,col_index_num,range_lookup) 释义&#xff1a; lookup_value&#xff1a;要查找的值&#xff0c;包括数字&#xff0c;文本等 table_array&#xff1a;要查找的值以及预期返回的内容所在的区域 col_index_num&#xff1a;查找的区域的列…

安装mysql8.0.36遇到的问题没有developer default 选项问题

安装mysql8.0.36的话没有developer default选项&#xff0c;直接选择customer就好了&#xff0c;点击next之后通过点击左边Available Products里面的号和中间一列的右箭头添加要安装的产品&#xff0c;最后会剩下6个 安装完成后默认是启动了&#xff0c;并且在电脑注册表注册了…

机器学习——决策树剪枝算法

机器学习——决策树剪枝算法 决策树是一种常用的机器学习模型&#xff0c;它能够根据数据特征的不同进行分类或回归。在决策树的构建过程中&#xff0c;剪枝算法是为了防止过拟合&#xff0c;提高模型的泛化能力而提出的重要技术。本篇博客将介绍剪枝处理的概念、预剪枝和后剪…

《优化接口设计的思路》系列:第九篇—用好缓存,让你的接口速度飞起来

一、前言 大家好&#xff01;我是sum墨&#xff0c;一个一线的底层码农&#xff0c;平时喜欢研究和思考一些技术相关的问题并整理成文&#xff0c;限于本人水平&#xff0c;如果文章和代码有表述不当之处&#xff0c;还请不吝赐教。 作为一名从业已达六年的老码农&#xff0c…

vue2 自定义 v-model (model选项的使用)

效果预览 model 选项的语法 每个组件上只能有一个 v-model。v-model 默认会占用名为 value 的 prop 和名为 input 的事件&#xff0c;即 model 选项的默认值为 model: {prop: "value",event: "input",},通过修改 model 选项&#xff0c;即可自定义v-model …

35 跨域相关问题, 以及常见的解决方式

前言 跨域相关 这是一个 经常会碰到的问题 然后 常见的解决方式 也大概就是几种, 各有各的问题 这里仅仅是 从理论上 来探讨这个问题 主流的解决方式 是通过代理, 将不同域 合并到同一个域 测试用例 测试用例如下, 这里仅仅是一个简单的数据展示 获取对方 “/config.jso…

【c++入门】引用,内联函数,auto

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本节我们来到c中一个重要的部分&#xff1a;引用 目录 1.引用的基本概念与用法1.1引用特性1.2使用场景1.3传值、传引用效率比较1.4引用做返回值1.5引用和指针的对…