Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QTcpServer and listening on multiple ports



  • Hey All,

    I suspect I haven't quite grasped how QTcpServer works.

    I'm writing a (not so) simple network chat program. I've realised that to chat to more than one client on the same WAN address, I need to use multiple ports (with NAT rules on the router).

    QTcpServer::listen only allows you to listen to the one port. Is there a way to listen on a port range (ie: 7770:7800)?

    Am I approaching this the wrong way perhaps?

    It seems like a simple enough solution, but it currently eludes me.

    Thanks in advance for your help.

    Steve Q. :-)



  • @steveq said in QTcpServer and listening on multiple ports:

    QTcpServer::listen only allows you to listen to the one port. Is there a way to listen on a port range (ie: 7770:7800)?

    No. TCP is session oriented, so it is essentially point-to-point. You can connect multiple clients to a single TCP port, and the server must differentiate between the individual sessions. In the POSIX world this was historically done by forking the server process so that each session had its own server process running. Aggregating multiple sessions under a single server process is more complicated.

    Am I approaching this the wrong way perhaps?

    I doubt your clients are all under the same WAN address. More likely your server is sitting behind NAT so it has a single hidden address but presents a single public IP to the WAN. For the clients to get thru to the NATTed server your router must do port forwarding when an external client wishes to make a connection.

    Even if your clients themselves are NATTed on the other end, the routers should handle remapping to the correct IP if you are using TCP.


  • Lifetime Qt Champion

    @steveq

    just to add to @Kent-Dorfman, you are looking for https://doc.qt.io/qt-5/qtcpserver.html#newConnection and https://doc.qt.io/qt-5/qtcpserver.html#nextPendingConnection

    No need for multiple ports, threads or forking.

    Regards

    Edit: https://wiki.qt.io/WIP-How_to_create_a_simple_chat_application might be interesting for you, too.



  • Hey @Kent-Dorfman and @aha_1980,
    Thanks so much for your prompt replies!

    So I still think I'm not getting this.

    The scenario I was talking about is this:
    Let's say I have a static IP address of 1.2.3.4 and I am using port 7770 to chat on. The router connected to that IP address has a NAT rule in it port forwarding 1.2.3.4:7770 to 192.168.1.2:7770. Now lets say I have a second machine on the same IP 1.2.3.4 and it has a LAN IP address of 192.168.1.3. We also want to chat to this machine. I can''t see how to do it unless I use another port so the router would have a rule 1.2.3.4:7771 to 192.168.1.3:7771.

    Does this make sense? My TCP listener would have to monitor both ports (although obviously not as I am not getting it!!).

    @aha_1980 I use newConnection and nextPendingConnection already. In fact my software is running really well when talking to machine, I just can't get my head around how to connect to to different machines behind the same static IP.

    Here is where I set up my server:

    	// Listen for any chat requests
    	m_pChatTcpServer = new QTcpServer(this);
    
    	connect(m_pChatTcpServer, SIGNAL(newConnection()), this, SLOT(chatNewConnection()));
    	connect(m_pChatTcpServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(chatServerError(QAbstractSocket::SocketError)));
    
    	m_pChatTcpServer->listen(QHostAddress::Any, static_cast<quint16>(m_settings.chatPort));
    

    And where I get a newConnection:

    // Called when a new chat connection comes in without us asking for it (m_pChatTcpServer)
    void MainWindow::chatNewConnection()
    {
    	 SQTcpSocket *socket = static_cast<SQTcpSocket *>(m_pChatTcpServer->nextPendingConnection());
    
    	 // Temporarily connect these signals until I am ready to hand this socket off to my ClientServer object
    	 connect(socket, SIGNAL(readyRead()), this, SLOT(chatReadyRead()),Qt::UniqueConnection);
    	 connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(chatConnectionError(QAbstractSocket::SocketError)),Qt::UniqueConnection);
    	 connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(chatTcpSocketState(QAbstractSocket::SocketState)),Qt::UniqueConnection);
    	 connect(socket, SIGNAL(disconnected()), this, SLOT(chatOnConnectionTerminated()),Qt::ConnectionType(Qt::UniqueConnection));
    	 connect(socket, SIGNAL(connected()), this, SLOT(chatOnConnectionEstablished()),Qt::UniqueConnection);
    }
    

    And then receiving the message form the server

    // Here for convenience in case I ever need it. Does nothing because any ready will be done once the socket is handed off to my ClientServer class
    void MainWindow::chatReadyRead()
    {
    	SQTcpSocket *socket = static_cast<SQTcpSocket *>(sender());
    
    	while(socket->bytesAvailable())
    	{
    		QByteArray receivedData = socket->readAll();
    
    		QDataStream ds1(receivedData.mid(0, 4));
    
    		ds1.setByteOrder(QDataStream::LittleEndian);
    
    		int messageSize;
    		ds1 >> messageSize;
    
    		QDataStream ds2(receivedData.mid(4, 4));
    
    		ds2.setByteOrder(QDataStream::LittleEndian);
    
    		int messageType;
    		ds2 >> messageType;
    
    		switch( messageType)
    		{
    			// Initial hand shaking message
    			case CHATMESSAGE_HELLO:
    			{
    				QString uuid, nameColour, nickName;
    
    				uuid = receivedData.mid(8, 32);
    
    				nameColour = receivedData.mid(40, 7);
    				nickName = receivedData.mid(47, -1);
    
    				// Reply back with my initial HELLO (using the same uuid so we both appear in the correct chat tab) message with the nick name colour and nick name
    				unsigned int messageType = CHATMESSAGE_HELLO;
    
    				QByteArray l_vDataToBeSent;
    
    				QDataStream l_vStream(&l_vDataToBeSent, QIODevice::WriteOnly);
    
    				l_vStream.setByteOrder(QDataStream::LittleEndian);
    
    				unsigned int size = sizeof(messageType) + static_cast<unsigned int>(m_settings.nickNameColour.length()) + static_cast<unsigned int>(m_settings.nickName.length()) + static_cast<unsigned int>(uuid.length());
    
    				l_vStream << size;
    
    				l_vStream << messageType;
    
    				l_vDataToBeSent.append(uuid);
    				l_vDataToBeSent.append(m_settings.nickNameColour);
    				l_vDataToBeSent.append(m_settings.nickName);
    
    				if ( socket ) socket->write(l_vDataToBeSent, l_vDataToBeSent.length());
    
    				// Now start the new chat
    				startNewChat( socket, uuid, nameColour, nickName );
    
    				break;
    			}
    		}
    	}
    }
    

    Thanks in advance guys, I think I am totally missing how this works here.

    Regards,

    Steve Q.


  • Lifetime Qt Champion

    @steveq You cannot have multiple maschines with the same IP in one network.

    NAT port forwarding still works with one server port only. The trick with port forwarding is, that different client IP addresses are mapped to one address (the NAT routers address), but different client ports.

    For your server, thats transparent.

    Edit: clarified port forwarding more



  • @aha_1980 okay, so I have two versions of my software running on two different machines both with unique LAN IP addresses (192.168.1.2 and 192.168.1.3), but the static IP address for my premises on the internet is provided by my ISP (1.2.3.4) and is the same for both machines, that is if you Google whatismyipaddress, they both have the same "internet" address, but unique LAN addresses. The NAT in the router won't let me forward my chat port (7770) to them both, and even if it did, both machines would get the one message.

    This is doing my head in!! Lol

    Sorry again in advance, I seem to be making this harder than it need to be.

    Steve Q.



  • I think the thing you don't make clear is your chat architecture. Is it a chat mesh network, or a single master server and multiple clients that all go thru that server? That is the configuration you need to implement. Having multiple chat servers is going to gum up things. You need one master/server that manages and rebroadcasts to relevant clients.

    Rather than worrying about implementation details you need to formalize your system architecture first.


  • Lifetime Qt Champion

    @steveq you should draw yourself a topology picture and understand how the data flow is.

    Remember that port forwarding is only for the server.

    The client establishes a connection to 1.2.3.4, which is an internet address so the packet leaves your local net. then your router sends the packet back into your local net to the server (which does not even know the clien is next to him!). the answers then goes the other way round.

    Note that multiple clients and one server can run on the same machine!



  • Hey Guys,

    Firstly thanks so much for your help.

    My software is both the server and client in the one package, which now appears to be the source of my problems. In saying that I don’t have the luxury of having a dedicated machine where I can have a server running 24/7. In the future I may look at re-writing my code and have a server running on a Raspberry Pi that I have. If I do that, I guess I’ll need two programs, the chat server and the chat client.

    In its current form it’s running really well, even allowing multiple clients in the one chat as well as running others chats concurrently. It suits the purpose for which I wrote it, apart from this current issue, which I can “hack” to work if I really want to.

    From what I understand, the better approach would be where I have a client which connects to a dedicated server and then passes the chat onto the receiving client. All communication then goes through the server.

    Thanks for everything guys, I really appreciate your help!

    Steve Q



  • @steveq
    Just as an add-on, you can think of two ways to model a client-server architecture.
    Centralized and Distributed.
    Both have their advantages and desvatangens, and all will depend on the purpose of your application.
    If your application has to provide transparency to the user, whether it is about access (no matter which server I am "connected") or fails (one of the servers is offline), you may have to rethink in a distributed approach.



  • Thanks @KillerSmath, it appears I have a little more reading to do! :-)


Log in to reply