#property strict#property show_inputs
// ---------------------------------------------------------------------// User-configurable parameters// ---------------------------------------------------------------------inputint PortNumber = 51234; // TCP/IP port number// ---------------------------------------------------------------------// Constants// ---------------------------------------------------------------------// Size of temporary buffer used to read from client sockets#define SOCKET_READ_BUFFER_SIZE 10000// ---------------------------------------------------------------------// Forward definitions of classes// ---------------------------------------------------------------------// Wrapper around a connected client socketclass Connection;
// ---------------------------------------------------------------------// Winsock structure definitions and DLL imports// ---------------------------------------------------------------------struct sockaddr_in {
short af_family;
short port;
int addr;
int dummy1;
int dummy2;
};
struct timeval {
int secs;
int usecs;
};
struct fd_set {
int count;
int single_socket;
int dummy[63];
};
#import "Ws2_32.dll"int socket(int, int, int);
int bind(int, sockaddr_in&, int);
int htons(int);
int listen(int, int);
int accept(int, int, int);
int closesocket(int);
int select(int, fd_set&, int, int, timeval&);
int recv(int, uchar&[], int, int);
int WSAGetLastError();
#import
// ---------------------------------------------------------------------// Global variables// ---------------------------------------------------------------------// Handle of main listening server socketint ServerSocket;
// List of currently connected clients
Connection * Clients[];
// ---------------------------------------------------------------------// Entry point // ---------------------------------------------------------------------voidOnStart()
{
if (!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)) {Print("Requires \'Allow DLL imports\'");return;}
// (Don't need to call WSAStartup because MT4 must have done this)// Create the main server socket
ServerSocket = socket(2/* AF_INET */, 1/* SOCK_STREAM */, 6/* IPPROTO_TCP */);
if (ServerSocket == -1) {Print("ERROR " , WSAGetLastError() , " in socket creation");return;}
// Bind the socket to the specified port number. In this example,// we only accept connections from localhost
sockaddr_in service;
service.af_family = 2/* AF_INET */;
service.addr = 0x100007F; // equivalent to inet_addr("127.0.0.1")
service.port = (short)htons(PortNumber);
if (bind(ServerSocket, service, 16/* sizeof(service) */) == -1) {Print("ERROR " , WSAGetLastError() , " in socket bind");return;}
// Put the socket into listening modeif (listen(ServerSocket, 0) == -1) {Print("ERROR " , WSAGetLastError() , " in socket listen");return;}
// Listening loop, which continues until Remove Script is used
timeval waitfor;
waitfor.secs = 0;
waitfor.usecs = 0;
while (!IsStopped()) {
// .........................................................// Do we have a new pending connection on the server socket?
fd_set PollServerSocket;
PollServerSocket.count = 1;
PollServerSocket.single_socket = ServerSocket;
int selres = select(0, PollServerSocket, 0, 0, waitfor);
if (selres > 0) {
Print("New incoming connection...");
int NewClientSocket = accept(ServerSocket, 0, 0);
if (NewClientSocket == -1) {
Print("ERROR " , WSAGetLastError() , " in socket accept");
} else {
Print("...accepted");
int ctarr = ArraySize(Clients);
ArrayResize(Clients, ctarr + 1);
Clients[ctarr] = new Connection(NewClientSocket);
Print("Got connection to client ", Clients[ctarr].GetID());
}
}
// .........................................................// Process any incoming data from client connections// (including any which have just been accepted, above)int ctarr = ArraySize(Clients);
for (int i = ctarr - 1; i >= 0; i--) {
// Return value from ReadAnyPendingData() is true// if the socket still seems to be alive; false if // the connection seems to have been closed, and should be discardedif (Clients[i].ReadAnyPendingData()) {
// Socket still seems to be alive
} else {
// Socket appears to be dead. Delete, and remove from listPrint("Lost connection to client ", Clients[i].GetID());
delete Clients[i];
for (int j = i + 1; j < ctarr; j++) {
Clients[j - 1] = Clients[j];
}
ctarr--;
ArrayResize(Clients, ctarr);
}
}
Sleep(10); // Sleep(1) appears to be a little too aggressive in this context
}
}
// ---------------------------------------------------------------------// Termination (could do this at the end of OnStart() instead.// It's just a little clearer to do it here// ---------------------------------------------------------------------voidOnDeinit(constint reason)
{
closesocket(ServerSocket);
for (int i = 0; i < ArraySize(Clients); i++) {
delete Clients[i];
}
}
// ---------------------------------------------------------------------// Simple wrapper around each connected client socket// ---------------------------------------------------------------------class Connection {
private:
// Client socket handleint mSocket;
// Temporary buffer used to handle incoming datauchar mTempBuffer[SOCKET_READ_BUFFER_SIZE];
// Stored-up data, waiting for a \r character string mPendingData;
public:
Connection(int ClientSocket) {mSocket = ClientSocket; mPendingData = "";}
~Connection() {closesocket(mSocket);}
string GetID() {returnIntegerToString(mSocket);}
bool ReadAnyPendingData();
};
// Called repeatedly on a timer from OnStart(), to check whether any// data is available on this client connection. Returns true if the // client still seems to be connected (*not* if there's new data); // returns false if the connection seems to be dead. bool Connection::ReadAnyPendingData()
{
// Check the client socket for data-readability
timeval waitfor;
waitfor.secs = 0;
waitfor.usecs = 0;
fd_set PollClientSocket;
PollClientSocket.count = 1;
PollClientSocket.single_socket = mSocket;
int selres = select(0, PollClientSocket, 0, 0, waitfor);
if (selres > 0) {
// Winsock says that there is data waiting to be read on this socketint res = recv(mSocket, mTempBuffer, SOCKET_READ_BUFFER_SIZE, 0);
if (res > 0) {
// Convert the buffer to a string, and add it to any pending// data which we already have on this connectionstring strIncoming = CharArrayToString(mTempBuffer, 0, res);
mPendingData += strIncoming;
// Do we have a complete message (or more than one) ending in \r?int idxTerm = StringFind(mPendingData, "\r");
while (idxTerm >= 0) {
if (idxTerm > 0) {
string strMsg = StringSubstr(mPendingData, 0, idxTerm);
// Print the \r-terminated message in the logPrint("#" , GetID() , ": " , strMsg);
}
// Keep looping until we have extracted all the \r delimited // messages, and leave any residue in the pending data
mPendingData = StringSubstr(mPendingData, idxTerm + 1);
idxTerm = StringFind(mPendingData, "\r");
}
returntrue;
} else {
// recv() failed. Assume socket is deadreturnfalse;
}
} elseif (selres == -1) {
// Assume socket is deadreturnfalse;
} else {
// No pending datareturntrue;
}
}
确实如此。谢谢。我不记得了。事实上,这并不能完全解决问题,因为我需要订单几乎立即被执行(我想用这种方法我只能每秒检查一次,但至少不是每一次勾选),但确实我可以用它轻松建立一个临时解决方案。
虽然OnTimer有最快的速率与系统时钟对齐,即每15ms,但我更喜欢以1ms的间隔循环(在循环中使用Sleep(1))。
虽然OnTimer有最快的速率与系统时钟对齐,即每15ms,但我更喜欢以1ms的间隔循环(在循环中使用Sleep(1))。
这不会是个问题吗?我的意思是,在MT4中没有内部进程可能需要执行OnTick(),以避免某种内部缓冲区在运行(EA)数周后溢出或过大,而不停止和只是循环?
我是在无知的情况下问的,我不知道这是否会成为一个问题,只是假设MT4的设计不是为了做这个。
所以你认为(/已经尝试)这不会是一个问题?(如果是这样,那就是好消息了)。
BTW:你如何让OnTimer() 在1秒的间隔内执行?
几年前,我曾经在init()中开始循环运行EA,原因类似--以最小的延迟读取外部数据。它曾经运行了数周而没有任何副作用。Sleep(1)是令人满意的,它使计算能力 的影响非常小。
定时器设置接受毫秒。
几年前,我曾经在init()中开始循环运行EA,原因类似--以最小的延迟读取外部数据。它曾经运行了数周而没有任何副作用。Sleep(1)是令人满意的,它使计算能力的影响非常小。
定时器设置接受毫秒。
BTW: 如何让OnTimer()在1秒的间隔以下执行? :?
事件SetMillisecondTimer
该函数向客户终端表明,该专家顾问或指标的定时器 事件应以小于一秒的间隔产生。
bool EventSetMillisecondTimer(
int milliseconds// milliseconds数
);
参数
毫秒
[定义定时器事件的频率的毫秒数。
返回值
在成功执行的情况下,返回true,否则 - false。要接收一个错误代码,应该调用GetLastError() 函数。
注意
这个功能是为 需要高分辨率定时器 的情况而设计的 。换句话说, 定时器事件的接收应该比每秒一次更频繁。如果一个周期为几秒的传统定时器对你来说已经足够了,请使用EventSetTimer()。
通常,这个函数应该从OnInit() 函数或在类构造函数 中调用。为了处理来自定时器的事件,专家顾问或指标应该有OnTimer() 函数。
每个专家顾问和每个指标都有自己的定时器,只从这个定时器接收事件。 在mql4应用程序关闭期间,如果定时器已经创建但没有被EventKillTimer() 函数禁用,那么它将被强行销毁。
每个程序只能启动一个定时器。每个mql4程序和图表都有自己的事件队列,所有新到的事件都放在那里。如果队列中已经包含了Timer 事件或者该事件处于处理阶段,那么新的Timer事件就不会被添加到mql4应用程序队列中。
事件SetMillisecondTimer
该函数向客户终端指出,该专家顾问或指标的定时器 事件应以小于一秒的间隔产生。
bool EventSetMillisecondTimer(
int milliseconds// milliseconds数量
);
参数
毫秒
[定义定时器事件的频率的毫秒数。
返回值
在成功执行的情况下,返回true,否则返回false。要接收一个错误代码,应该调用GetLastError() 函数。
注意
这个功能是为 需要高分辨率的定时器 的情况而设计的 。换句话说, 定时器事件的接收频率应该超过每秒一次。如果一个周期为几秒钟的传统定时器对你来说已经足够了,请使用EventSetTimer()。
通常,这个函数应该在OnInit() 函数或类的构造函数 中调用。为了处理来自计时器的事件,专家顾问或指标应该有OnTimer() 函数。
每个专家顾问和每个指标都有自己的定时器,只从这个定时器接收事件。在mql4应用程序关闭期间,如果定时器已经创建,但没有被EventKillTimer() 函数禁用,则会被强行销毁。
每个程序只能启动一个定时器。每个mql4程序和图表都有自己的事件队列,所有新到的事件都放在那里。如果队列中已经包含了Timer 事件或者这个事件处于处理阶段,那么新的Timer事件就不会被添加到mql4应用程序队列中。
谢谢你的详细解释;我的错;我在网上读得太快了。(链接已将docs.mql4.com 域名与forum.mql4.com 互换。以防有人检查这个线程...)
编辑:我想利用版主在身边的机会。你知道每隔N个毫秒执行OnTimer()和之前帖子中描述的每隔N个毫秒做同样的循环是否存在任何区别?
我不明白为什么对 "毫秒 "计时如此大惊小怪。除非这是在非常低延迟 的经纪商连接上的某种"高频新闻缩放器",否则OnTimer() 的一秒间隔作为响应时间应该是绰绰有余的。
如果你把JAVA应用程序的执行、IPC(局域网和广域网)通信、订单管理的执行和经纪商服务器的延迟加在一起,对于正常的策略来说,使用 "ms "计时并没有真正的优势。
但是,如果是某种"高频新闻缩放器",在VPS上使用,与经纪商的连接延迟较低,那么你就不应该搞什么JAVA和IPC,而应该直接用非常紧凑的MQL编码策略,以获得尽可能低的延迟。
编辑。
编辑:我想利用版主在身边的机会。你知道每隔N毫秒执行一次OnTimer()和之前帖子中描述的每隔N毫秒做一次同样的循环是否存在任何区别?
仅仅因为我是版主,这并不意味着我比其他人更了解情况。事实上,这个主题中的其他发帖人显然比我更了解这个主题。我对Java一无所知。
我不明白在定时器事件中使用Sleep() 的意义,因为它使EA处于暂停状态,这可能意味着错过很多OnTick()事件。当然,根据EA的情况,OnTick事件可能并不重要。
几年前,我曾经在init()中开始循环运行EA,原因类似--以最小的延迟读取外部数据。它曾经运行了数周而没有任何副作用。Sleep(1)是令人满意的,它使计算能力的影响非常小。
为了好玩......下面的脚本接受多个并发的TCP/IP连接,并将传入的以CR为分隔符的信息写到专家日志中。例如,一旦脚本运行,你可以通过Telnet(默认为51234端口)连接,你输入的每一行文字都会被打印出来。
为了好玩......下面的脚本接受多个并发的TCP/IP连接,并将收到的以CR为分隔符的信息写到专家日志中。
......显而易见,如果你想在EA中而不是在脚本中完成上述工作,那么你只需按以下方式进行修改。