Chương trình máy khách cho dịch vụ echo và chat trong MQL5
Hãy viết một kịch bản đơn giản để kết nối với dịch vụ echo MQL5/Experts/MQL5Book/p7/wsEcho/wsecho.mq5
(lưu ý rằng đây là một kịch bản, nhưng chúng ta đặt nó trong thư mục MQL5/Experts/MQL5Book/p7/
, biến nó thành một bộ chứa duy nhất cho các chương trình MQL liên quan đến web, vì tất cả các ví dụ tiếp theo sẽ là Expert Advisors). Vì trong chương này, chúng ta đang xem xét việc tạo ra các phức hợp phần mềm trong các dự án, chúng ta sẽ thiết kế kịch bản này như một phần của dự án mqproj, bao gồm cả thành phần máy chủ.
Các tham số đầu vào của kịch bản cho phép bạn chỉ định địa chỉ của dịch vụ và nội dung tin nhắn. Mặc định là kết nối không bảo mật. Nếu bạn định khởi chạy máy chủ wsecho.js
với hỗ trợ TLS, bạn cần thay đổi giao thức thành wss
an toàn. Hãy nhớ rằng việc thiết lập kết nối an toàn mất nhiều thời gian hơn (khoảng vài giây) so với thông thường.
input string Server = "ws://localhost:9000/";
input string Message = "My outbound message";
#include <MQL5Book/AutoPtr.mqh>
#include <MQL5Book/ws/wsclient.mqh>
2
3
4
5
Trong hàm OnStart
, chúng ta tạo một thể hiện của máy khách WebSocket (wss
) cho địa chỉ đã cho và gọi phương thức open
. Trong trường hợp kết nối thành công, chúng ta đợi tin nhắn chào mừng từ dịch vụ bằng cách gọi wss.readMessage
ở chế độ chặn (mặc định đợi tối đa 5 giây). Chúng ta sử dụng một con trỏ tự động trên đối tượng kết quả để không phải gọi delete
thủ công ở cuối.
void OnStart()
{
Print("\n");
WebSocketClient<Hybi> wss(Server);
Print("Opening...");
if(wss.open())
{
Print("Waiting for welcome message (if any)");
AutoPtr<IWebSocketMessage> welcome(wss.readMessage());
...
2
3
4
5
6
7
8
9
10
Lớp WebSocketClient
chứa các hàm xử lý sự kiện mẫu, bao gồm phương thức đơn giản onMessage
, sẽ in lời chào ra nhật ký.
Sau đó, chúng ta gửi tin nhắn của mình và lại đợi phản hồi từ máy chủ. Tin nhắn echo cũng sẽ được ghi vào nhật ký.
Print("Sending message...");
wss.send(Message);
Print("Receiving echo...");
AutoPtr<IWebSocketMessage> echo(wss.readMessage());
}
...
2
3
4
5
6
Cuối cùng, chúng ta đóng kết nối.
if(wss.isConnected())
{
Print("Closing...");
wss.close();
}
}
2
3
4
5
6
Dựa trên tệp kịch bản, hãy tạo một tệp dự án (wsecho.mqproj
). Chúng ta điền các thuộc tính dự án với số phiên bản (1.0), bản quyền và mô tả. Hãy thêm các tệp máy chủ dịch vụ echo vào nhánh Settings and Files
(điều này ít nhất sẽ nhắc nhở nhà phát triển rằng có một máy chủ thử nghiệm). Sau khi biên dịch, các phụ thuộc (tệp tiêu đề) sẽ xuất hiện trong hệ thống phân cấp.
Mọi thứ nên trông giống như trong ảnh chụp màn hình dưới đây.
Dự án dịch vụ echo, kịch bản máy khách và máy chủ
Nếu kịch bản nằm trong thư mục Shared Projects
, chẳng hạn như MQL5/Shared Projects/MQL5Book/wsEcho/
, thì sau khi biên dịch thành công, tệp ex5 của nó sẽ tự động được chuyển đến thư mục MQL5/Scripts/Shared Projects/MQL5Book/wsEcho/
, và mục nhập tương ứng sẽ được hiển thị trong nhật ký biên dịch. Đây là hành vi tiêu chuẩn khi biên dịch bất kỳ chương trình MQL nào trong các dự án chia sẻ.
Trong tất cả các ví dụ của chương này, đừng quên khởi động máy chủ trước khi kiểm tra kịch bản MQL. Trong trường hợp này, chạy lệnh: node.exe wsecho.js
trong thư mục web
.
Tiếp theo, hãy chạy kịch bản wsecho.ex5
. Nhật ký sẽ hiển thị các hành động đang diễn ra, cũng như các thông báo tin nhắn.
Opening...
Connecting to localhost:9000
Buffer: 'HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: mIpas63g5xGMqJcKtreHKpSbY1w=
'
Headers:
[,0] [,1]
[0,] "upgrade" "websocket"
[1,] "connection" "Upgrade"
[2,] "sec-websocket-accept" "mIpas63g5xGMqJcKtreHKpSbY1w="
> Connected ws://localhost:9000/
Waiting for welcome message (if any)
> Message ws://localhost:9000/ server#Hello, user1
Sending message...
Receiving echo...
> Message ws://localhost:9000/ user1#My outbound message
Closing...
Close requested
Waiting...
SocketRead failed: 5273 Available: 1
> Disconnected ws://localhost:9000/
Server close ack
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Các tiêu đề HTTP trên là phản hồi của máy chủ trong quá trình bắt tay. Nếu chúng ta nhìn vào cửa sổ bảng điều khiển nơi máy chủ đang chạy, chúng ta sẽ thấy các tiêu đề HTTP mà máy chủ nhận được từ máy khách của chúng ta.
Nhật ký máy chủ dịch vụ echo
Ngoài ra, kết nối của người dùng, tin nhắn và ngắt kết nối cũng được ghi nhận tại đây.
Hãy thực hiện công việc tương tự cho dịch vụ chat: tạo một máy khách WebSocket trong MQL5, một dự án cho nó và thử nghiệm. Lần này, loại chương trình máy khách sẽ là Expert Advisor vì chat cần hỗ trợ các sự kiện tương tác từ bàn phím trên biểu đồ. Expert Advisor được đính kèm với sách trong thư mục MQL5/MQL5Book/p7/wsChat/wschat.mq5
.
Để thể hiện công nghệ nhận sự kiện trong các phương thức xử lý, hãy định nghĩa lớp riêng của chúng ta MyWebSocket
, được kế thừa từ WebSocketClient
.
class MyWebSocket: public WebSocketClient<Hybi>
{
public:
MyWebSocket(const string address, const bool compress = false):
WebSocketClient(address, compress) { }
/* void onConnected() override { } */
void onDisconnect() override
{
// chúng ta có thể làm điều gì đó khác và gọi (hoặc không gọi) mã cũ
WebSocketClient<Hybi>::onDisconnect();
}
void onMessage(IWebSocketMessage *msg) override
{
// TODO: chúng ta có thể cắt bớt bản sao của tin nhắn của chính mình,
// nhưng chúng được giữ lại để gỡ lỗi
Alert(msg.getString());
delete msg;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Khi nhận được tin nhắn, chúng ta sẽ hiển thị nó không phải trong nhật ký mà dưới dạng cảnh báo, sau đó đối tượng cần được xóa.
Trong ngữ cảnh toàn cục, chúng ta mô tả đối tượng của lớp wss
của chúng ta và chuỗi message
nơi đầu vào bàn phím của người dùng sẽ được tích lũy.
MyWebSocket wss(Server);
string message = "";
2
Hàm OnInit
chứa các bước chuẩn bị cần thiết, đặc biệt là khởi động một bộ đếm thời gian và mở kết nối.
int OnInit()
{
ChartSetInteger(0, CHART_QUICK_NAVIGATION, false);
EventSetTimer(1);
wss.setTimeOut(1000);
Print("Opening...");
return wss.open() ? INIT_SUCCEEDED : INIT_FAILED;
}
2
3
4
5
6
7
8
Bộ đếm thời gian là cần thiết để kiểm tra tin nhắn mới từ người dùng khác.
void OnTimer()
{
wss.checkMessages(false); // sử dụng kiểm tra không chặn trong bộ đếm thời gian
}
2
3
4
Trong trình xử lý OnChartEvent
, chúng ta phản hồi các lần nhấn phím: tất cả các phím chữ và số được dịch thành ký tự và gắn vào chuỗi message
. Nếu cần, bạn có thể nhấn Backspace
để xóa ký tự cuối cùng. Tất cả văn bản đã nhập được cập nhật trong bình luận biểu đồ. Khi tin nhắn hoàn tất, nhấn Enter
để gửi nó đến máy chủ.
void OnChartEvent(const int id, const long &lparam, const double &dparam,
const string &sparam)
{
if(id == CHARTEVENT_KEYDOWN)
{
if(lparam == VK_RETURN)
{
const static string longmessage = ...
if(message == "long") wss.send(longmessage);
else if(message == "bye") wss.close();
else wss.send(message);
message = "";
}
else if(lparam == VK_BACK)
{
StringSetLength(message, StringLen(message) - 1);
}
else
{
ResetLastError();
const short c = TranslateKey((int)lparam);
if(_LastError == 0)
{
message += ShortToString(c);
}
}
Comment(message);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Nếu chúng ta nhập văn bản "long", chương trình sẽ gửi một văn bản khá dài đã được chuẩn bị sẵn. Nếu văn bản tin nhắn là "bye", chương trình sẽ đóng kết nối. Ngoài ra, kết nối sẽ được đóng khi chương trình thoát.
void OnDeinit(const int)
{
if(wss.isConnected())
{
Print("Closing...");
wss.close();
}
}
2
3
4
5
6
7
8
Hãy tạo một dự án cho Expert Advisor (tệp wschat.mqproj
), điền vào các thuộc tính của nó và thêm phần phụ trợ vào nhánh Settings and Files
. Lần này chúng ta sẽ cho thấy tệp dự án trông như thế nào từ bên trong. Trong tệp mqproj, nhánh Dependencies
được lưu trong thuộc tính "files", và nhánh Settings and Files
nằm trong thuộc tính "tester".
{
"platform" :"mt5",
"program_type":"expert",
"copyright" :"Copyright 2022, MetaQuotes Ltd.",
"version" :"1.0",
"description" :"WebSocket-client for chat-service.\r\nType and send text messages for all connected users.\r\nShow alerts with messages from others.",
"optimize" :"1",
"fpzerocheck" :"1",
"tester_no_cache":"0",
"tester_everytick_calculate":"0",
"unicode_character_set":"0",
"static_libraries":"0",
"files":
[
{
"path":"wschat.mq5",
"compile":true,
"relative_to_project":true
},
{
"path":"MQL5\\Include\\MQL5Book\\ws\\wsclient.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\MQL5Book\\URL.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\MQL5Book\\ws\\wsframe.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\MQL5Book\\ws\\wstools.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\MQL5Book\\ws\\wsinterfaces.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\MQL5Book\\ws\\wsmessage.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\MQL5Book\\ws\\wstransport.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\MQL5Book\\ws\\wsprotocol.mqh",
"compile":false,
"relative_to_project":false
},
{
"path":"MQL5\\Include\\VirtualKeys.mqh",
"compile":false,
"relative_to_project":false
}
],
"tester":
[
{
"type":"file",
"path":"..\\Web\\MQL5Book.crt",
"relative_to_project":true
},
{
"type":"file",
"path":"..\\Web\\MQL5Book.key",
"relative_to_project":true
},
{
"type":"file",
"path":"..\\Web\\wschat.htm",
"relative_to_project":true
},
{
"type":"file",
"path":"..\\Web\\wschat.js",
"relative_to_project":true
},
{
"type":"file",
"path":"..\\Web\\wschat_client.js",
"relative_to_project":true
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
Nếu Expert Advisor nằm trong thư mục Shared Projects
, chẳng hạn như MQL5/Shared Projects/MQL5Book/wsChat/
, sau khi biên dịch thành công, tệp ex5 của nó sẽ tự động được chuyển đến thư mục MQL5/Experts/Shared Projects/MQL5Book/wsChat/
.
Khởi động máy chủ node.exe wschat.js
. Bây giờ bạn có thể chạy một vài bản sao của Expert Advisor trên các biểu đồ khác nhau. Về cơ bản, dịch vụ này liên quan đến "giao tiếp" giữa các thiết bị đầu cuối khác nhau và thậm chí các máy tính khác nhau, nhưng bạn cũng có thể thử nghiệm nó từ một thiết bị đầu cuối.
Dưới đây là ví dụ về giao tiếp giữa biểu đồ EURUSD và GBPUSD.
(EURUSD,H1)
(EURUSD,H1) Opening...
(EURUSD,H1) Connecting to localhost:9000
(EURUSD,H1) Buffer: 'HTTP/1.1 101 Switching Protocols
(EURUSD,H1) Upgrade: websocket
(EURUSD,H1) Connection: Upgrade
(EURUSD,H1) Sec-WebSocket-Accept: Dg+aQdCBwNExE5mEQsfk5w9J+uE=
(EURUSD,H1)
(EURUSD,H1) '
(EURUSD,H1) Headers:
(EURUSD,H1) [,0] [,1]
(EURUSD,H1) [0,] "upgrade" "websocket"
(EURUSD,H1) [1,] "connection" "Upgrade"
(EURUSD,H1) [2,] "sec-websocket-accept" "Dg+aQdCBwNExE5mEQsfk5w9J+uE="
(EURUSD,H1) > Connected ws://localhost:9000/
(EURUSD,H1) Alert: server#Hello, user1
(GBPUSD,H1)
(GBPUSD,H1) Opening...
(GBPUSD,H1) Connecting to localhost:9000
(GBPUSD,H1) Buffer: 'HTTP/1.1 101 Switching Protocols
(GBPUSD,H1) Upgrade: websocket
(GBPUSD,H1) Connection: Upgrade
(GBPUSD,H1) Sec-WebSocket-Accept: NZENnc8p05T4amvngeop/e/+gFw=
(GBPUSD,H1)
(GBPUSD,H1) '
(GBPUSD,H1) Headers:
(GBPUSD,H1) [,0] [,1]
(GBPUSD,H1) [0,] "upgrade" "websocket"
(GBPUSD,H1) [1,] "connection" "Upgrade"
(GBPUSD,H1) [2,] "sec-websocket-accept" "NZENnc8p05T4amvngeop/e/+gFw="
(GBPUSD,H1) > Connected ws://localhost:9000/
(GBPUSD,H1) Alert: server#Hello, user2
(EURUSD,H1) Alert: user1#I'm typing this on EURUSD chart
(GBPUSD,H1) Alert: user1#I'm typing this on EURUSD chart
(GBPUSD,H1) Alert: user2#Got it on GBPUSD chart!
(EURUSD,H1) Alert: user2#Got it on GBPUSD chart!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Vì tin nhắn của chúng ta được gửi đến mọi người, bao gồm cả người gửi, chúng được nhân đôi trong nhật ký, nhưng trên các biểu đồ khác nhau.
Giao tiếp cũng có thể được nhìn thấy ở phía máy chủ.
Nhật ký máy chủ dịch vụ chat
Bây giờ chúng ta có tất cả các thành phần kỹ thuật để tổ chức dịch vụ tín hiệu giao dịch.