远程函数调用
提交—>init repo
1. mkdir thrift_lesson
2. vim readme.md
git init 初始化仓库
git add .
git commit -m "init repo"
去 ac git 创建项目
链接 && push:
git remote add origin git@git.acwing.com:yyy1004/thrift_lesson2.git
git push -u origin master
创建三个文件夹:匹配端 游戏端 接口
mkdir match_system game thrift
服务端接口 match.thrift
cd thrift
vim match.thrift
# 参考 https://github.com/apache/thrift/blob/master/tutorial/tutorial.thrift
namespace cpp match_service # 命名空间
struct User{
1:i32 id,
2:string name,
3:i32 score
}
service Match{
i32 add_user(1: User user, 2: string info),
i32 remove_user(1: User user, 2: string info),
}
提交—> match thrift
match.thrift 实现服务端接口 match_server
cd match_system
mkdir src
cd src
thrift -r --gen cpp ../../thrift/match.thrift
改名:
mv gen-cpp match_server
mv match_server/Match_server.skeleton.cpp main.cpp
修改(跑通):
vim main.cpp
return 0;
printf("Start Match Server\n");
编译与链接 改错 运行
g++ -c main.cpp match_server/*.cpp
g++ *.o -o main -lthrift -pthread
./main
// 让程序提示正在运行
vim main.cpp
***
#include <iostream>
using namespace std;
cout << "Start Match Server"
***
提交—> match_server
git status
git add .
git restore --stage *.o main
git commit -m "add match server"
git push
去掉*.o *.swp main
gen 客户端 game python
cd game
mkdir src
cd src
thrift -r --gen py ../../thrift/match.thrift
mv gen-py match_client
rm match_client/match/Match-remote # 删掉服务端
vim client.py
# from tutorial import Calculator
# from tutorial.ttypes import InvalidOperation, Operation, Work
from match_client.match import Match
from match_client.match.ttypes import User
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
def main():
# Make socket
transport = TSocket.TSocket('localhost', 9090)
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
# client = Calculator.Client(protocol) # 修改
client = Match.Client(protocol)
# Connect!
transport.open()
user = User(1, "hh", 1500)
client.add_user(user,"")
# Close!
transport.close()
# 新加入
if __name__=="__main__":
main()
- 运行
python3 client.py
提交—> 客户端 初级版 client.py等
完善client.py
vim client.py
from sys import stdin
def operate(op, user_id, username, score):
user = User(user_id, username, score)
if op == "add":
client.add_user(user, "")
elif op == "remove":
client.remove_user(user, "")
# Close!
transport.close()
def main():
for line in stdin:
op, user_id, username, score = line.split(' ')
operate(op, int(user_id), username, int(score))
提交 —> finish match_client
完善match_system main.cpp v2.0(两个线程)
视频55:30
1. 生产者-消费者模型
2. 需要并行
(1)用户不停进来与出去
(2)不停匹配
3.生产者与消费者 媒介 => 消费队列
提交—> match_server v2.0
编写match_client 存储结果
视频1:27:00
服务器函数 save_data(username, password,player1_id, player2_id)
1. 查看存储信息
homework 4 getinfo
- 加密 密码
md5sum
按ctrl + d 结束
- 从git上复制save.thrift(接口文件)
vim save.thrift
:set paste
shift + insert
- 生成 cpp代码
cd mathch_system/src
thrift -r --gen cpp ../../thrift/save.thrift
mv gen-cpp save_client
rm Save_server.skeleton.cpp # 两个main函数 冲突 这是save 的服务段cpp
- 对照官网cpp client端代码修改main.cpp
#include <thrift/transport/TTransportUtils.h>
#include <thrift/transport/TSocket.h>
#include "save_client/Save.h" # 自己的头文件
using namespace ::save_service #命名空间 在match_system/thrift/save.thrift里
main函数里的代码复制过来
加到 save_result
:set paste
shift + insert
:set nopaste
gg=G
-----
std::shared_ptr<TTransport> socket(new TSocket("localhost", 9090));
std::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
CalculatorClient client(protocol);
try {
transport->open();
client.ping();
cout << "ping()" << endl;
cout << "1 + 1 = " << client.add(1, 1) << endl;
Work work;
work.op = Operation::DIVIDE;
work.num1 = 1;
work.num2 = 0;
try {
client.calculate(1, work);
cout << "Whoa? We can divide by zero!" << endl;
} catch (InvalidOperation& io) {
cout << "InvalidOperation: " << io.why << endl;
// or using generated operator<<: cout << io << endl;
// or by using std::exception native method what(): cout << io.what() << endl;
}
work.op = Operation::SUBTRACT;
work.num1 = 15;
work.num2 = 10;
int32_t diff = client.calculate(1, work);
cout << "15 - 10 = " << diff << endl;
// Note that C++ uses return by reference for complex types to avoid
// costly copy construction
SharedStruct ss;
client.getStruct(ss, 1);
cout << "Received log: " << ss << endl;
transport->close();
} catch (TException& tx) {
cout << "ERROR: " << tx.what() << endl;
}
-----
注意:client.py 里的访问地址改成 真实地址
因为在同一个服务器上,所以:127.0.0.1
main.cpp 中的save_result的地址改成myserver的地址 homework 4 getinfo
Calculator => Save
transport->open();与transport->close();之间的都删掉
加入
client.save_data("acs_4880","password", a, b);
调试:
save_data()有返回值
根据返回值判断是否成功!
编译:
g++ -c save_client/*.cpp
g++ -c main.cpp
g++ *.o -o main -lthrift -pthread
去 myserver 查看cat homework/lesson_6/result.txt
优化匹配机制match()(两个简单线程)
每隔一秒匹配一次
如果q里有task 则将task里弹出一个,直至q是 empty()
当q 是空的时候 ,每隔一秒匹配一次。
并且 当分数相差不超过50的时候 才能匹配上。
match()
1. 先排序
2. 匹配相邻的两个玩家
sort(users.begin(), users.end(), [&](User& a, User& b){
return a.score > b.score;
}
for (uint32_t i = 1; i < users.size(); i ++ ){
auto a = users[i - 1], b = users[i];
bool flag = true;
if (b.score - a.score <= 50){
users.erase(users.begin() + i - 1, users.begin() + i + 1); //左闭右开
save_result(a.id, b.id);
flag = false;
break;
}
}
if (flag) break; //防止死循环
lck.unlock();
pool.match();
sleep(1);
提交—> match()优化 update main.cpp match() mul v4.0
由单线程变多线程
每向服务器发送一次请求,都新开一个线程处理 ,比较高效
视频2:00:00
演示地址 github
- 先添加头文件
#include <thrift/concurrency/ThreadManager.h>
#include <thrift/concurrency/ThreadFactory.h>
#include <thrift/TToString.h>
#include <thrift/server/TThreadedServer.h>
- 添加 定义方式
TThreadedServer server(
std::make_shared<CalculatorProcessorFactory>(std::make_shared<CalculatorCloneFactory>()),
std::make_shared<TServerSocket>(9090), //port
std::make_shared<TBufferedTransportFactory>(),
std::make_shared<TBinaryProtocolFactory>());
- 定义工厂
class CalculatorCloneFactory : virtual public CalculatorIfFactory {
public:
~CalculatorCloneFactory() override = default;
CalculatorIf* getHandler(const ::apache::thrift::TConnectionInfo& connInfo) override
{
std::shared_ptr<TSocket> sock = std::dynamic_pointer_cast<TSocket>(connInfo.transport);
cout << "Incoming connection\n";
cout << "\tSocketInfo: " << sock->getSocketInfo() << "\n";
cout << "\tPeerHost: " << sock->getPeerHost() << "\n";
cout << "\tPeerAddress: " << sock->getPeerAddress() << "\n";
cout << "\tPeerPort: " << sock->getPeerPort() << "\n";
return new CalculatorHandler;
}
void releaseHandler( ::shared::SharedServiceIf* handler) override {
delete handler;
}
};
- 格式化代码
:set paste
shift + insert
:set nopaste
gg=G
- 替换: “Calculator” -> “Match”
:1,$s/word1/word2/g 不确认
:n1,n2s/word1/word2/g n1与n2之间 去替换
/gc 确认
- 替换 “::shared::SharedServiceIf”->”MatchIf” ???????
- 查看被监听的端口:netstat -tunple 可以查看目前已经监听的端口
智能匹配(时间)
随着等待的时间增长,配匹的可能性变大
需要记录当前user的等待秒数
在pool里增加
vector<int> wt; 等待秒数
在pool里的add
wt.push_back(0);
在pool的remove
wt.erase(wt.begin() + i );
在match里
去掉排序
for (uint32_t i = 0; i < wt.size(); i ++ )
wt[i] ++; //等待秒数 + 1
while(users.size() > 1){
bool flag = true;
for (uint32_t i = 0; i < users.size(); i ++){
for (uint32_t j = i + 1; j < users.size(); j ++ ){
auto a = users[i], b = users[j];
if(check_match(i, j)){
users.erase(users.begin() + j);
users.erase(users.begin() + i);
wt.erase(wt.begin() + j);
wt.erase(wt.begin() + i);
save_result(i, j);
flag = false;
break;
}
}
if(!flag) break;
}
if(flag) break;
}
删掉 consume_task()里if else 里的pool.match();
bool check_match(uint32_t i, uint32_t j){
auto a = users[i], b = users[j];
int dt = abs(a.score - b.score);
int a_max_dif = wt[i] * 50;
int b_max_dif = wt[j] * 50;
return dt <= a_max_dif && dt <= b_max_dif;
}
测试:
homework 6 test
提交 —> 随着时间推移匹配范围扩大
知识点:
- 条件变量 cpp condition_vairable
- socket 服务器间通信 :CPP zeromq cpzmq
- 并行 CPP openmp
请问下这是 Linux 课还是 Django 课?
是 Django课的