How to check incomingConnection for valid IP before starting QTcpSocket in separate thread
-
And you think that checking for each connection the peer address in the main application thread will be better than just checking it in a separate thread for each connection and closing it? From my point of view you should just let it be like this because for a large number of incoming connections this will work better and faster than what you are planning... At least this is what I think...
-
Well, I just thought: why create a thread with all its overhead along with a new socket for a peer that eventually turns out to be not acceptable.
Checking for a valid address in a relatively small network will probably be less efford, so I thought. Maybe I am wrong. Actually I did not seriously consider the opposite.
My application is a print server listening on ports 515 (lpd) and 9100 (raw) for postscript files to be converted to pdf format rather than really being printed.
BTW: do you know a way to accomplish this sort of pre-checking, apart from your controverse recommendation? -
Why create a socket at all? The whole networking APIs are asynchronous, so you should get a long way without bothering with threading...
-
I mostly agree with Tobias - the main thread is capable of handling many sockets simultaneously and asynchronously. On the other hand, I also like the architecture of one thread per socket/client where every client is handled nicely in isolation (and in parallel) from others.
In my opinion, the best combination performance wise (with the least overhead and maximum parallelism) would be to have all sockets in the main thread. Connect to the appropriate QAbstractSocket and QIODevice slots and do the host checking and all socket I/O in main thread. Also have a QThreadPool to leverage multiple CPUs/cores. Submit the actual jobs for parallel processing using QThreadPool::start() or QtConcurrent::run(). And in the end, have the job object notify the main thread (via a signal) about the result.
-
You might have guessed what I forgot to mention right in the beginning: I have very little knowledge of Qt and do not understand various explanations in the docs quite well.
My concept is this:- run an instance of MyQTcpService in the main Thread.
- have it myQTcpService->listen(QHostAddress::Any, 515) on the due socket, f.i. on port 515
- then on every occasionally MyQTcpService::incommingConnection(intptr handle)
I have two possibilities:
-
a. create a new MyQTcpSocket and call *myQTcpSocket->setSocketDescriptor(handle)* or
-
b. create a new *MyThread(handle)* and do the a.-stuff within the constructor of *MyThread*.
- then on signal myQTcpSocket->readyRead() myCommunication() slot gets called, etc.
I did not take the first approach (a.) because I feared freezing of my user interface in case of long files to be processed. So to me the thread approach seemed to be adequate.
In the latter case the initially posted question arose: why first create all the infrastructure just to destroy it in case that myTcpSocket->peerAddress() returned an invalid peer?
(Note: the only way I found to get the peerAdress information, is via the respective socket, so before I can get this info, I need the socket, but before I can create the socket inside a thread as given above, the thread must exist.) -
You can obtain the peer address this way without creating a new QTcpSocket and a new thread:
In your QTcpServer::incomingConnection(int socketDescriptor) implementation, call "getperrname()":http://linux.die.net/man/2/getpeername on the socketDescriptor handle. It's a native OS function operating on raw socket file descriptors. You can use its output (the sockaddr struct) to initialize a QHostAddress object for convenience. Call close() on the socketDescriptor if you decide to deny the client.
-
Thanks martin_ky, that's what I was looking for.
Still I have no idea how to access/check the socket descriptor. The handle provided by incomingConnection(qintptr handle) is a pointer and as such does not offer any methods to access the socket information. Is there any kind of cast I could use?
The only way I found to access this information is by creating a socket, then call setSocketDescriptor(handle) on it, and only after this is done I can call peerAdress() on that same socket, so the question keeps hanging on. -
I see your confusion. The qintptr is not really a pointer, but a pointer-sized integer value. Pointer-sized integers (or INTPTRs) are commonly used to pass native handle values of various objects of the underlying operating system. For example, on Windows a file (or socket) handle is defined as the WINAPI type HANDLE which is in turn defined as void pointer. On Linux and unixes a file or socket handle (more commonly called a 'file descriptor' on unix platforms) is defined as a simple int. Qt hides this underlying platform details and uses a qintptr type to pass native handle values. Such type is guaranteed to be able to represent both int values as well as pointer values.
The documentation on "QTcpServer::incomingConnection()":http://qt-project.org/doc/qt-5.0/qtnetwork/qtcpserver.html#incomingConnection tells us that the socketDescriptor argument is the native socket descriptor for the accepted connection.
Bottom line: the socketDescriptor is not a pointer to some object, it is a value that you can use directly in any native calls, including getpeername(). You will probably get a warning about casting to a smaller/different type, but you can safely ignore that, or add an explicit (int) cast.
-
martin_ky, thanks for your hints. After ours of head ache I looked up the internet for the getpeername() method you mentioned and finally found a Windows site where these socket basics are explained. Hope I´ll find way through this now.
BTW working on a Win-OS hopefully will not kick me out of the decent part of mankind in the eyes of the rest of the world.
-
Unless it was clear from my last post and from the Qt documentation: the qintptr IS the native socket descriptor of the incoming connection.
Your incomingConnection() implementation can use it like this:
@void MyTcpServer::incomingConnection(qintptr socketDescriptor)
{
sockaddr nativeAddress;
int sz = sizeof nativeAddress;
getpeername(socketDescriptor, &nativeAddress, &sz);
QHostAddress address(&nativeAddress);
qDebug() << "Peer address is:" << address.toString();//TODO: decide what to do with the incoming connection
}
@On Windows, don't forget to include WinSock2.h and link the Ws2_32.lib
-
Following the hints of your last post I had found the Win-Socket stuff on the internet and after some hours of head scratching came to a similar code, which did not work. Now I copied your snippet (thanks!) into my code. For reasons I cannot conceive of, it does not work for me either:
@void TcpServerThreaded::incomingConnection(qintptr socketDescriptor){
sockaddr nativeAddress;
int sz = sizeof nativeAddress;
if(getpeername(socketDescriptor, &nativeAddress, &sz)== SOCKET_ERROR){
emit annotation(new QString("there is only little hope\n"));
}else{
QHostAddress address(&nativeAddress);
emit annotation(new QString(address.toString()));
}
...
}
@
Compiles without errors, but guess what annotation I get... -
For the fun of it, I tried to compile it both on Qt 5.0.2 and Qt 4.8.3 (Windows, MSVC).
On 4.8.3 works OK. But on 5.0.2 I'm getting the following error:
- getpeername() returns -1 (SOCKET_ERROR)
- WSAGetLastError() returns 10014 (WSAEFAULT)
I have absolutely no idea why it works on Qt 4 and not on Qt 5 :(
-
I will try to find out a little more about it. Try and error etc.. Nevertheless great hints you gave me. Thanks again.
-
You're welcome. It seems to me, that something changed between Qt 4 -> Qt 5, which makes this approach unusable on Windows.
Perhaps somebody from the Qt developers could look at this more closely?
-
Found a solution, however did not yet understand fully why it works:
the ERRORCODE from WSAGetLastError() 10014 means something like address(-size) error.
(See http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx)
So I assumed there must be something going wrong in the header file with the selection of the proper structure to use. Then I just increased the value of @ int sz = sizeof nativeAddress;@, which is being passed as a parameter to getpeername().
After having added 12 to sz, all of a sudden it worked.
Thanks to all again and especially to martin_ky. -
Ok, I figured this out (I was curious :). Bbbill, you're on the right track with the size of the sockaddr sturcture. This struct simply is not big enough to hold IPv6 addresses.
Do not just blindly increase the passed size of the address structure, or you will most certainly corrupt your stack. Instead sockaddr, use the sockaddr_storage struct, which is large enough to contain also IPv6 addresses. More light on this in "this thread.":http://stackoverflow.com/questions/8835322/api-using-sockaddr-storage
The difference between Qt 4 and 5 which I was talking about was in my QTcpServer::listen(QHostAddress::Any) call:
- QHostAddress::Any in Qt 4 means "listen on all IPv4 interfaces".
- but in Qt 5 "listen on all IPv4 and all IPv6 interfaces".
Therefore I was getting IPv6 connections when compiled with Qt 5, but not in Qt 4. If you don't want to use IPv6 and save some trouble, just make your QTcpServer listen only on QHostAddress::AnyIPv4. In this case, my original code works perfectly.
-
Yes, your recommendation makes sense. I changed my code for "sockaddr_storage". Have to use a cast in getpeername() now. Nasty but sure much more reliable than my tweeking with magic numbers.
Also I had to initialise the winsocket-dll by WSAStartup()-call prior to using native methods, otherwise it would not work. As I would like to leverage my programm to ip6 capability, I'll stick with this more general approach and go on learning to get it running. Thanks a lot.