多人聊天室(带私聊功能)Linux网络编程基础

这篇具有很好参考价值的文章主要介绍了多人聊天室(带私聊功能)Linux网络编程基础。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在和同学一起努力下终于完成了期末作业哈哈哈哈

文章目录

目录

前言

一、需求分析

二、功能设计

1.服务器端:

2.客户端:

三、流程图:

编程流程图:

服务器流程图:

客户端流程图:

四、运行效果:

项目源码:

服务器源码

客户端源码:

总结:


前言

Linux网络编程是我们这学期学习嵌入式的重要内容

不仅仅是期末作业,更是对linux网络编程的巩固。

接下来我会将全部内容分享出来哈哈ψ(`∇´)ψ

提示:完成得比较随意,不够严谨,仅供参考,大家都能做得更好!


一、需求分析

       需要一个可以实现多人同时在线的聊天室网络应用,用户各自在登录时自定义一个网名,并以这个网名在多人聊天室中与别人聊天,同时每个用户可以通过用户名辨别其他用户。

       用户还可以通过已知的用户名,与指定用户进行私聊(仅限对方可见)。

(走个流程)

二、功能设计

一个能容纳多人在线聊天的聊天室的网络应用程序,包括客户端与服务器端:

1.服务器端:

服务器端:能同时接受多用户的登陆,并将每个用户的聊天信息广播到其他用户;广播的时候会附加上发言的用户名;

2.客户端:

客户端:用户能够以` client name` 的命令行程序运行,并以`name`的用户名登陆聊天室(不需要密码);客户端能允许用户发言,并实时接收服务器的广播信息;当客户端输入`quit`时,退出客户端;客户端具备私聊的功能,当A用户需要向B用户(假设`B`为用户名)私聊时,使用命令`send:B:message` 发送聊天信息;服务器会把该信息发送给B用户;


三、流程图:

编程流程图:

多人聊天室(带私聊功能)Linux网络编程基础

服务器流程图:

多人聊天室(带私聊功能)Linux网络编程基础

客户端流程图:

多人聊天室(带私聊功能)Linux网络编程基础


四、运行效果:

先运行服务器端,然后在其他终端分别运行客户端即可:

如图:三个用户分别进入聊天室,系统均会进行广播通知,并显示当前在线人数,当其中一个客户端运行时,会先注册登录(无需密码)。

A 和 B 分别在群聊中发送消息,消息均在各个客户端显示;A 向 C 单独发送私聊悄悄话,仅C的客户端能看见A发来的消息,说明私聊功能实现。

 截图

多人聊天室(带私聊功能)Linux网络编程基础


项目源码:

服务器源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#define BUF_SIZE (4096)
#define SEM_SIZE (20) // 群聊上限人数

// 信号量--判断群聊人数
sem_t sem;
// 服务端文件描述符
int svr_fd;
// 存储群友,多一个是为了当群人满时,空一个出来接发信息
int cli_fd[SEM_SIZE + 1] = {};

struct client
{
	/* data */
	char buf[BUF_SIZE];	 // message
	char name[BUF_SIZE]; // name
	int client_fd;		 // fd
};

struct client clients[SEM_SIZE];

// 字符串分割函数
void split(char *src, const char *separator, char **dest, int *num)
{
	char *pNext;
	// 记录分隔符数量
	int count = 0;
	// 原字符串为空
	if (src == NULL || strlen(src) == 0)
		return;
	// 未输入分隔符
	if (separator == NULL || strlen(separator) == 0)
		return;
	/*
		c语言string库中函数,
		声明:
		char *strtok(char *str, const char *delim)
		参数:
		str -- 要被分解成一组小字符串的字符串。
		delim -- 包含分隔符的 C 字符串。
		返回值:
		该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。

	*/
	char *strtok(char *str, const char *delim);
	// 获得第一个由分隔符分割的字符串
	pNext = strtok(src, separator);
	while (pNext != NULL)
	{
		// 存入到目的字符串数组中
		*dest++ = pNext;
		++count;
		/*
			strtok()用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。
			当strtok()在参数s的字符串中发现参数delim中包涵的分割字符时,则会将该字符改为\0 字符。
			在第一次调用时,strtok()必需给予参数s字符串,往后的调用则将参数s设置成NULL。
			每次调用成功则返回指向被分割出片段的指针。

		*/
		pNext = strtok(NULL, separator);
	}
	*num = count;
}

// 群发函数
void *send_all(char *buf)
{
	for (int i = 0; i < SEM_SIZE; i++)
	{
		// 若值为-1,则没有此群友,表示已经退出或未被占有
		if (-1 != cli_fd[i])
		{
			printf("%s\n", buf);
			printf("send to %d\n", cli_fd[i]);
			write(cli_fd[i], buf, strlen(buf) + 1);
		}
	}
}

/**
 * 发送给指定的用户
 * char *buf 发送的消息
 * int fd 发给谁
 */
void *send_one(char *buf, int fd)
{
	printf("send to %d : %s\n",fd, buf);
	// printf("send to %d\n", fd);
	write(fd, buf, strlen(buf) + 1);
}

// 服务端接收函数
void *server(void *arg)
{

	int fd = *(int *)arg;
	char buf[BUF_SIZE];
	char name[BUF_SIZE], ts[BUF_SIZE];
	
	

	// 获取昵称
	read(fd, clients[fd].name, sizeof(name));
	clients[fd].client_fd = fd;

	// printf("clients[fd].name = %s\n", clients[fd].name);
	printf("存放clients[fd].client_fd = %d\n", clients[fd].client_fd);
	sprintf(ts, "[system]热烈欢迎 %s 进入群聊", clients[fd].name);
	send_all(ts);

	for (;;)
	{
		// 接收信息,无信息时将阻塞
		int recv_size = read(fd, clients[fd].buf, sizeof(buf));

		
		// 收到退出信息
		if (0 >= recv || NULL != strstr(clients[fd].buf, "quit"))
		{
			sprintf(ts, "[system]欢送 %s 离开群聊\n", clients[fd].name);
			int index = 0;
			// 找到要退出的那个人,并将其置为-1
			for (; index < SEM_SIZE; index++)
			{
				if (cli_fd[index] == fd)
				{
					cli_fd[index] = -1;
					break;
				}
			}
			// 群发XXX退出聊天室提示消息
			send_all(ts);

			// 群友退出,信号量+1
			int n;
			sem_post(&sem);
			sem_getvalue(&sem, &n);

			printf("[system] %s 离开群聊,群聊还剩%d人\n", clients[fd].name, SEM_SIZE - n);
			strcpy(clients[fd].buf, "quit");

			write(fd, clients[fd].buf, strlen(clients[fd].buf) + 1);
			close(fd);
			pthread_exit(NULL);
		}

		// 单独发送或者群发
		//    ------/send name message
		if (0 >= recv || NULL != strstr(clients[fd].buf, "send"))// 单独发
		{ 	
			char str[100];
			char *p[10]={0};
			int num=0,i;
			//拷贝
			strcpy(str,clients[fd].buf);
			// printf("clients[fd].buf1 = %s\n", clients[fd].buf); //send name message
			// 对 clients[fd].buf进行截取
			split(clients[fd].buf,":",p,&num);
			// printf("clients[fd].buf1 = %s\n", clients[fd].buf); //send
			// printf("split str is = %s\n",p[1]);
			// 然后遍历数组进行比较 找到和name相同的数组元素,取出来进行发送
			for(i = 0;i < SEM_SIZE; i++) {


                                // 判断名字,去除对应client_fd进行发送
				if(NULL != strstr(clients[i].name,p[2])){
					printf("client[%d].name3 = %s\n",i,clients[i].name);
					char msg[200];
					sprintf(msg,"[悄悄话]%s:%s",clients[fd].name,p[3]);
					send_one(msg, clients[i].client_fd);
                                }
                        }
		}
		else
		{ // 群发
			send_all(clients[fd].buf);
		}
	}
}

/**
 * quit
 */
void sigint(int signum)
{
	close(svr_fd);
	sem_destroy(&sem);
	printf("[system]服务器关闭\n");
	exit(0);
}

int main()
{
	signal(SIGINT, sigint);
	// 初始化信号量,群聊上限SEM_SIZE人
	sem_init(&sem, 0, SEM_SIZE);

	// 创建socket对象
	printf("[system]创建socket对象...\n");
	svr_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (0 > svr_fd)
	{
		perror("socket");
		return -1;
	}


	//端口复用函数:解决端口号被系统占用的情况
	int on = 1;
	int gg = setsockopt(svr_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	if(gg==-1)
	{
		perror("setsockopt");
		return -1;
	}

	// 准备通信地址(自己)
	printf("[system]准备通信地址...\n");
	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(6666);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	socklen_t addrlen = sizeof(addr);

	// 绑定socket对象与地址
	printf("[system]绑定socket对象与地址...\n");
	if (bind(svr_fd, (struct sockaddr *)&addr, addrlen))
	{
		perror("bind");
		return -1;
	}

	// 设置监听和排除数量
	printf("[system]设置监听");
	if (listen(svr_fd, 10))
	{
		perror("listen");
		return -1;
	}

	printf("[system]等待客户端链接...\n");
	// 将初始值置全为-1,表示该聊天位置没有人占领
	memset(cli_fd, -1, sizeof(cli_fd));
	for (;;)
	{
		int sem_num;
		sem_getvalue(&sem, &sem_num);

		// 找到没有人占领的聊天位
		int index = 0;
		while (-1 != cli_fd[index])
			index++;
		cli_fd[index] = accept(svr_fd, (struct sockaddr *)&addr, &addrlen);

		if (0 > cli_fd[index])
		{
			perror("accept");
			return -1;
		}

		char buf[BUF_SIZE];
		if (0 >= sem_num)
		{
			printf("[system]人数已满,%d号客户端链接失败\n", cli_fd[index]);
			sprintf(buf, "[system]人数已满,客户端链接失败");
			write(cli_fd[index], buf, strlen(buf) + 1);
			close(cli_fd[index]);
			cli_fd[index] = -1;
		}
		else
		{
			sem_trywait(&sem);
			sem_getvalue(&sem, &sem_num);
			char msg[BUF_SIZE] = {};
			printf("[system]%d号客户端链接成功,当前聊天人数%d人\n", cli_fd[index], SEM_SIZE - sem_num);
			sprintf(msg, "[system]客户端链接成功,当前聊天人数%d人\n", SEM_SIZE - sem_num);
			write(cli_fd[index], msg, strlen(msg) + 1);

			// 创建线程客户端
			pthread_t tid;
			pthread_create(&tid, NULL, server, &cli_fd[index]);
		}
	}
}

客户端源码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define BUF_SIZE (4096)


/**
 * 读取server转发过来的消息
*/
void* client_read(void* arg)
{
	int cli_fd = *(int*)arg;
	char buf[BUF_SIZE];
	
	//接收数据
	for(;;)
	{
		int recv_size = read(cli_fd,buf,BUF_SIZE);
		if(0 >= recv_size || 0 == strcmp(buf,"quit"))
		{
			printf("已经与服务器断开链接\n");
			pthread_exit(NULL);
		}
		printf("%s\n",buf);
	}
}

int main()
{
	//创建socket对象
	printf("创建socket对象...\n");
	int cli_fd = socket(AF_INET,SOCK_STREAM,0);
	if(0 > cli_fd)
	{
		perror("socket");
		return -1;
	}
	
	//准备通信地址(服务端)
	printf("准备通信地址...\n");
	struct sockaddr_in addr = {};
	addr.sin_family = AF_INET;
	addr.sin_port = htons(6666);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");//此处填写自己的ip地址
	socklen_t addrlen = sizeof(addr);
	
	//链接服务端
	printf("链接服务端口...\n");
	if(connect(cli_fd,(struct sockaddr*)&addr,addrlen))
	{
		perror("connect");
		return -1;
	}
	
	char buf[BUF_SIZE];
	
	read(cli_fd,buf,BUF_SIZE);
	if(NULL == strstr(buf,"链接成功"))
	{
		printf("群聊人已满,请稍后再来\n");
		close(cli_fd);
		return 0;
	}
	printf("%s\n",buf);
	
	//链接成功,创建客户端
	pthread_t tid;
	pthread_create(&tid,NULL,client_read,&cli_fd);
	
	//输入昵称
	char name[BUF_SIZE] = {};
	printf("请输入你的昵称:");
	gets(name);
	write(cli_fd,name,strlen(name)+1);
	
	//发送数据
	for(;;)
	{
		printf(">>");
		gets(buf); // 获取键盘字符串
		
		char msg[BUF_SIZE]; // 存储格式化后的字符串
		sprintf(msg,"%s:%s",name,buf); // 输出字符串
		
		//把msg发送到cli_fd
		int send_size = write(cli_fd,msg,strlen(msg)+1);
		// 如果字符串是quit就退出
		if(0 >= send_size || 0 == strcmp(buf,"quit"))
		{
			printf("结束通信\n");
			close(cli_fd);
			pthread_exit(NULL);
			return 0;
		}
	}
}

总结:

太好咯,又做完一个作业 哈哈哈O(∩_∩)O文章来源地址https://www.toymoban.com/news/detail-491315.html

到了这里,关于多人聊天室(带私聊功能)Linux网络编程基础的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用Linux系统IO多路复用中eopll创建基于TCP通信协议的多人聊天室

    一.1.搭建好TCP的通信模型 2.创建红黑树根节点 3.将套接字事件添加到红黑树中,使其被监听 4.当套接字事件发生,表示有客户端连接,将连接事件加入到红黑树节点当中 5.每当连接事件发生时,表示客户端发送信息到服务器 6.每当有事件准备就绪时,将对应的红黑树节点信息

    2024年02月13日
    浏览(44)
  • Python多人聊天室

    链接:https://pan.baidu.com/s/1kzxiLTkvdxGAMgF3SQzcaw?pwd=vb9h 提取码:vb9h 利用socket方式编写一个多人聊天室程序,可以实现多个用户之间的群聊功能,私聊功能,显示当前用户功能 在聊天室程序中增加利用ftp实现文件的上传,下载,删除,查看当前文件功能 在聊天室程序中增加利用

    2024年02月03日
    浏览(59)
  • Java 多人聊天室

    Java实训做的多人聊天室,效果如图: 能够实现多个客户端之间的互相聊天,服务端来负责接收数据和转发数据。 每个客户端可以自己设置和修改自己的名称,并传给服务器让其他客户端知道。 当有客户端加入和退出时,群聊里每个客户端都有提示。(优化了异常检测,意外

    2024年02月10日
    浏览(40)
  • 【Java socket编程】多人聊天室

    课程设计大作业 功能: 1.具有 点对点通信 功能,任意客户端之间能够发送消息。 2.具有 群组通信 功能: 客户端可以 自主建立群组 ,添加或删除组成员; 客户端能够向组内成员同时发送消息,其他组成员不能收到。 3.具有 广播 功能 客户端能够向所有其他成员 广播消息

    2024年02月04日
    浏览(39)
  • 基于Python web的多人聊天室

              本文介绍了基于即时通讯的Python实现web版多人聊天室的设计和实现。这个系统利用了多种先进的技术,如Django、Channels、WebSocket,来实现即时通信,并利用MySQL和Redis作为数据库,同时还采用了多种前端技术,如bootstrap、CSS、html和js,来提供出色的用户体验。该系

    2024年02月16日
    浏览(40)
  • vue3-多人聊天室角色识别(全栈)

    主要技术栈是vue3,springboot,websocket,element-plus 主要目的是复习和梳理 发送信息,包装信息,转json,用socket发送到后端,使输入栏清空 后端解析json,因为是socket接口,所以不能用@requestbody解析json,用以下方式解析json 设置发送时间为当前时间,然后执行mapper数据库插入 广

    2024年02月11日
    浏览(39)
  • Python多人聊天室-基于socket UDP协议

    使用Python编写的基于socket UDP通信的多功能即时聊天室,包含Tkinter编写的图形化聊天界面,功能包括有账号注册和登录,登录成功后可以查看在线用户,并和聊天室内的其他在线用户聊天,包含私聊和群发,能发送文字、表情包,以及文件等。 登录和注册 显示在线用户 群聊

    2024年02月11日
    浏览(57)
  • C语言实现--基于UDP的多人在线聊天室

    目录 实现功能 实现思想 实现代码(部分及详解) 服务端部分代码 客户端部分代码 实现效果 项目中出现的问题和解决方法 项目整体代码展示 代码优化思路 服务端代码 客户端代码 服务端可以同时连接多个客户端; 新的客户端连接服务端时,可以在服务端显示自己的名字并

    2024年02月04日
    浏览(63)
  • 基于Python guI的多人聊天室的设计与实现

    现在,即时聊天系统已成为 Internet 上的主要交流工具,并且涌现出大量的AP和平台。这些AP和平台都拥有更加完善的交换机制,使得人们可以更加便捷地进行沟通和交换信息。 广域网的聊天系统多重多样,知名的软件主要有 Facebook、腾讯 QQ 等。局域网聊天通信软件也有很多,

    2024年02月05日
    浏览(53)
  • WebSocket+Vue实现简易多人聊天室 以及 对异步调用的理解

    代码仓库:github   HTTP是不支持长连接的,WebSocket是一种通信协议,提供了在单一、长连接上进行全双工通信的方式。它被设计用于在Web浏览器和Web服务器之间实现,但也可以用于任何需要实时通信的应用程序。使用ws作为协议标识符,如果需要加密则使用wss作为协议标识符

    2024年01月17日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包