目录
一:主要流程
服务器:
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和添加多线程命令,对于服务器和客户端工程,都需要进行相应操作。