浅学C++(7)网络编程(生产者消费者模型 网络聊天室的实现)

生产者与消费者模型
    生产者:生产数据的线程
    消费者:使用数据的线程
    仓库:临时存放数据的缓冲区(仓库解决了生产、消费不匹配)

    可能产生的问题:
        消费快于生产:仓库空虚、饿死
        生产快于消费:仓库爆满、撑死
    
    使用条件变量解决以上问题:
        当缓冲区空的时候:消费者进程睡入条件变量(empty),通知生产者线程全部醒来(full)
        当缓冲区满的时候:生产者进程睡入条件变量(full),通知消费者线程全部醒来(empty)

网络聊天室

代码中所用到的 头文件 和 使用的函数 请参照 上一篇 网络socket 以及线程池的封装

客户端业务逻辑:
    1、客户端注册名字
    2、发送消息|接收消息

服务端业务逻辑:
    1、开启线程池、等待客户端连接
    2、接收客户端名字,告诉所有的在线的客户端,XXX进入聊天室。
    3、接收客户端消息,转发给其他客户端。
    4、通知其他客户端 xxx退出聊天室
    5、同时在线人数最多50人。

    注意:任何客户端都应该可以随时进入退出。

服务器代码

#include <stdio.h>
#include <stdlib.h>
#include <network.h>
#include <stdbool.h>
#include <string.h>
#include "threadpool.h"

#define MAX_CLIENT (50)
//	buf的字节数
#define BUF_SIZE (4096)

//	在线客户端nw的数组
NetWork* cli_arr[MAX_CLIENT];


//	添加nw
int add_clinw(NetWork* nw)
{
	for(int i=0; i<MAX_CLIENT; i++)
	{
		if(NULL == cli_arr[i])
		{
			cli_arr[i] = nw;
			return i;
		}
	}
}

//	转发消息给其他客户端
void send_cli_arr(const char* msg,int index)
{
	for(int i=0; i<MAX_CLIENT; i++)
	{
		if(NULL != cli_arr[i] && i != index)
		{
			send_nw(cli_arr[i],msg,strlen(msg)+1);	
		}
	}
}

//	消费者业务逻辑函数
void chat_work(void* arg)
{
	//	添加arg到客户端nw数组中	
	int index = add_clinw(arg);

	char* buf = malloc(BUF_SIZE);
	//	接收昵称并转发
	int ret = recv_nw(cli_arr[index],buf,BUF_SIZE);
	if(0 >= ret || 0 == strncmp(buf,"quit",4))
	{
		free(buf);
		close_nw(cli_arr[index]);
		cli_arr[index] = NULL;
		return;
	}
	strcat(buf," 进入聊天室,大家小心!");
	send_cli_arr(buf,index);
	
	buf[ret-1] = ':';
	char* msg = buf+ret;

	for(;;)
	{
		int ret =recv_nw(cli_arr[index],msg,BUF_SIZE-ret);	
		if(0 >= ret || 0 == strncmp(msg,"quit",4))
		{
			sprintf(msg,"退出聊天室!");
			send_cli_arr(buf,index);
			free(buf);
			close_nw(cli_arr[index]);
			cli_arr[index] = NULL;
			return;
		}
		send_cli_arr(buf,index);
	}
}

int main(int argc,const char* argv[])
{
	if(3 != argc)
	{
		printf("User: ./server <ip> <port>\n");
		return EXIT_SUCCESS;
	}
	
	//	启动网络服务
	NetWork* svr_nw = 
		init_nw(SOCK_STREAM,atoi(argv[2]),argv[1],true);
	if(NULL == svr_nw)
	{
		puts("网络错误,请检查!\n");
		return EXIT_FAILURE;
	}

	//	创建并启动线程池
	ThreadPool* threadpool = create_threadpool(MAX_CLIENT,20,chat_work);
	start_threadpool(threadpool);

	for(;;)
	{
		//	生产者 等待客户端连接
		NetWork* cli_nw = accept_nw(svr_nw);
		if(NULL == cli_nw)
		{
			puts("网络异常!\n");
			continue;
		}

		//	添加数据到仓库
		push_threadpool(threadpool,cli_nw);
	}
}

客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <network.h>
#include <string.h>
#include <pthread.h>

void* run(void* arg)
{
	char recv_buf[4096] = {};
	size_t buf_size = sizeof(recv_buf);
	NetWork* nw = arg;
	for(;;)
	{
		int ret = recv_nw(nw,recv_buf,buf_size);	
		if(0 >= ret)
		{
			puts("服务器正在升级!");
			exit(EXIT_FAILURE);
		}
		printf("\r%s\n>>>",recv_buf);
		fflush(stdout);
	}
}

int main(int argc,const char* argv[])
{
	if(3 != argc)
	{
		puts("User: ./client <ip> <port>");
		return EXIT_SUCCESS;
	}

	NetWork* nw = init_nw(SOCK_STREAM,atoi(argv[2]),argv[1],false);
	if(NULL == nw)
	{
		puts("网络有误,请检查!");
		return EXIT_FAILURE;
	}

	char send_buf[4096] = {};
	size_t buf_size = sizeof(send_buf);

	printf("请输入你的昵称:");
	scanf("%[^/n]%*c",send_buf);

	send_nw(nw,send_buf,strlen(send_buf)+1);
	
	//	开启接收群聊消息线程
	pthread_t tid;
	pthread_create(&tid,NULL,run,nw);

	for(;;)
	{
		printf(">>>");
		scanf("%s",send_buf);
		int ret = send_nw(nw,send_buf,strlen(send_buf)+1);
		if(0 == strncmp(send_buf,"quit",4))
		{
			puts("已退出当前聊天室!");
			return EXIT_SUCCESS;
		}
		if(0 >= ret)
		{
			puts("服务器正在升级,请稍候");
			return EXIT_FAILURE;
		}
	}
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值