使用线程实现QQ聊天

使用线程实现QQ聊天

 
   server端代码:

#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include<string.h>
#include<arpa/inet.h>
#include <pthread.h>
#include<vector>

#define      SERVER_PORT    9527   //port 
#define      BACKLOG        10     //同时允许多少个客户端跟server建立连接  
#define      GROUP_CHAT     0      //群聊
#define      PRIVATE_CHAT   1      //私聊
#define      OFF_LINE       2      //下线 
#define      ON_LINE        3      //上线
using namespace std;

typedef  struct    //定义一个聊天结构体
{
	int user_id;       //用户id
	int type;
	char name[16];     //用户名
	char message[200]; //消息内容
	char  to_the_other_one[20]; //要私聊的用户

}STU;

typedef struct
{
	int  accept_fd;      //保存客户端的accept_fd
	char user_name[16]; //姓名
	char message[200];  //消息内容

}CLIENT_ACCEPT_FD;

vector<CLIENT_ACCEPT_FD > client_info;//容器中存放客户端信息

//线程处理函数
void* thread_fun(void* accept_fd)
{
	int r_size;
	size_t w_size;
	int tmp_fd = *((int*)accept_fd);//  accept_fd 传过来,用这个accept_fd 实现群发功能
	STU  user0;//声明用户1
	CLIENT_ACCEPT_FD  Client;//保存着accept_fd的聊天结构体
	//对这两个结构体初始化
	bzero(&user0, sizeof(user0));
	bzero(&Client, sizeof(Client));
	Client.accept_fd = tmp_fd;
	client_info.push_back(Client);//把客户端信息加入容器中
	//cout << "容器中的大小是" << client_info.size() << endl;
	while (1)
	{
		//读客户端发过来的数据
		r_size = read(tmp_fd, &user0, sizeof(user0));  //r_size >0 有读到数据   <0  错误    == 0 有客户端退出
		if (r_size > 0)
		{
			//cout << "姓名:" << user0.name << endl;
			//cout << "消息体:" << user0.message << endl;
			strcpy(Client.user_name, user0.name);
			strcpy(Client.message, user0.message);
			cout << "姓名:" << Client.user_name << "消息" << Client.message << endl;
			
			//cout << "服务器收到的类型是" << user0.type << endl;
			if (user0.type == GROUP_CHAT) //是群聊
			{
				//遍历容器  回发消息给每个客户端
				for (int i = 0; i < client_info.size(); i++)
				{
					w_size = write(client_info[i].accept_fd, &user0, sizeof(user0));
				}
			}
			if (user0.type == PRIVATE_CHAT) //是私聊
			{
				
				for (int i = 0; i < client_info.size(); i++)
				{
					if (client_info[i].user_name == user0.to_the_other_one)
					{
						w_size = write(client_info[i].accept_fd, &user0, sizeof(user0));
						break;
					}
					
				}
			}
		}
		else if (r_size == 0)//客户端下线 
		{
			vector<CLIENT_ACCEPT_FD>::iterator it;
			cout << "没下线前的" << client_info.size() << endl;
			for (it = client_info.begin(); it != client_info.end(); it++)
			{
				//找到下线用户的accept_fd  把他从容器中移除
				if (it->accept_fd == tmp_fd)
				{
					user0.type = OFF_LINE;
					client_info.erase(it);
					break;
				}
			}
			cout << "下线后的" << client_info.size() << endl;
			for (int i = 0; i < client_info.size(); i++)
			{
				w_size = write(client_info[i].accept_fd, &user0, sizeof(user0));
			}
			return NULL;
		}
		else if (r_size < 0)
		{
			perror("read error");
			return NULL;
		}
	}

}

int main()
{
	int ret = 0;
	int accept_fd;
	pthread_t  thread_id;
	// creat socket
	int socket_fd;//套接字描述符
	socket_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (socket_fd < 0)
	{
		perror("socket error");
		return -1;
	}
	// 设置端口复用
	int opt = 1;//开启端口复用
	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	//bind 
	struct sockaddr_in server_addr;
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(SERVER_PORT);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	ret = bind(socket_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
	if (ret < 0)
	{
		perror("bind error");
		return -1;
	}
	//listen
	ret = listen(socket_fd, BACKLOG);
	while (1)
	{
		cout << "wait connect ..." << endl;
		accept_fd = accept(socket_fd, NULL, NULL);
		cout << "connected   !!!" << endl;
		if (accept_fd == -1)
		{
			perror("accept error");
			return -1;
		}
		ret = pthread_create(&thread_id, NULL, thread_fun, &accept_fd);//创建线程
		if (ret != 0)
		{
			perror("pthread_create error");
			return -1;
		}
	}
	return 0;
}

   client 端代码:

#include<iostream>
#include<stdio.h>
#include<unistd.h>
#include<arpa/inet.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>

#define     SERVER_PORT  9527
#define     SERVER_IP    "192.168.75.128"
#define     GROUP_CHAT    0      //群聊
#define     PRIVATE_CHAT  1      //私聊
#define     OFF_LINE      2      //下线 
#define     ON_LINE       3      //上线
using namespace std;


typedef  struct    //定义一个聊天结构体
{
	int user_id;       //用户id
	int type;
	char name[16];     //用户名
	char message[200]; //消息内容
	char  to_the_other_one[20]; //要私聊的用户

}STU;

int main()
{
	//客户端代码
/*
	思路:socket 读用专门的进程来实现。从客户端获取聊天内容用另一个进程去做
*/
	pid_t pid;
	int r_size = 0;
	//socket
	int client_fd;
	int ret = 0;
	client_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (client_fd == -1)
	{
		perror("socket error");
		return -1;
	}
	//connect
	struct sockaddr_in  addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(SERVER_PORT);
	addr.sin_addr.s_addr = inet_addr(SERVER_IP);
	ret = connect(client_fd, (const struct sockaddr*)  &addr,sizeof(addr));

	//聊天结构体初始化
	STU  chat1;
	char name[16] = { 0 };
	char other_name[16] = { 0 };
	char buffer[100] = { 0 };
	int m_type;
	bzero(&chat1,sizeof(chat1));
	cout << "请输入你的姓名:" << endl;
	cin >> name;
	cout << "请输入聊天类型  0:群聊  1:私聊 " << endl;
	cin >> m_type;
	cout << "类型是" << m_type << endl;
	if (m_type == 1)
	{
		cout << "请输入你要私聊的用户名" << endl;
		cin >>other_name;
	}
	pid = fork();
	if (pid > 0)//父进程
	{
		while (fgets(buffer, sizeof(buffer), stdin) != NULL)
		{
			strcpy(chat1.to_the_other_one, other_name);//对方名字
			strcpy(chat1.name, name);
			strcpy(chat1.message, buffer);//通过buffer 把消息传递到结构体的message中
			chat1.type = m_type;//类型传递
			write(client_fd, &chat1, sizeof(chat1));
			memset(&chat1, 0, sizeof(chat1));
			memset(buffer, 0, sizeof(buffer));
		}
	}
	else if (pid == 0)//子进程
	{
		//读取数据
		STU chat2;
		bzero(&chat2, sizeof(chat2));
		int tmp = 0;
		while (r_size = (read(client_fd, &chat2, sizeof(chat2))) > 0)
		{
			if (strcmp(chat2.name, name) != 0)
			{
				cout << chat2.name << ": " << chat2.message << endl;
			}
			if (chat2.type == OFF_LINE)//下线
			{
				cout << chat2.name << "下线了" << endl;
			}
		}
	}
	
	return 0;
}


   运行效果:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
由于QQ聊天通讯涉及到网络通讯和服务器端的处理,代码比较复杂。在这里我提供一个简单的实现代码,可以供您参考。 首先,我们需要创建一个登录界面和一个聊天界面的Qt窗口。登录界面包括一个用户名输入框、一个密码输入框和一个登录按钮。聊天界面包括一个消息列表框、一个消息输入框和一个发送按钮。 在登录界面中,我们需要实现登录按钮的槽函数,用于向服务器发送登录请求。在聊天界面中,我们需要实现发送按钮的槽函数,用于向服务器发送消息请求。 以下是一个简单的实现示例: ```cpp // 登录界面槽函数 void MainWindow::on_loginButton_clicked() { // 获取用户名和密码 QString username = ui->usernameEdit->text(); QString password = ui->passwordEdit->text(); // 向服务器发送登录请求 QJsonObject json; json.insert("type", "login"); json.insert("username", username); json.insert("password", password); QJsonDocument doc(json); QByteArray data = doc.toJson(QJsonDocument::Compact); tcpSocket->write(data); } // 聊天界面槽函数 void MainWindow::on_sendButton_clicked() { // 获取消息内容 QString message = ui->messageEdit->text(); // 向服务器发送消息请求 QJsonObject json; json.insert("type", "message"); json.insert("message", message); QJsonDocument doc(json); QByteArray data = doc.toJson(QJsonDocument::Compact); tcpSocket->write(data); } ``` 在这里,我们使用了Qt中的网络模块,通过TCP socket与服务器进行通讯。在登录请求和消息请求中,我们使用了JSON格式的数据进行传输,方便服务器端进行处理。 在服务器端,我们需要解析客户端发送的请求,并且根据请求类型进行相应的处理。以下是一个简单的示例: ```cpp // 处理客户端请求 void Server::onReadyRead() { QTcpSocket *client = qobject_cast<QTcpSocket *>(sender()); if (client == nullptr) { return; } QByteArray data = client->readAll(); QJsonDocument doc = QJsonDocument::fromJson(data); if (doc.isNull()) { return; } QJsonObject json = doc.object(); QString type = json.value("type").toString(); if (type == "login") { QString username = json.value("username").toString(); QString password = json.value("password").toString(); // 处理登录请求 ... } else if (type == "message") { QString message = json.value("message").toString(); // 处理消息请求 ... } } ``` 在这里,我们使用了Qt中的JSON模块,对客户端发送的请求进行解析。根据请求类型,我们可以进行相应的处理,比如登录验证和消息转发等。 当然,这只是一个简单的实现示例,实际的实现还需要考虑诸多细节问题,比如消息的加密和解密、用户在线状态的处理、异常情况的处理等。同时,服务器端的实现也需要考虑并发性和稳定性等问题,建议使用多线程或者异步IO进行实现

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值