Not able to get poco 1.6.1 or 1.7.6 to bind to ipv6 with Net::DatagramSocket - poco-libraries

Using the following code I can bind to an ipv4 address but not to a scope global ipv6 address that is also bound to this same machine. I am compiling the code like this:
g++ -lPocoFoundation -lPocoXML -lPocoUtil -lPocoNet -lcrypto -lssl -I/usr/include/Poco -o pocoudpipv6 pocoudpipv6.cpp
When I execute ./pocoudpipv6 10.X.X.X, it holds open the socket and cycles on "Waiting..." until I hit ctrl-c, which is expected. ss reports the socket:
# ss -nelup |grep 20000
UNCONN 0 0 10.X.X.X:20000 *:* users:(("pocoudpipv6",pid=2444,fd=3)) uid:1000 ino:14526705 sk:2a <->
But when I execute with ./pocoudpipv6 2001:X:X:X::X:X, this occurs:
We're resetting the ipaddress from ::1 to 2001:X:X:X::X:X
Address family is ipv6
Failure launching. Error was Net Exception: Address family not supported
This problem occurs with 1.7.6 on Slackware64 14.2 as well as with 1.6.1 on Debian 8 jessie amd64. As far as I've read, ipv6 being enabled in Poco is supposed to be the default. Is there something else I need to do in order to get this test-case to work with ipv6?
And I do have at least one daemon that is binding to an ipv6 socket on this machine:
udp UNCONN 0 0 2001:X:X:X::X:X:123 :::* users:(("ntpd",pid=2133,fd=22)) ino:5247 sk:19 v6only:1 <->
Thanks in advance!
Code as follows:
#include <iostream>
#include <Net/DatagramSocket.h>
#include <Net/SocketAddress.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
struct sigaction sigact = { 0 };
struct io_handling {
uint8_t exit_value;
};
struct io_handling io_handler = { 0 };
static void sigint_signal_handler(int sig)
{
if(sig == SIGINT) {
std::cout << std::endl << "Commencing shutdown in 5... 4... 3... 2... 1..." << std::endl;
io_handler.exit_value = 1;
}
}
static void cleanup(void)
{
sigemptyset(&sigact.sa_mask);
}
int main(int argc, char **argv)
{
Poco::Net::DatagramSocket *pSocket = new Poco::Net::DatagramSocket();
Poco::UInt16 port = 20000;
Poco::Net::IPAddress *ipAddress = new Poco::Net::IPAddress("::1");
if(argc == 2) {
delete ipAddress;
ipAddress = new Poco::Net::IPAddress(argv[1]);
std::cout << std::endl << "We're resetting the ipaddress from ::1 to " << argv[1];
}
std::cout << std::endl << "Address family is ";
if(ipAddress->family() == static_cast<Poco::Net::IPAddress::Family>(Poco::Net::Impl::IPAddressImpl::IPv6)) {
std::cout << "ipv6 ";
} else if(ipAddress->family() == static_cast<Poco::Net::IPAddress::Family>(Poco::Net::Impl::IPAddressImpl::IPv4)) {
std::cout << "ipv4 ";
} else {
std::cout << "something else, something very wrong.";
}
try {
sigact.sa_handler = sigint_signal_handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGINT, &sigact, (struct sigaction *)NULL);
pSocket->bind(Poco::Net::SocketAddress(*ipAddress, port));
while(!io_handler.exit_value) {
sleep(1);
std::cout << std::endl << "Waiting...";
}
} catch(Poco::Exception& ex) {
std::cout << std::endl << "Failure launching. Error was " << ex.displayText() << std::endl;
}
delete pSocket;
delete ipAddress;
return 0;
}

Self-resolved. The lib I was working with was only calling up Poco::Net::DatagramSocket's default ctor, which then gives an ipv4-only socket. Calls to ::bind() will not reset that based upon the input ip address.
The solution is to not new pSocket until handling ipAddress in the example above, and then passing that and the port casted as Poco::Net::SocketAddress to the ctor, and it will create the proper socket type and automatically bind it.

Related

QSerial without QThreads

I create a "server" lib with a Qt GUI.
I don't and can't use QThreads because this is supposed to be as independent from Qt as possible, and because I have other threads already working like a Ethernet part.
The thread ExternalRs232Thread() is lauched by the public function RunExternalRs232()
RunExternalRs232() opens the serial port, returns -1 if can't open serial, and if is ok runs the function ExternalRs232Thread() in a detached thread.
Initially, I have tried to run this with Serialib, but this never works properly with the Qt project. So I decided to give QSerialPort a try like this:
Server.h
#ifndef SERVER_H
#define SERVER_H
#include <iostream>
#include <winsock2.h>
#include <windows.h>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <sstream>
#include <time.h>
#include <chrono>
#include <mutex>
#include <QObject>
#include "libs/json.hpp"
#include <QtSerialPort>
#include "hdlccsvparser.h"
using namespace std;
using json = nlohmann::json;
using std::chrono::milliseconds;
using std::chrono::duration_cast;
using std::chrono::seconds;
using std::chrono::system_clock;
struct Contact {
int port; //udp port of te contact
time_t time; //last communication date ( is still active ? )
};
class Server : public QObject
{
Q_OBJECT
public:
Server();
virtual ~Server();
[...]
//return 0 if sucess , 1 port is not usabe, 2 port already in use
int RunExternalRs232(string PortCom, unsigned int baudrate);
int StopExternalRs232(string PortCom);
[...]
//clean exit
void ExitServer();
signals:
[...]
private:
[...]
// External ThreadS and exiting loop of threadS in set (if key d'ont exist anymore, exit loop)
mutex mtxExternalRs232;
unordered_set<string> ExternalRs232PortActiveList;
void ExternalRs232Thread(QSerialPort* Rs232Connection, string port);
[...]
};
#endif // SERVER_H
Server.cpp
#include "Server/Server.h"
#include <winsock2.h>
/* Public Part*/
[...]
int Server::RunExternalRs232(string PortCom, unsigned int baudrate){
//PortCom = (char *)"COM1";
//baudrate = 9600;
QSerialPort Rs232Connection;
// Connection to serial port
// COMxx
Rs232Connection.setPortName(QString::fromStdString(PortCom));
Rs232Connection.setBaudRate(baudrate);
Rs232Connection.setDataBits(QSerialPort::Data8);
Rs232Connection.setParity(QSerialPort::NoParity);
Rs232Connection.setStopBits(QSerialPort::OneStop);
Rs232Connection.setFlowControl(QSerialPort::NoFlowControl);
int i = Rs232Connection.open(QIODevice::ReadWrite);
// If connection fails, errorOpening != 1
if(i != 1){
return i;
}
mtxExternalRs232.lock();
ExternalRs232PortActiveList.insert(PortCom);
mtxExternalRs232.unlock();
cout << "External Rs232 starting on " << PortCom << endl;
stringstream sstmp;
sstmp << PortCom;
emit LogMessage(QString::fromStdString("External Rs232 starting on " + sstmp.str()),0);
std::thread thServer(&Server::ExternalRs232Thread,this,&Rs232Connection,PortCom);
thServer.detach();
return 0;
}
int Server::StopExternalRs232(string PortCom){
PortCom = "COM1";
mtxExternalRs232.lock();
/*
if(ExternalRs232PortActiveList.count(PortCom) != 1){ //???
//port not in use
cerr << "close Failed Rs232 " << PortCom << endl;
mtxExternalRs232.unlock();
return 1;
}*/
cout << "close External Rs232 " << PortCom << endl;
ExternalRs232PortActiveList.erase(PortCom);
mtxExternalRs232.unlock();
return 0;
mtxExternalRs232.lock();
cout << ExternalRs232PortActiveList.count(PortCom) << " here " << endl;
mtxExternalRs232.unlock();
}
[...]
/* Private Part */
/* Thread part */
[...]
void Server::ExternalRs232Thread(QSerialPort * Rs232Connection, string port){
//char/binary read on rs232
char tmp;
//if rs232 has data
bool res = false;
stringstream message;
Rs232Connection->write("H");
mtxExternalRs232.lock();
int iscount = ExternalRs232PortActiveList.count(port);
mtxExternalRs232.unlock();
while(iscount == 1){
res = false;
if(!Rs232Connection->waitForReadyRead(300)){
//no data skipp
} else {
QByteArray datas = Rs232Connection->readAll();
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
string tmp = codec->toUnicode(datas).toStdString();
cout << tmp << endl;
}
mtxExternalRs232.lock();
iscount = ExternalRs232PortActiveList.count(port);
mtxExternalRs232.unlock();
}
Rs232Connection->flush();
Rs232Connection->close();
}
[...]
/* functions part */
[...]
Server::Server(){
//init winsock
if(WSAStartup(MAKEWORD(2,2),&wsa) != 0){
cerr << "Could not init winsock2 : " << WSAGetLastError();
}
//creating a socket
if((s = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET){
cerr << "Could not create a socket : " << WSAGetLastError();
}
}
Server::~Server(){
//dtor
}
at the end, I get
QObject::startTimer: Timers can only be used with threads started with QThreads
So how is possible to run Serial Rs232 without using QThreads?

Make Peer-to-Peer communication on Qt Remote Objects

I want to make simple communication example on Qt Remote Objects. I want to make the communication peer-to-peer, therefore I'm trying to merge both Source and Replica of the same remote object functionality in one application (REPC_MERGED tool used to generate Source and Replica base classes).
#include <QtCore/QCoreApplication>
#include "MyPeerHost.h"
#include "Client.h"
#include <QDebug>
static QString peer_node_name(int number)
{
QString ret = QString("peer_%1").arg(number);
return ret;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MyPeerHost peerHost; // just inherits auto-generated MyPeerSimpleSource
QUrl thisAddress = "local:" + peer_node_name(0);
QRemoteObjectHost sourceNode(thisAddress);
if(sourceNode.enableRemoting(&peerHost))
{
qInfo() << "Source remoting enabled successfully" << thisAddress;
QUrl remoteAddress = "local:" + peer_node_name(1);
QSharedPointer<MyPeerReplica> replica;
QRemoteObjectNode replicaNode;
if(replicaNode.connectToNode(remoteAddress))
{
qInfo() << "Replica connected to the address" << remoteAddress << "successfully";
replica.reset(replicaNode.acquire<MyPeerReplica>());
QString sourceClassName = peerHost.staticMetaObject.className();
qDebug() << "Replica wait for Source" << sourceClassName << "...";
if(replica->waitForSource(1000))
{
qInfo() << "Replica object completely initialized";
Client client;
client.setReplicaObject(replica);
client.sendMessage("AAA");
}
else
{
qCritical() << "Replica wait for Source" << sourceClassName << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Replica connect to the address" << remoteAddress << "FAILED" << replicaNode.lastError();
}
}
else
{
qCritical() << "Source remoting enable FAILED" << sourceNode.lastError();
}
return a.exec();
}
Application output:
Source remoting enabled successfully QUrl("local:peer_0")
Replica connected to the address QUrl("local:peer_1") successfully
Replica wait for Source "MyPeerHost" ...
Replica wait for Source "MyPeerHost" FAILED QRemoteObjectNode::NoError
As you see, replicaNode successfully connected to the non-existent node QUrl("local:peer_1").
What I am doing wrong?
You don't have valid Qt code.
Qt relies on an event loop to handle asynchronous behavior, which is started by the a.exec() at the end of your main() routine. Qt Remote Objects, in turn, relies on the event loop for all of its communication.
In your code, you create your objects on the stack, but in code blocks that go out of scope before you start the event loop. They will therefore be destructed before the event loop is kicked off.
I'd recommend starting with some of the examples, make sure they work, then grow what you are trying to do from there.

How could you develop a simple DEALER/ROUTER message flow, using ZeroMQ?

I'm fairly new to TCP messaging (and programming in general) and I am trying to develop a simple ROUTER/DEALER message pair with ZeroMQ but am struggling in getting the router to receive a message from the dealer and send one back.
I can do a simple REQ/REP pattern with no problem, where I can send one message from my machine to my VM.
However, when trying to develop a ROUTER/DEALER pair, I can't seem to get the ROUTER-instance to receive the message (ROUTER on VM, DEALER on main box). I have had some success where I could spam 50 messages in a while(){...} loop, but can't send a single message and have the ROUTER send one back.
So from what I have read, a TCP message in a ROUTER/DEALER pair are sent with a delimiter of 0 at the beginning, and this 0 must be sent first to the ROUTER to register an incoming message.
So I just want to send the message "ROUTER_TEST" to my server, and for my server to respond with "RECEIVED".
DEALER
#include <cstdlib>
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include "zmq.h"
const char connection[] = "tcp://10.0.10.76:5555";
int main(void)
{
int major, minor, patch;
zmq_version(&major, &minor, &patch);
printf("\nInstalled ZeroMQ version: %d.%d.%d\n", major, minor, patch);
printf("Connecting to: %s\n", connection);
void *context = zmq_ctx_new();
void *requester = zmq_socket(context, ZMQ_DEALER);
int zc = zmq_connect(requester, connection);
std::cout << "zmq_connect = " << zc << std::endl;
int sm = zmq_socket_monitor(requester, connection, ZMQ_EVENT_ALL);
std::cout << "zmq_socket_monitor = " << sm << std::endl;
char messageSend[] = "ROUTER_TEST";
int request_nbr;
int n = zmq_send(requester, NULL, 0, ZMQ_DONTWAIT|ZMQ_SNDMORE );
int ii = 0;
if(n==0) {
std::cout << "n = " << n << std::endl;
while (ii < 50)
{
n = zmq_send(requester, messageSend, 31, ZMQ_DONTWAIT);
ii++;
}
}
return 0;
}
ROUTER
// SERVER
#include <cstdlib>
#include <iostream>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include "zmq.h"
int main(void)
{
void *context = zmq_ctx_new();
void *responder = zmq_socket(context, ZMQ_ROUTER);
printf("THIS IS WORKING - ROUTER\n");
int rc = zmq_bind(responder, "tcp://*:5555");
assert(rc == 0);
zmq_pollitem_t pollItems[] = {
{responder, 0, ZMQ_POLLIN, -1}};
int sm = zmq_socket_monitor(responder, "tcp://*:5555", ZMQ_EVENT_LISTENING);
std::cout << "zmq_socket_monitor = " << sm << std::endl;
uint8_t buffer[15];
while (1)
{
int rc = zmq_recv(responder, buffer, 5, ZMQ_DONTWAIT);
if (rc == 0)
{
std::cout << "zmq_recv = " << rc << std::endl;
zmq_send(responder, "RECIEVED", 9,0);
}
zmq_poll(pollItems, sizeof(pollItems), -1);
}
return 0;
}
Your code calls, on the DEALER-side a series of:
void *requester = zmq_socket( context,
ZMQ_DEALER // <-- .STO <ZMQ_DEALER>, *requester
);
...
int n = zmq_send( requester, // <~~ <~~ <~~ <~~ <~~ <~~ .STO 0, n
NULL, // NULL,sizeof(NULL)== 0
0, // explicitly declared 0
ZMQ_DONTWAIT // _DONTWAIT flag
| ZMQ_SNDMORE //---- 1x ZMQ_SNDMORE flag==
); // 1.Frame in 1st MSG
int ii = 0; // MSG-under-CONSTRUCTION
if ( n == 0 ) // ...until complete, not yet sent
{
std::cout << "PREVIOUS[" << ii << ".] CALL of zmq_send() has returned n = " << n << std::endl;
while ( ii < 50 )
{ ii++;
n = zmq_send( requester, //---------//---- 1x ZMQ_SNDMORE following
messageSend, // // 2.Frame in 1st MSG
31, // // MSG-under-CONSTRUCTION, but
ZMQ_DONTWAIT // // NOW complete & will get sent
); //---------//----49x monoFrame MSGs follow
}
}
...
What happens on the opposite side, the ROUTER-side code ?
...
while (1)
{
int rc = zmq_recv( responder, //----------------- 1st .recv()
buffer,
5,
ZMQ_DONTWAIT
);
if ( rc == 0 )
{
std::cout << "zmq_recv = " << rc << std::endl;
zmq_send( responder, // _____________________ void *socket
"RECEIVED", // _____________________ void *buffer
9, // _____________________________ size_t len
0 // _____________________________ int flags
);
}
zmq_poll( pollItems,
sizeof( pollItems ),
-1 // __________________________________ block ( forever )
);// till ( if ever ) ...?
}
Here, most probably, the rc == 0 but once, if not missed, but never more
Kindly notice, that your code does not detect in any way if a .recv()-call is also being flagged by a ZMQ_RECVMORE - signaling a need to first also .recv()-all-the-rest multi-Frame parts of the first message, before becoming able to .send()-any-answer...
An application that processes multi-part messages must use the ZMQ_RCVMORE zmq_getsockopt(3) option after calling zmq_recv() to determine if there are further parts to receive.
Next, the buffer and messageSend message-"payloads" are a kind of fragile entities and ought be re-composed ( for details best read again all details about how to carefully initialise, work with and safely-touch any zmq_msg_t object(s) ), as after a successful .send()/.recv() the low level API ( since 2.11.x+ ) considers 'em disposed-off, not re-useable. Also note, that messageSend is not (as imperatively put into the code) a 31-char[]-long, was it? Was there any particular intention to do this?
The zmq_send() function shall return number of bytes in the message if successful. Otherwise it shall return -1 and set errno to one of the values defined below. { EAGAIN, ENOTSUP, EINVAL, EFSM, ETERM, ENOTSOCK, EINTR, EHOSTUNREACH }
Not testing error-state means knowing nothing about the actual state ( see EFSM and other potential trouble explainers ) of REQ/REP and DEALER/ROUTER (extended) .send()/.recv()/.send()/.recv()/... mandatory dFSA's order of these steps
"So from what I have read, a TCP message in a ROUTER/DEALER pair are sent with a delimiter of 0 at the beginning, and this 0 must be sent first to the ROUTER to register an incoming message."
This seems to be a misunderstood part. The app-side is free to compose any number of monoframe or multi-frame messages, yet the "trick" of a ROUTER prepended identity-frame is performed without users assistance ( message-labelling is performed automatically, before any ( now, principally all ) multi-frame(d) messages get delivered to the app-side ( using the receiver's side .recv()-method ). Due handling of multi-frame messages was noted above.

how to set SO_REUSEADDR on the socket used by QTcpServer?

I have been using a QTcpServer subclass as a http server, but now I need to reuse the server port.
I have tried to set ShareAddress | ReuseAddressHint in a QTcpSocket, it seems worked, because the same port can be bound twice. But I did not find a way to get a QTcpSocket object from an existing QTcpServer object.
I also used the socketDescriptor() to get the native socket, because I want to use the linux C way to setsockopt, but I don't know how to use linux C code with Qt code together to set socket options.(I followed the Qt style until now.)
I am on ubuntu and Qt5.4. And I am stuck...
Any help would be appreciated. Thanks in advance.
Because SO_REUSEPORT needs to be set before bind/listen is called you need to create descriptor first, set all needed flags, bind, listen and forward your descriptor to QTcpServer for future usage with it.
Here is an example which will listen on port 9999 on any interface
mytcpserver.h:
#ifndef MYTCPSERVER_H
#define MYTCPSERVER_H
#include <QObject>
#include <QTcpSocket>
#include <QTcpServer>
class MyTcpServer : public QObject
{
Q_OBJECT
public:
explicit MyTcpServer(QObject *parent = 0);
public slots:
void newConnection();
private:
QTcpServer *server;
};
#endif // MYTCPSERVER_H
mytcpserver.cpp:
#include "mytcpserver.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
MyTcpServer::MyTcpServer(QObject *parent):QObject(parent)
{
this->server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, &MyTcpServer::newConnection);
// open server and listen on given port
int sockfd = 0;
struct sockaddr_in serv_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
qDebug() << "ERROR: IoT Omega Daemon can't open socket";
exit(EXIT_FAILURE);
}
int flag = 1;
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) == -1)
{
qDebug() << "ERROR: Can't set SO_REUSEADDR";
exit(EXIT_FAILURE);
}
//set Address,IFace, Port...
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(9999);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(sockaddr_in)) < 0)
{
qDebug() << "ERROR: can't bind socket";
exit(EXIT_FAILURE);
}
if(listen(sockfd,SOMAXCONN) < 0)
{
qDebug() << "ERROR: can't listen on port";
exit(EXIT_FAILURE);
}
//forward our descriptor with SO_REUSEPORT to QTcpServer member
server->setSocketDescriptor(sockfd);
}
void MyTcpServer::newConnection()
{
qDebug() << "NEW CONNECTION " << __LINE__;
QTcpSocket * socket = server->nextPendingConnection();
socket->write("Hello client\r\n");
socket->flush();
socket->waitForBytesWritten(3000);
socket->close();
}
Maybe you would like to optimize your socket usage even more? For example setting SO_LINGER timeout of zero to avoid large numbers of connections sitting in the TIME_WAIT state? Maybe you don't ned waiting for ACKs(TCP_NODELAY)?
Then your newConnection function can look like this:
void MyTcpServer::newConnection()
{
qDebug() << "NEW CONNECTION " << __LINE__;
QTcpSocket * socket = server->nextPendingConnection();
int flag = 1;
struct linger l = {1,0};
if(setsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_REUSEADDR, (const char *)&flag, sizeof(int)) < 0)
{
qDebug() << "ERROR: Can't set SO_REUSEADDR";
}
if(setsockopt(socket->socketDescriptor(), IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) < 0)
{
qDebug() << "ERROR: can't fork set TCP_NODELAY";
}
socket->write("Hello client\r\n");
socket->flush();
socket->waitForBytesWritten(3000);
if(setsockopt(socket->socketDescriptor(), SOL_SOCKET, SO_LINGER, (const char *)&l,sizeof(l)) < 0)
{
qDebug() << "ERROR: Can't set SO_LINGER";
}
socket->close();
}
This will just do the job and free any used socket resource so they can be reused just right after that. Usefull for heavyload microservers, etc..

iostream and No_delay option

I am trying to disable the Nagle Algorithm using the answer for the same question: ASIO ip::tcp::iostream and TCP_NODELAY:
boost::asio::ip::tcp::iostream socketStream;
const boost::asio::ip::tcp::no_delay option( true );
socketStream.rdbuf()->set_option( option );
boost::asio::io_service io_service;
tcp::endpoint endpoint (tcp::v4 (), 6666);
tcp::acceptor acceptor (io_service, endpoint);
std::cout << "Waiting for connection.." << std::endl;
acceptor.accept (*socketStream.rdbuf ());
std::cout << "Connected!" << std::endl;
and when running the code this error appears:
set_option: Bad file descriptor
How can I solve this problem?
Where you set the option, the stream is still invalid (not open).
Wait until the socket is open, before setting the option:
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
static boost::asio::ip::tcp::no_delay const no_delay_option (true);
int main() {
using boost::asio::ip::tcp;
tcp::iostream socketStream;
boost::asio::io_service io_service;
tcp::endpoint endpoint (tcp::v4(), 6666);
tcp::acceptor acceptor (io_service, endpoint);
std::cout << "Waiting for connection.." << std::endl;
acceptor.accept (*socketStream.rdbuf ());
socketStream.rdbuf()->set_option(no_delay_option);
std::cout << "Connected!" << std::endl;
std::cout << socketStream.rdbuf() << "\n";
}
(We send main.cpp to port 6666 using netcat there)

Resources