QTcpSocket and Specifying Client Outgoing Network Device - c++

Windows 8.1 user here, using Qt 5.3. Trying to learn network programming (please bear with me). Let's say I have two network devices on my machine. One is assigned the IP 192.168.1.2, and the other 192.168.1.3. The first device has priority.
My goal is to create a QTcpServer on 192.168.1.2 and a QTcpSocket client on 192.168.1.3. The way I envision this would work is the data packets from the client will start at 192.168.1.3 (on some port), travel to the router, then to the server at 192.168.1.2 (on some port). Ok, hopefully this sounds reasonable.
Here's the problem. I can't find a functioning way to specify an outgoing address with QTcpSocket. There appears to be a bind method, but it doesn't do much. Each time I send that from the client, it travels on the default device at 192.168.1.2.
socket = new QTcpSocket(this);
qDebug() << socket->localAddress(); // shows "0"
qDebug() << socket->localPort(); // shows "0"
socket->bind(QHostAddress("192.168.1.3"), 50000);
qDebug() << socket->localAddress(); // shows "50000"
qDebug() << socket->localPort(); // shows "0"
//socket->setLocalAddress(QHostAddress("192.168.1.4")); // error, says it's protected
//socket->setLocalPort("50000"); // error, says it's protected
//qDebug() << socket->localAddress();
//qDebug() << socket->localPort();
socket->connectToHost("google.com", 80); // network manager shows data on 192.168.1.2
Any ideas?

Related

Make a ping function with QTcpSocket

I want to make a ping function with Qt (and don't like the QProcess execution system ping way).
Here's the demo code,
void SimmplePing(const QString& sAddress, int port=80)
{
QTcpSocket messenger;
messenger.connectToHost(sAddress, port);
if (!messenger.waitForConnected(3000))
{
qDebug() << messenger.error();
}
else
{
qDebug() << "OK";
}
}
void test()
{
SimmplePing("182.34.19.222", 80);
}
I test it with "192.168.0.1" (my router IP) and "www.baidu.com" both can work.
But I test it "182.34.19.222" failed with QAbstractSocket::SocketTimeoutError error message.
However, pinging it within system cmd can work properly. Couldn't figure out why.
The main difference here is that the ping command as it's normally used, uses the ICMP protocol which doesn't use TCP for communication. Trying to connect to a host via websockets using TCP is handled in a different way. If the TCP socket of the host you're trying to connect to doesn't listen on the specified port, in this case port 80, you won't recieve an answer, which can lead to the behaviour you encountered.
(https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol)

Unable to connect to establish a socket connection using QTcpSocket to a linux box (telnet works)

code:
watchTowerSocket = new QTcpSocket();
watchTowerSocket->connectToHost("198.168.101.230", 4400);
if(watchTowerSocket->waitForConnected())
{
qDebug() << "Connected";
watchTowerSocket->write("HELLO SERVER");
}
else
{
qDebug() << "Not Connected";
}
When I run my application, I get "Connected" on the console. However, my server (which is running on a linux box) does not receive a connection. Also, I ran wireshark and found that no TCP packet is getting generated when the above code is being run.
What must I try to fix this?

Qt Socket not waiting for bytes written

I wrote a chess client/server mobile application and have a remote server that has been tested on the West Coast, the East Coast, in between, etc. The program is this in a nutshell:
1. Log in to remote server with correct username/password via iOS/Android or Windows desktop.
2. Enter queue for a 1-minute, 5-minute or 30-minute game of chess.
3. Wait for another opponent to join queue.
4. Get matched and play game of chess.
5. When game is over, log out or play more chess.
I am getting the weirdest freaking error when I log in to the server via my school's internet, however. This is so weird because it the ONLY ISP that has problems out of the many ISP's I have connected from.
When I log into the server through my school's internet, I will get the following error and errorString from my socket.
QAbstractSocket::UnknownSocketError "Unknown error"
The steps to produce this in my application are:
1. Enter username and password, log into server. (Successfully completes this).
2. Click to join a queue for a game of chess. (Successfully writes to socket, but fails to wait for bytes written then emits the above error and error string.
I checked the server and readyRead() is not even called, so I know the client is not sending anything to the server.
The funny thing is, I found a workaround for getting past this error.
1. Click on Settings page.
2. Click Save. (Does the exact same thing as I try to do above. Write to socket, flush and wait for bytes written).
3. Join queue (Successfully joins queue).
The workaround MAKES NO SENSE since it does not do anything differently than what I tried to do above (write to socket, flush and wait for bytes written).
Does anyone have a clue on what might be going on?
Why is this error specific to ONE internet location? My school internet is slow as hell, but doesn't explain why the socket is disconnected immediately in the function below.
Why does my workaround work?
What can I do to learn more about my problem (i.e. stupid error message..."unknown error").
Not only this, but when I right-click on the function below, then click 'Find usages', the build folder appears. This is the only function in the program that does this. WTF???
Socket disconnects in this function.
void CG_serverConnection::sendQueueType(int timeControl)
{
//Create local JSON object
QJsonObject userInfo;
//Create object
userInfo["PacketHeader"] = QUEUE_REQUEST;
userInfo["TimeControl"] = timeControl;
//Create JSON document
QJsonDocument doc;
doc.setObject(userInfo);
qDebug() << "Send queue type called! ";
qDebug() << doc.toJson();
QByteArray byteArray = doc.toBinaryData();
//Send over socket
if(m_socket->write(byteArray))
{
qDebug() << "Wrote to socket";
}
else
m_socket->errorString();
if(m_socket->flush())
{
qDebug() << "Flushed";
}
else
qDebug() << m_socket->errorString();
if(m_socket->waitForBytesWritten(50000))
{
qDebug() << "Bytes were written.";
}
else
{
qDebug() << m_socket->error();
qDebug() << m_socket->errorString();
}
}
Where I call the function
Button
{
id: btn_oneMinuteGame
text: "One Minute"
style: cgButtonStyle
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: Lobby.getControlWidth()
Layout.preferredHeight: Lobby.getControlHeight()
onClicked:
{
ServerConnection.sendQueueType(1) // TODO : Magic numbers
root.startOneMinuteGame()
}
}
For some reason if I call this at the top of my function, everything works...
Makes no sense. If someone can explain why this works or you have another solution, please post it.
//Temporary bug fix
this->sendUpdatedUserInfo();
Function I call that somehow makes everything work
void CG_serverConnection::sendUpdatedUserInfo()
{
QJsonObject request;
request["PacketHeader"] = UPDATE_INFO;
request["loggedIn"] = m_player.loggedIn;
request["banned"] = m_player.banned;
request["username"] = m_player.username;
request["elo"] = m_player.elo;
request["countryFlag"] = m_player.countryFlag;
request["pieceSet"] = m_player.pieceSet;
request["language"] = m_player.language;
request["sound"] = m_player.sound;
request["coordinates"] = m_player.coordinates;
request["arrows"] = m_player.arrows;
request["autoPromote"] = m_player.autoPromote;
request["boardTheme"] = m_player.boardTheme;
QJsonDocument doc;
doc.setObject(request);
qDebug() << "Updated userInfo being sent: ";
qDebug() << doc.toJson();
m_socket->write(doc.toBinaryData());
m_socket->flush();
m_socket->waitForBytesWritten();
}

UDP bind failures

{Windows 7, MinGW 4.8, boost 1.55}
I'm having some problems with UDP binds. I've a client that broadcasts datagrams for listeners listening on specific port and binds to a port itself if the listeners want to communicate something back.
The port on which the client needs to bind is X and the servers are listening on Y.
Problem:
If I simulate a client-crash (eg., by causing segmentation fault by dereferencing a nullptr) after binding the UDP socket to the port, then once the client application is no longer running (no longer listed in Windows Task Manager) netstat -ano | find "X" still shows that someone is bound to port X and ip address of 0.0.0.0 (the client had specified the IP address as any address). The PID cannot be found in Windows Task Manager. However when I downloaded application TCPView I can see that a <non-existent> process is still bound to 50000. On starting the client (without making it crash this time) subsequently.
I get two behaviors:
<1> On some machines the client is unable to bind to the socket again (although reuse_address option is set to true) and the error message is: An attempt was made to access a socket in a way forbidden by its access permissions.
<2> On other machines the client binds successfully but the read handler is not called and the client does not receive any datagram on port X although the servers are unicasting to the client port X. Infact <2> is true even for launching multiple instances of the client on the same machine even if none of the clients were deliberately made to crash and exist as zombie processes. Only the 1st one gets datagrams.
Here is how client socket is set up:
if(!m_udpSocket.is_open())
{
m_udpSocket.open(m_localEndpoint.protocol(), errorCode); //m_localEndpoint is address 0.0.0.0 and port X
if(errorCode)
{
std::cerr << "Unable to open socket: " << errorCode.message() << std::endl;
}
else
{
m_udpSocket.set_option(boost::asio::socket_base::reuse_address(true), errorCode);
if(errorCode)
{
std::cerr << "Reuse address option set failure. " << errorCode.message() << std::endl;
}
m_udpSocket.set_option(boost::asio::socket_base::broadcast(true), errorCode);
if(errorCode)
{
std::cerr << "Socket cannot send broadcast. " << errorCode.message() << std::endl;
}
else
{
m_udpSocket.bind(m_localEndpoint, errorCode);
if(errorCode)
{
std::cerr << "Socket cannot bind...!! " << errorCode.message() << std::endl;
}
}
}
}
Can you explain why do I get <1> and <2> and what can I do to avoid them and make socket bind even if there is some other process bound to that socket? I need to support Windows, Linux and MAC.

boost:asio IPv4 address and UDP comms

Problem Solved - See bottom for solution notes
I'm trying to build a simple app to test an ethernet-capable microcontroller. All I want to do is send and receive small UDP packets. The code is using boost::asio for the networking, and is incredibly simple. For debugging I moved all the intialisation out of the constructors so I could check each step. Here's the body of my stuff:
boost::system::error_code myError;
boost::asio::ip::address_v4 targetIP;
targetIP.from_string("10.1.1.75", myError); // Configure output IP address. HACKHACK--Hardcoded for Debugging
std::cout << "GetIP - " << myError.message() << std::endl;
std::cout << "IP: " << targetIP << std::endl;
boost::asio::ip::udp::endpoint myEndpoint; // Create endpoint on specified IP.
myEndpoint.address(targetIP);
myEndpoint.port(0x1000);
std::cout << "Endpoint IP: " << myEndpoint.address().to_string() << std::endl;
std::cout << "Endpoint Port: " << myEndpoint.port() << std::endl;
boost::asio::io_service io_service; // Create socket and IO service, bind socket to endpoint.
udp::socket socket(io_service);
socket.open( myEndpoint.protocol(), myError );
std::cout << "Open - " << myError.message() << std::endl;
socket.bind( myEndpoint, myError );
std::cout << "Bind - " << myError.message() << std::endl;
char myMessage[] = "UDP Hello World!"; // Send basig string, enable socket level debugging.
socket.send(boost::asio::buffer(myMessage, sizeof(myMessage)), boost::asio::socket_base::debug(true), myError);
std::cout << "Send - " << myError.message() << std::endl;
boost::array<char, 128> recv_buf; // Receive something (hopefully an echo from the uP)
udp::endpoint sender_endpoint;
size_t len = socket.receive_from( boost::asio::buffer(recv_buf), myEndpoint );
std::cout.write(recv_buf.data(), len);
The snag happens right at the beginning. The address_v4 doesn't want to accept the IP that I'm passing into it. The output of this app is:
GetIP - The operation completed successfully
IP: 0.0.0.0
Endpoint IP: 0.0.0.0
Endpoint Port: 4096
Open - The operation completed successfully
Bind - The operation completed successfully
Send - A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied
I'm assuming the send error is a result of the address_v4 not getting set correctly, but there is no reason that I can think of for such a thing to be taking place.
For those playing along at home, my PC has dual ethernet cards, one of which has been DHCP'd 10.1.1.7, so the target IP should be reachable without any routing. I'm using BOOST 1.46.1 on 32-bit Win7 and MSVS 10. It also fails when I try an IP of 127.0.0.1, correct me if I'm wrong but that should work for loopback in this context?
Edit with Updates:
So thanks to the earlier answers I've gotten the IP address into my address_v4, and I'm no longer trying to bind when I meant to use connect. The significanly changed section of code is the TX, which now looks like:
socket.open( targetEndpoint.protocol(), myError );
std::cout << "Open - " << myError.message() << std::endl;
char myMessage[] = "UDP Hello World!"; // Send basig string, enable socket level debugging.
socket.send_to(boost::asio::buffer(myMessage, sizeof(myMessage)), targetEndpoint, boost::asio::socket_base::debug(true), myError);
std::cout << "Send - " << myError.message() << std::endl;
(I renamed myEndpoint to targetEndpoint to help reduce confusion.....)
I now get the error while trying to send:
The attempted operation is not supported for the type of object referenced
I would give my firstborn for an informative error message at this point! The error is consistent regardless of which target port I use. The only thing I can think of is that I need to be setting the source port somewhere, but I don't see how you can do that in any of the boost::asio documentation.
Final Resolution
I have managed to make this work, so I'm going to post the gotchas that I found in a nice neat list for anyone else who stumbles across this answer with similar problems to me. I think the main issue I had was that none of the boost examples ever show how to connect to a specified IP, they all use a resolver. It made the examples a lot harder to understand for me.
When using the from_string call to convert a text IP, use the syntax from the first answer below rather than my syntax above!
When setting up the UDP socket, order of operations is crucial! If you don't want to do it in the constructor you need to:
Open the socket using the required protocol.
Bind the socket to a local endpoint which specifies the source UDP port number.
Connect the socket to the remote endpoint which specifies the destination IP and Port number.
Attempting to bind after the connect will cause the bind to fail. The transmission will operate just fine, but your packets will be sent from an arbitrary port number.
Use a send method to actually transmit. Do not attempt to enable debugging data with boost::asio::socket_base::debug(true)! All this flag seems to do is cause error messages within an otherwise functional send!
I'd also like to share that my most valuable debugging tool in this entire exercise was Wireshark. Maybe it's only because I'm used to having a CRO or Protocol Analyser when I'm working on comms like this, but I found being able to see the bytes-on-wire display helped me sort out a whole bucketload of stuff that I would otherwise never have tracked down.
Cheers for your help on the IP issues and helping me realise the difference between connect and bind.
The problem you are currently seeing appears to be your usage of this line:
targetIP.from_string("10.1.1.75", myError);
boost::asio::ip::address::from_string is a static function, that returns a constructed ip::address object. Change it to look like this:
targetIP = boost::asio::ip::address::from_string("10.1.1.75", myError);
And your IP address should be populated properly.
On the top of my head, you try to bind the socket to an endpoint with address 10.1.1.75, but that seems to be a remote endpoint? I would assume you would like to bind it locally and use send_to, as it is UDP
In this line there is an error:
targetIP = boost::asio::ip::address::from_string("10.1.1.75", myError);
You should put:
targetIP = boost::asio::ip::address_v4::from_string("10.1.1.75", myError);
and then targetIP has the right value!