sockets/README.md

295 Zeilen
8,3 KiB
Markdown

2026-03-13 14:02:04 -03:00
# Package sockets
**TCP socket communication library for MQL5 using Winsock2**
*MIT License*
---
## What is this?
`sockets` is a **TCP socket communication library** for MQL5 that wraps the Windows Winsock2 API.
It provides:
- `ClientSocket` class for connecting to TCP servers
- `ServerSocket` class for creating TCP servers
- Non-blocking I/O operations
- Message framing with customizable separators
- Binary and text data transmission
- Error handling without Print() calls
- Automatic hostname resolution (DNS)
- Support for both 32-bit and 64-bit MetaTrader 5
This package enables MQL5 programs to communicate over TCP/IP networks, making it possible to integrate Expert Advisors, Scripts, and Services with external applications, databases, and web services.
## Prerequisites
To use this project, you will need:
1. [**MetaTrader 5**](https://www.mql5.com/) installed.
2. **KnitPkg CLI**: The KnitPkg package manager for MetaTrader. If you don't have it, you can install it by following the instructions in the [main KnitPkg repository](https://github.com/knitpkg-dev/knitpkg-mt.git).
3. **KnitPkg homepage**: See [https://knitpkg.dev](https://knitpkg.dev) for an overview and [https://docs.knitpkg.dev](https://docs.knitpkg.dev) for documentation.
---
## Features
- **Package type** (`type: package`) — header-only library consumable by other KnitPkg projects
- **MQL5 target** — designed for MetaTrader 5
- **Compile support** — `kp compile` compiles the unit test script(s)
- **TCP client sockets** — connect to remote servers from EAs and Scripts
- **TCP server sockets** — create servers in MQL5 Services
- **Non-blocking I/O** — all operations are non-blocking for responsive applications
- **Message framing** — built-in support for message delimiters (e.g., newline)
- **Binary and text data** — send/receive both string and binary data
- **Error handling** — retrieve error messages via GetLastError() without Print() calls
- **DNS resolution** — automatic hostname to IP address resolution
- **Cross-architecture** — works on both 32-bit and 64-bit MT5 terminals
---
## Installing as a dependency (recommended)
To add `sockets` to your KnitPkg project:
```bash
kp add @douglasrechia/sockets
```
---
## Basic usage (example)
In the case your MQL5 project uses `include_mode: include`, include headers from the KnitPkg include directory:
```mql5
#include "../knitpkg/include/douglasrechia/sockets/Sockets.mqh"
```
### Client Socket Example
```mql5
void OnStart()
{
// Connect to server
douglasrechia::ClientSocket* client = new douglasrechia::ClientSocket("127.0.0.1", 8080);
if(!client.IsSocketConnected())
{
Print("Connection failed: ", client.GetLastError());
delete client;
return;
}
// Send a message
if(!client.Send("Hello Server\n"))
{
Print("Send failed: ", client.GetLastError());
}
// Receive response (with newline separator)
string response = "";
while(response == "" && client.IsSocketConnected())
{
response = client.Receive("\n");
Sleep(10);
}
Print("Server response: ", response);
// Cleanup
delete client;
}
```
### Server Socket Example
```mql5
douglasrechia::ServerSocket* server;
douglasrechia::ClientSocket* clients[];
void OnStart()
{
// Create server on port 8080
server = new douglasrechia::ServerSocket(8080);
if(!server.Created())
{
Print("Server creation failed: ", server.GetLastError());
return;
}
Print("Server listening on port 8080");
// Main server loop
while(!IsStopped())
{
// Accept new connections
douglasrechia::ClientSocket* newClient = server.Accept();
if(newClient != NULL)
{
Print("New client connected");
int size = ArraySize(clients);
ArrayResize(clients, size + 1);
clients[size] = newClient;
}
// Process existing clients
for(int i = ArraySize(clients) - 1; i >= 0; i--)
{
string msg = clients[i].Receive("\n");
if(msg != "")
{
Print("Received: ", msg);
// Echo back
clients[i].Send("Echo: " + msg + "\n");
}
// Remove disconnected clients
if(!clients[i].IsSocketConnected())
{
delete clients[i];
ArrayRemove(clients, i, 1);
}
}
Sleep(10);
}
// Cleanup
for(int i = 0; i < ArraySize(clients); i++)
delete clients[i];
delete server;
}
```
> Note: The exact method signatures and usage patterns are documented in the header files. Use MetaEditor IntelliSense to browse the available APIs.
---
## Exploring the project locally (optional)
If you want to explore how a KnitPkg package is structured, clone the repository and run the standard workflow.
### 1) Clone into MetaTrader `Scripts` folder (example)
```bash
# Go to your MetaTrader 'Scripts' directory.
# Tip: in MetaEditor, right-click the `Scripts` folder and choose "Open Folder".
cd "C:\Users\username\AppData\Roaming\MetaQuotes\Terminal\<TERMINAL_ID>\MQL5\Scripts"
git clone https://forge.mql5.io/DouglasRechia/sockets.git
cd sockets
```
### 2) Compile
```bash
kp compile
```
---
## Running unit tests
Unit tests are implemented as a MetaTrader **Script**.
1. Compile the project.
2. Restart MetaTrader (so the Navigator refreshes).
3. Run the generated unit test script from the Navigator on any chart.
4. Check results in the MetaTrader console (Experts/Scripts tab, depending on your setup).
The compiled binary is placed under the project `bin/` directory (and appears in the Navigator under the corresponding compiled location after refresh).
---
## One-command download/build via the registry (optional)
You can also use `kp get` to query the registry for metadata and automatically download/build the latest stable version.
Example (run from your MetaTrader *Data Folder* root):
```bash
cd "C:\Users\username\AppData\Roaming\MetaQuotes\Terminal\<TERMINAL_ID>"
kp get mql5 @douglasrechia/sockets
```
Restart MetaTrader afterward to refresh the Navigator, then run the unit test script as described above.
---
## API Reference
### ClientSocket
#### Constructor
```cpp
ClientSocket(string serverHost, ushort serverPort)
```
Creates a client socket and connects to the specified server.
#### Methods
| Method | Description |
|--------|-------------|
| `bool IsSocketConnected()` | Returns true if socket is connected |
| `string GetLastError()` | Returns the last error message |
| `void ClearLastError()` | Clears the last error message |
| `void CloseSocket()` | Closes the socket connection |
| `bool Send(string strMsg)` | Sends a UTF-8 string |
| `bool Send(uchar& buffer[], int startAt=0, int szToSend=-1)` | Sends binary data |
| `string Receive(string MessageSeparator="")` | Receives string data with optional message separator |
| `int Receive(uchar& buffer[])` | Receives binary data, returns number of bytes |
### ServerSocket
#### Constructor
```cpp
ServerSocket(ushort serverPort, bool reuse=true)
```
Creates a server socket listening on the specified port.
#### Methods
| Method | Description |
|--------|-------------|
| `bool Created()` | Returns true if server was created successfully |
| `string GetLastError()` | Returns the last error message |
| `void ClearLastError()` | Clears the last error message |
| `void CloseServer()` | Closes the server socket |
| `ClientSocket* Accept()` | Accepts a pending connection, returns NULL if none waiting |
---
## Message Framing
TCP is stream-oriented, not message-oriented. The `MessageSeparator` parameter solves the message framing problem:
```cpp
// Without separator - returns all buffered data (may be incomplete)
string data = client.Receive("");
// With separator - returns only complete messages
string message = client.Receive("\n"); // Waits for newline
```
When using a separator:
- Data accumulates in an internal buffer until the separator is found
- Returns the first complete message (without the separator)
- Remaining data stays buffered for the next call
- Returns empty string if message is incomplete
---
## License
This project is released under the **MIT License**. See `LICENSE` for details.
---
## Disclaimer
This code is provided **as-is**, for **educational purposes only**.
No warranty (express or implied) is provided. Use at your own risk.