【Linux网络编程】- 聊天系统 1.0版本

目录

一:主要流程​​​​​​​

二:结果演示

三:完整代码


一:主要流程​​​​​​​

服务器:

1.socket函数 网络初始化(判断网络是否可行)

2.bind函数 网络IP地址 端口号绑定

3.listen函数 端口监听是否有客户端连接
4.accept函数 做客户端连接动作

5.每一个acceptfd(连接成功的客户端)都有一个线程 为其服务[创建线程]

6.read读取客户端请求

7.业务分析

8.write结果

客户端:

1.socket函数 网络初始化(判断网络是否可行)

2.connect函数 给一个ip地址和端口号,主动连接服务器

3.创建两个线程 一个线程复杂度 一个线程负责写 必须实现读和写操作不能相互影响 读写必须分开执行  读和写 异步处理

4.读到服务器反馈结果 做相应UI显示处理

自定义通信协议 :定长包头 + 不定长包体 

读两次 = 第一次读头 根据头的类型再读一次 体

发一次=利用内存拷贝将头和体结构体拷贝一个char buf[]数组

服务器:读请求 分析请求并处理 写反馈结果

客户端:发送请求 读取反馈结果 UI显示结果 

二:结果演示

首先打开服务器

开启两个客户端,分别输入账号和密码 

服务器上可以看到用户数量为2 

输入接收者账号后,可以发送消息,如左边窗口,输入1002,输入 你好。

右边窗口可以看到 :1001对我说 你好

同理,右边窗口输入接收者账号后,也可以发送消息,如右边窗口,输入1001,输入 你也好

左边窗口就可以看到:1002对我说 你也好

当然,一人也可以发送多条信息,模拟现实中的QQ聊天

三:完整代码

服务器:

#include<iostream>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<map>
#include"protocol.h"

using namespace std;

map<int, int> onlineUser;

void* pthread_function(void* pv)
{
	char buf[300] = { 0 };
	HEAD head = { 0 };
	USER user = { 0 };
	BACKMSG back = { 0 };
	CHATMSG msg = { 0 };
	int fd = *(int*)pv;

	while (1)
	{
		int res = read(fd, &head, sizeof(HEAD));
		cout << "服务器读头 收到res = " << res << endl;

		if (head.businessType == 1)
		{
			res = read(fd, &user, head.businessLeg);
			cout << "服务器读头 收到res = " << res << endl;

			//正常需要执行数据库的查询操作判断账号密码是否正确,这里暂且省略
			//这里模仿已经登录成功,保存用户
			onlineUser[user.name] = fd;
			cout <<"onlineUser.size = " << onlineUser.size() << endl;

			bzero(&head, sizeof(HEAD));

			back.flag = 1;
			strcpy(back.message, "登录成功");

			head.businessType = 1;
			head.businessLeg = sizeof(BACKMSG);

			memcpy(buf, &head, sizeof(HEAD));
			memcpy(buf + sizeof(HEAD), &back, sizeof(BACKMSG));

			res = write(fd, buf, sizeof(HEAD) + sizeof(BACKMSG));
			cout << "服务器登录反馈 res = " << res << endl;
			bzero(buf, sizeof(buf));
		}
		else if(head.businessType == 2)
		{
			res = read(fd, &msg, head.businessLeg);
			cout << "服务器读头 收到res = " << res << endl;
			cout << "接收者: fd = " << onlineUser[msg.rcvid] << endl;

			bzero(&head, sizeof(HEAD));

			head.businessType = 2;
			head.businessLeg = sizeof(CHATMSG);

			memcpy(buf, &head, sizeof(HEAD));
			memcpy(buf + sizeof(HEAD), &msg, sizeof(CHATMSG));

			res = write(onlineUser[msg.rcvid],buf,sizeof(HEAD)+ sizeof(CHATMSG));
			cout << "服务器发送给接收者:res = " << res << endl;
			res = write(fd, buf, sizeof(HEAD) + sizeof(CHATMSG));
			cout << "服务器发送给发送者:res = " << res << endl;
		}
	}
}

int main()
{
	pthread_t threadid;
	pid_t pid = 0;
	char buf[50] = { 0 };
	struct sockaddr_in addr;
	int len = 0;
	int acceptfd = 0;

	//初始化网络 识别当前计算机是否可以联网
	//第一个参数:采用IPV4 IP地址 第二个参数:网络分配TCP 
	int socketfd = socket(AF_INET, SOCK_STREAM, 0);
	if (socketfd == -1)
	{
		perror("socket error");
	}
	else
	{
		cout << "socketfd = " << socketfd << endl;
		//确定用IPV4地址
		addr.sin_family = AF_INET;
		//服务器开放自己的IP地址给客户端连接使用   INADDR_ANY生成默认的可以联网的IP地址
		addr.sin_addr.s_addr = INADDR_ANY;
		//绑定服务器端口号0-65535  10000以下系统默认使用
		addr.sin_port = htons(10086);

		len = sizeof(addr);

		//bind 绑定ip地址 绑定端口号
		if (bind(socketfd, (struct sockaddr*)&addr, len) == -1)
		{
			perror("bind error");
		}

		if (listen(socketfd, 10) == -1)
		{
			perror("listen error");
		}

		cout << "网络搭建成功" << endl;

		while (1)
		{
			cout << "服务器等待客户端连接........." << endl;
			//服务器等待客户端连接 阻塞式函数 acceptfd在服务器代表已经连接成功的客户端
			acceptfd = accept(socketfd, NULL, NULL);

			cout << "有客户端成功连接 acceptfd = " << acceptfd << endl;

			pthread_create(&threadid, NULL, pthread_function, &acceptfd);
			
		}
	}
	return 0;
}

自定义通信协议

#pragma once
//通用的通信协议 头
typedef struct head
{
	int businessType;//业务类型
	int businessLeg;//协议体长度
}HEAD;

typedef struct user
{
	int name;//账号
	char pass[20];//密码
}USER;

typedef struct chatmsg
{
	int sendid;//聊天发送者
	int rcvid;//聊天接收者
	char contenxt[150];//聊天内容
}CHATMSG;

typedef struct backmsg
{
	int flag;//业务结果
	char message[20];//业务反馈内容
}BACKMSG;

客户端:

#include<iostream>
#include <sys/types.h>         
#include <sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include"protocol.h"

using namespace std;

bool isLogin = false;
int self = 0;

void* read_function(void* pv)
{
	HEAD head = { 0 };
	BACKMSG back = { 0 };
	CHATMSG msg = { 0 };
	int fd = *(int*)pv;
	while (1)
	{
		int res = read(fd,&head,sizeof(HEAD));
		cout << "客户端读头 res = " << res << endl;

		if (head.businessType == 1)
		{
			res = read(fd, &back, sizeof(BACKMSG));
			cout << "客户端读体 res = " << res << endl;
			cout << "客户端读到登录结果 back.message = " << back.message << endl;
			isLogin = true;
		}
		else if (head.businessType == 2)
		{
			res = read(fd, &msg, sizeof(CHATMSG));
			cout << "客户端读体 res = " << res << endl;

			if (msg.sendid == self)
			{
				cout << "我说:" << msg.contenxt << endl;
			}
			else if(msg.rcvid == self)
			{
				cout << msg.sendid << "对我说:" << msg.contenxt << endl;
			}
		}
	}
}

void* write_function(void* pv)
{
	char buf[300] = { 0 };
	int fd = *(int*)pv;
	HEAD head = { 0 };
	USER user = { 0 };
	CHATMSG msg = { 0 };
	int res = 0;
	while (1)
	{
		if (isLogin == false)
		{
			cout << "请输入账号:" << endl;
			cin >> user.name;
			cout << "请输入密码:" << endl;
			cin >> user.pass;

			self = user.name;

			head.businessType = 1;
			head.businessLeg = sizeof(user);

			memcpy(buf, &head,sizeof(HEAD));
			memcpy(buf+ sizeof(HEAD), &user, sizeof(USER));

			res = write(fd, buf, sizeof(HEAD) + sizeof(USER));
			cout << "客户端发送 res = " << res << endl;
			bzero(buf, sizeof(buf));

			cout << "可以开始聊天" << endl;
		}
		else if(isLogin == true)
		{
			bzero(&head, sizeof(HEAD));
			//cout << "请输入发送者账号:" << endl;
			//cin >> msg.sendid;
			msg.sendid = self;
			cout << "请输入接收者账号:" << endl;
			cin >> msg.rcvid;
			cout << "请输入内容:" << endl;
			cin >> msg.contenxt;

			head.businessType = 2;
			head.businessLeg = sizeof(CHATMSG);

			memcpy(buf, &head, sizeof(HEAD));
			memcpy(buf + sizeof(HEAD), &msg, sizeof(CHATMSG));

			res = write(fd, buf, sizeof(HEAD) + sizeof(CHATMSG));
			cout << "客户端发送 res = " << res << endl;
			bzero(buf, sizeof(buf));
		}
		sleep(2);
	}
}

int main()
{
	pthread_t readthread = 0;
	pthread_t wrtiethread = 0;

	struct sockaddr_in addr;
	int len = 0;
	//初始化网络 识别当前计算机是否可以联网
	//第一个参数:采用IPV4 IP地址 第二个参数:网络分配TCP 
	int socketfd = socket(AF_INET, SOCK_STREAM, 0);
	cout << "客户端 socketfd = " << socketfd << endl;
	if (socketfd == -1)
	{
		perror("socket error");
	}
	else
	{
		//确定用IPV4地址
		addr.sin_family = AF_INET;
		//客户端主动寻找服务器IP地址 127.0.0.1本机回环地址 192.168.75.128
		addr.sin_addr.s_addr = inet_addr("192.168.75.128");
		//绑定服务器端口号0-65535  10000以下系统默认使用
		addr.sin_port = htons(10086);

		len = sizeof(addr);

		//主动去连接服务器 IP和端口
		if (connect(socketfd, (struct sockaddr*)&addr, len) == -1)
		{
			perror("connect error");
		}
		else
		{
			cout << "客户端连接服务器成功" << endl;
		}

		pthread_create(&readthread, NULL, read_function, &socketfd);
		pthread_create(&wrtiethread, NULL, write_function, &socketfd);

		while (1)
		{
		}
	}

	return 0;
}

自定义通信协议

#pragma once
//通用的通信协议 头
typedef struct head
{
	int businessType;//业务类型
	int businessLeg;//协议体长度
}HEAD;

typedef struct user
{
	int name;//账号
	char pass[20];//密码
}USER;

typedef struct chatmsg
{
	int sendid;//聊天发送者
	int rcvid;//聊天接收者
	char contenxt[150];//聊天内容
}CHATMSG;

typedef struct backmsg
{
	int flag;//业务结果
	char message[20];//业务反馈内容
}BACKMSG;

服务器和客户端 .h文件是一样的。

注意点:VS默认不识别多线程,需要右键工程属性,链接器 - 命令行 - pthread ,才可以正常运行程序,对于自定通信协议.h和添加多线程命令,对于服务器和客户端工程,都需要进行相应操作。 

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125884326