基于UDP实现直播间聊天的功能

需求:
	软件划分为用户客户端和主播服务端两个软件client.c和server.c 

用户客户端负责:
    1.接收用户的昵称
    2.接收用户输入的信息,能够将信息发送给服务端
    3.接收服务端回复的数据信息,并完成显示

主播服务端负责:
    1.对所有加入直播间的用户的IP地址和端口实现管理(加入、退出)
    2.当有新的客户端加入时,能够向所有客户端提示:"欢迎 XXX 用户进入直播间"
    3.当有客户端退出时,能够向所有客户端提示:"XXX 离开直播间"
    4.能够实现客户端聊天内容的转发,当某个客户端发送聊天信息时,能够将该信息转给除了该用户之外聊天室内所有其余客户端用户

head.h文件

#ifndef __HEAD_H__
#define __HEAD_H__

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/in.h>

struct address
{
	int mark;
	struct sockaddr_in addr;
};

struct msgbuf{
	int type;
	char name[32];
	char text[512];
};

#define MSG_TYPE_START		100
#define MSG_TYPE_END		200
#define MSG_TYPE_CHAT		300
#define RECV_ADDR			"192.168.1.152"
#define RECV_PORT			50000

#endif

client.c

#include "head.h"

char name[32] = {0};
int sockfd = 0;
struct sockaddr_in recvaddr;
pthread_t tid_send;		//发线程
pthread_t tid_recv;		//收线程

void *sendfun(void *arg)
{
	struct msgbuf sendmsg;	
	ssize_t nsize = 0;

	while (1)
	{
		memset(&sendmsg, 0, sizeof(sendmsg));
		sendmsg.type = MSG_TYPE_CHAT;
		sprintf(sendmsg.name, "%s", name);
		gets(sendmsg.text);
		
		if (!strcmp(sendmsg.text, ".quit"))
		{
			sendmsg.type = MSG_TYPE_END;
		}

		nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
		if (-1 == nsize)
		{
			perror("fail to sendto");
			return NULL;
		}

		if (sendmsg.type == MSG_TYPE_END)
		{
			break;
		}
	}

	pthread_cancel(tid_recv);
	
	return NULL;
}

void *recvfun(void *arg)
{
	struct msgbuf recvmsg;
	ssize_t nsize = 0;

	while (1)
	{
		memset(&recvmsg, 0, sizeof(recvmsg));
		nsize = recvfrom(sockfd, &recvmsg, sizeof(recvmsg), 0, NULL, NULL);
		if (-1 == nsize)
		{
			perror("fail to recvfrom");
			return NULL;
		}

		if (recvmsg.type == MSG_TYPE_CHAT)
		{
			printf("%s(%s:%d)>%s\n", recvmsg.name, RECV_ADDR, RECV_PORT, recvmsg.text);
		}
		else if (recvmsg.type == MSG_TYPE_END)
		{
			break;
		}
	}
	
	pthread_cancel(tid_send);

	return NULL;
}

int main(void)
{
	struct msgbuf sendmsg;
	ssize_t nsize = 0;

	recvaddr.sin_family = AF_INET;
	recvaddr.sin_port = htons(RECV_PORT);
	recvaddr.sin_addr.s_addr = inet_addr(RECV_ADDR);

	printf("请输入您的昵称:\n");
	gets(name);

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		perror("fail to socket");
		return -1;
	}
	
	memset(&sendmsg, 0, sizeof(sendmsg));
	sendmsg.type = MSG_TYPE_START;
	sprintf(sendmsg.name, "%s", name);
	nsize = sendto(sockfd, &sendmsg, sizeof(sendmsg), 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
	if (-1 == nsize)
	{
		perror("fail to sendto");
		return -1;
	}

	pthread_create(&tid_send, NULL, sendfun, NULL);
	pthread_create(&tid_recv, NULL, recvfun, NULL);

	pthread_join(tid_send, NULL);
	pthread_join(tid_recv, NULL);

	close(sockfd);

	return 0;
}

server.c

#include "head.h"

struct address ClientIpList[100];

int AddClientIp(struct sockaddr_in TmpAddr)
{
	int i = 0;

	for(i = 0;i < 100;++i)
	{
		if(ClientIpList[i].mark == 0)
		{
			ClientIpList[i].addr = TmpAddr;
			ClientIpList[i].mark = 1;
			break;
		}
	}

	return 0;
}
int DelClientIp(struct sockaddr_in TmpAddr)
{
	int i = 0;

	for(i = 0;i < 100;++i)
	{
		if(0 == memcmp(&TmpAddr,&ClientIpList[i].addr,sizeof(TmpAddr)))
		{
			ClientIpList[i].mark = 0;
			break;
		}
	}

	return 0;
}

int BoardcastClientIp(int sockfd,struct sockaddr_in TmpAddr,struct msgbuf TmpMes)
{
	int i = 0;
	ssize_t nsize = 0;
	for(i = 0;i < 100;++i)
	{
		if(ClientIpList[i].mark == 0)
		{
			continue;
		}

		if(0 != memcmp(&TmpAddr,&ClientIpList[i].addr,sizeof(TmpAddr)))
		{
			nsize = sendto(sockfd,&TmpMes,sizeof(TmpMes),0,(struct sockaddr *)&ClientIpList[i].addr,sizeof(ClientIpList[i].addr));
			if(-1 == nsize)
			{
				continue;
			}
		}
	}

	return 0;
}


int main(void)
{
	int sockfd = 0;
	int ret = 0;
	ssize_t nsize = 0;
	struct msgbuf recvmes;
	struct sockaddr_in recvaddr;
	struct sockaddr_in sendaddr;
	socklen_t addrlen = sizeof(sendaddr);

	recvaddr.sin_family = AF_INET;
	recvaddr.sin_port = htons(RECV_PORT);
	recvaddr.sin_addr.s_addr = INADDR_ANY;

	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd)
	{
		perror("fail to socket");
		return -1;
	}
	
	ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
	if (-1 == ret)
	{
		perror("fail to bind");
		return -1;
	}

	memset(&ClientIpList,0,sizeof(ClientIpList));
	while (1)
	{
		memset(&recvmes, 0, sizeof(recvmes));
		nsize = recvfrom(sockfd, &recvmes, sizeof(recvmes), 0,(struct sockaddr *)&sendaddr, &addrlen);
		if (-1 == nsize)
		{
			perror("fail to recvfrom");
			return -1;
		}
		
		
		if(recvmes.type == MSG_TYPE_START)
		{
			AddClientIp(sendaddr);
			recvmes.type = MSG_TYPE_CHAT;
			sprintf(recvmes.text,"%s",recvmes.name);
		}
		else if (recvmes.type == MSG_TYPE_END)
		{
			DelClientIp(sendaddr);
			recvmes.type = MSG_TYPE_CHAT;
			sprintf(recvmes.text,"%s",recvmes.name);
		}
		
		if (recvmes.type == MSG_TYPE_CHAT)
		{
			BoardcastClientIp(sockfd,sendaddr,recvmes);
		}
	}

	close(sockfd);

	return 0;
}

结果:

在这里插入图片描述

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

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

相关文章

无尘车间:保障电子产品品质与员工健康

在当今数字化时代&#xff0c;电子产品已经成为我们生活中不可或缺的一部分。从智能手机到计算机&#xff0c;从家用电器到汽车电子系统&#xff0c;电子产品无处不在&#xff0c;给我们的生活带来了便利与快捷。然而&#xff0c;这些高科技产品的背后是一系列复杂的制造过程&a…

Paddle上手实战——NLP经典cls任务“推特文本情感13分类”

Paddle上手实战——NLP经典cls任务“推特文本情感13分类” 实战背景介绍 数据地址:https://www.heywhale.com/home/activity/detail/611cbe90ba12a0001753d1e9/content Twitter推文具备多重特性,首要之处在于其与Facebook的显著区别——其完全基于文本形式,通过Twitter接…

基于docker安装的Jenkins实现python执行自动化测试程序

背景 通过Jenkins实现自动化测试,在全局配置中配置好后,执行构建发生如下错误 解决办法: 在Jenkins中插件管理中下载python后,回到Jenkins容器中 查找刚下载的python所在位置 到Jenkins中全局配置中修改脚本 1.可以在环境变量中定义python所在位置 2.在一下图示中进行获取…

Rust泛型与trait特性,模仿接口的实现

泛型是一个编程语言不可或缺的机制。 C 语言中用"模板"来实现泛型&#xff0c;而 C 语言中没有泛型的机制&#xff0c;这也导致 C 语言难以构建类型复杂的工程。 泛型机制是编程语言用于表达类型抽象的机制&#xff0c;一般用于功能确定、数据类型待定的类&#xf…

VMware Workstation安装Linux虚拟机与虚拟机克隆,特别适合搭建虚拟机集群环境,工作效率直线上升~

虚拟机 一、安装虚拟机二、克隆虚拟机三、配置静态IP地址一、安装虚拟机 设置虚拟机名称与安装位置 设置磁盘大小 配置硬件参数

Redis主从架构和管道Lua(一)

Redis主从架构 架构 Redis主从工作原理 如果为master配置了一个slave,不管这个slave是否是第一次连接上Master,它都会发送一个PSYNC命令给master请求复制数据。master受到PSYNC命令&#xff0c;会在后台进行数据持久化通过bgsave生成最新的 RDB快照文件&#xff0c;持久化期间…

Linux阻塞与非阻塞IO简介

一. 简介 阻塞与非阻塞IO是Linux驱动开发中很常见的两种设备访问模式&#xff0c;在编写驱动的时候&#xff0c;一定要考虑到阻塞和非阻塞。 本文来学习一下&#xff0c;什么是 Linux下的阻塞与非阻塞IO访问。 二. Linux阻塞与非阻塞IO 这里的 “IO” 并不是我们学习 STM32…

[机器视觉]halcon十二 条码识别、字符识别之字符识别

[机器视觉]halcon十二 条码识别、字符识别之字符识别 流程 获取图像-》创建模型-》查找文本-》清除模型 效果 算子 create_text_model_reader &#xff1a; 创建文本模型 find_text : 查找文本 get_text_result &#xff1a;获取文本内容 set_text_model_param : 设置文本模板…

使用Pytorch导出自定义ONNX算子

在实际部署模型时有时可能会遇到想用的算子无法导出onnx&#xff0c;但实际部署的框架是支持该算子的。此时可以通过自定义onnx算子的方式导出onnx模型&#xff08;注&#xff1a;自定义onnx算子导出onnx模型后是无法使用onnxruntime推理的&#xff09;。下面给出个具体应用中的…

米酒生产加工污水处理需要哪些工艺设备

米酒生产加工过程中产生的污水是一项重要的环境问题&#xff0c;需要采用适当的工艺设备进行处理。下面将介绍一些常用的污水处理工艺设备。 首先&#xff0c;生产过程中的污水需要进行初级处理&#xff0c;常见的设备包括格栅和砂池。格栅用于去除污水中的大颗粒杂质&#xff…

python导出数据到sqlite中

import sqlite3# 数据 data [{username: 张三, age: 33, score: 13},{username: 李四, age: 44, score: 14},{username: 王五, age: 55, score: 15}, ]# 连接SQLite数据库&#xff08;如果不存在则创建&#xff09; conn sqlite3.connect(test.db)# 创建游标对象 cursor con…

神经网络8-注意力机制

注意力机制&#xff08;Attention Mechanism&#xff09;源于对人类视觉的研究。在认知科学中&#xff0c;由于信息处理的瓶颈&#xff0c;人类会选择性地关注所有信息的一部分&#xff0c;同时忽略其他可见的信息。这种机制被称为注意力机制。举个例子来说&#xff0c;当我们观…

【排序算法】深入理解插入排序算法:从原理到实现

1. 引言 排序算法是计算机科学中的基本问题之一&#xff0c;它的目标是将一组元素按照某种规则进行排列。插入排序是其中一种简单但有效的排序算法&#xff0c;通过逐步构建有序序列来实现排序。本文将从原理、时间复杂度、应用场景、优缺点等方面深入探讨插入排序算法&#x…

keepalived原理以及lvs、nginx跟keeplived的运用

keepalived基础 keepalived的原理是根据vrrp协议&#xff08;主备模式&#xff09;去设定的 vrrp技术相关原理 状态机&#xff1b; 优先级0~255 心跳线1秒 vrrp工作模式 双主双备模式 VRRP负载分担过程 vrrp安全认证&#xff1a;使用共享密匙 keepalived工具介绍 keepal…

如何压缩图片大小到100kb以下?

如何压缩图片大小到100kb以下&#xff1f;不知道上班族小伙伴有没有发现&#xff0c;当我们工作中使用图片的时候经常遇到遇到一个尴尬的情况&#xff0c;例如我们需要网某个网站上传一张图片的时候&#xff0c;会被限制要求图片大小不能超过100kb&#xff0c;如果超过就无法进…

基于uniapp cli项目开发的老项目,运行报错path.replace is not a function

项目&#xff1a;基于uniapp cli的微信小程序老项目 问题&#xff1a;git拉取代码&#xff0c;npm安装包时就报错&#xff1b; cnpm能安装成功包&#xff0c;运行报错 三种方法尝试解决&#xff1a; 更改代码&#xff0c;typeof pathstring的话&#xff0c;才走path.replace…

JVM 面试题

1、什么情况下会发生栈内存溢出。 栈内存溢出通常发生在以下几种情况中&#xff1a; 函数递归调用过深&#xff1a; 当函数递归调用自身且没有合适的退出条件时&#xff0c;每次递归调用都会在栈上分配一个新的栈帧来存储局部变量、返回地址等信息。如果递归层次过多&#xff…

计讯物联山体滑坡地质灾害监测方案为灾区保驾护航

针对我国某些地区频繁爆发山体滑坡的情况&#xff0c;计讯物联深入调研滑坡体自动监测、无线通讯、险情预报等方面&#xff0c;自主研发反应快速高效、可广泛应用的山体滑坡地质灾害监测方案&#xff0c;全面掌握山体滑坡信息&#xff0c;为当地居民留有余裕的逃生时间。 计讯物…

Docker 配置阿里云镜像加速器

一、首先需要创建一个阿里云账号 二、登录阿里云账号 三、进入控制台 四、搜索容器镜像服务&#xff0c;并选择 五、选择镜像工具中的镜像加速 六 、配置镜像源 注意&#xff1a;有/etc/docker文件夹的直接从第二个命令开始

JavaSE-10(JDK8 新特性-万字总结)

新特性概述 Lambda 表达式函数式接口引用Stream API接口中的默认方法 / 静态方法新时间日期 API其他新特性 Lambda表达式/匿名函数 在 Java 中&#xff0c;匿名函数通常是指 Lambda 表达式。 Lambda 表达式允许你以一种简洁、紧凑的方式编写匿名函数&#xff0c;而不必创建…