digitalmars.D - problem with sockets under win32; premature connection termination
- Downs (24/24) May 10 2007 The following code should theoretically wait for a single TCP connection...
- Chris Miller (21/52) May 10 2007 to =
- Downs (5/54) May 10 2007 Yes, on Linux. Not on Win32. (it's even recommended, see the std.socket ...
- Regan Heath (59/59) May 12 2007 I'd like to think I have a bit of experience with sockets and yet this p...
- Regan Heath (86/86) May 12 2007 Still stumped I tried C this time with the same results.
- Downs (18/22) May 12 2007 Yes, um, I kind of solved it for miniserv in the meantime ^^ But my solu...
- Regan Heath (3/29) May 13 2007 Well, I'm glad you're situation is resolved but I'm still puzzled as to ...
- Regan Heath (1/1) May 13 2007 Aha! Don't I feel like a fool. The problems I was having were caused b...
The following code should theoretically wait for a single TCP connection, and transfer a file over it. import std.stdio, std.socket, std.string, std.file; void main(char[][] args) { auto l=new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP); l.blocking=true; with (l) { bind(new InternetAddress("0.0.0.0", atoi(args[1]))); listen(16); } auto request=l.accept; request.send("HTTP/1.0 200 OK\r\n\r\n"); request.send(read(args[2])); request.shutdown(SocketShutdown.BOTH); request.close; } Trying to use this program to host a file (images work best), I ran into the following problem: the image would transmit, partially, then the request.send call would return - prematurely - and the rest of the unsent data would be silently discarded. Experimenting with linger and SetOption produced no discernible effect. I've tried to find a solution to this problem for weeks now, but I don't seem to be making headway. Halp? ;_; -- downs PS: If you try to replicate it, it is recommended to use a mid-latency connection so that the transfer doesn't complete immediately. PPS: Interestingly, it works with netcat. Firefox, IE and Opera break though.
May 10 2007
On Thu, 10 May 2007 18:55:05 -0400, Downs <default_357-line yahoo.de> = wrote:The following code should theoretically wait for a single TCP =connection, and transfer a file over it. import std.stdio, std.socket, std.string, std.file; void main(char[][] args) { auto l=3Dnew Socket(AddressFamily.INET, SocketType.STREAM, =ProtocolType.TCP); l.blocking=3Dtrue; with (l) { bind(new InternetAddress("0.0.0.0", atoi(args[1]))); listen(16); } auto request=3Dl.accept; request.send("HTTP/1.0 200 OK\r\n\r\n"); request.send(read(args[2])); request.shutdown(SocketShutdown.BOTH); request.close; } Trying to use this program to host a file (images work best), I ran in=to =the following problem: the image would transmit, partially, then the request.send call would ==return - prematurely - and the rest of the unsent data would be silently discarded. Experimenting with linger and SetOption produced no discernible effect=.I've tried to find a solution to this problem for weeks now, but I don='t =seem to be making headway. Halp? ;_; -- downs PS: If you try to replicate it, it is recommended to use a mid-latency==connection so that the transfer doesn't complete immediately. PPS: Interestingly, it works with netcat. Firefox, IE and Opera break ==though.Socket.send doesn't guarantee the whole thing will send; it returns how = = many bytes are buffered to be sent. You should loop until it reports all= = the bytes are buffered to be sent, or use SocketStream, which does this = = for you. Also, using Socket.shutdown to shutdown sends/writes (included = by = BOTH) might be aborting the unsent buffer. Finally, after these = appropriate changes, I believe there's still a chance that the whole = buffer won't be sent if the linger time expires before the whole buffer = = was able to send.
May 10 2007
Chris Miller wrote:On Thu, 10 May 2007 18:55:05 -0400, Downs <default_357-line yahoo.de> wrote:Sorry, I omitted this. I tried it; didn't help.The following code should theoretically wait for a single TCP connection, and transfer a file over it. import std.stdio, std.socket, std.string, std.file; void main(char[][] args) { auto l=new Socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP); l.blocking=true; with (l) { bind(new InternetAddress("0.0.0.0", atoi(args[1]))); listen(16); } auto request=l.accept; request.send("HTTP/1.0 200 OK\r\n\r\n"); request.send(read(args[2])); request.shutdown(SocketShutdown.BOTH); request.close; } Trying to use this program to host a file (images work best), I ran into the following problem: the image would transmit, partially, then the request.send call would return - prematurely - and the rest of the unsent data would be silently discarded. Experimenting with linger and SetOption produced no discernible effect. I've tried to find a solution to this problem for weeks now, but I don't seem to be making headway. Halp? ;_; -- downs PS: If you try to replicate it, it is recommended to use a mid-latency connection so that the transfer doesn't complete immediately. PPS: Interestingly, it works with netcat. Firefox, IE and Opera break though.Socket.send doesn't guarantee the whole thing will send; it returns how many bytes are buffered to be sent.Also, using Socket.shutdown to shutdown sends/writes (included by BOTH) might be aborting the unsent buffer.Yes, on Linux. Not on Win32. (it's even recommended, see the std.socket docs)Finally, after these appropriate changes, I believe there's still a chance that the whole buffer won't be sent if the linger time expires before the whole buffer was able to send.I tried sending linger to 30s. Didn't help any. But thanks for your input. -- downs
May 10 2007
I'd like to think I have a bit of experience with sockets and yet this problem has me a bit stumped too. I decided to pull the calls to the winsock stuff out of the classes and call them directly, eg --------------------- import std.stdio, std.socket, std.string, std.file; import std.c.windows.winsock, std.windows.syserror, std.c.windows.windows; alias std.c.windows.winsock ws; void main(char[][] args) { char[] buf = "TEST"; socket_t l, request; sockaddr_in sin; uint num = 0; int size; sin.sin_addr.s_addr = inet_addr("127.0.0.1"); sin.sin_port = htons(atoi(args[1])); writef(std.string.toString(inet_ntoa(sin.sin_addr)),":", std.string.toString(ntohs(sin.sin_port)),"\n"); l = cast(socket_t)ws.socket(AddressFamily.INET, SocketType.STREAM, ProtocolType.TCP); writef(l,"\n"); writef(ws.ioctlsocket(l, FIONBIO, &num),"\n"); writef(ws.bind(l, cast(sockaddr*)&sin, sin.sizeof),"\n"); writef(ws.listen(l, 16),"\n"); request = cast(socket_t) ws.accept(l, null, null); size = sin.sizeof; ws.getsockname(request, cast(sockaddr*)&sin, &size); writef(std.string.toString(inet_ntoa(sin.sin_addr)),":", std.string.toString(ntohs(sin.sin_port)),"\n"); writef(request,"\n"); writef(ws.ioctlsocket(request, FIONBIO, &num),"\n"); writef(ws.send(request, buf.ptr, buf.length, 0),"\n"); Sleep(5000); writef(ws.shutdown(request, SocketShutdown.BOTH),"\n"); writef(ws.closesocket(request),"\n"); writef(ws.closesocket(l),"\n"); } --------------------- Compiling this with: dmd bug.d wsock32.lib and running with: bug 80 then telnetting to it, i.e. telnet 127.0.0.1 80 gives me this output on screen: 127.0.0.1:80 924 0 0 0 127.0.0.1:80 916 0 4 0 0 0 the actual socket handle values may differ for you but note the send reports 4 and the other calls report 0, no error. The most interesting part is the pause I added, this prevent the socket being closed immediately, however my telnet session connects and immediately closes receiving no data. I'm hoping by posting this someone can tell us both where we went wrong ;) Regan Heath
May 12 2007
Still stumped I tried C this time with the same results. ---------- #include <winsock2.h> #include <stdio.h> #include <stdlib.h> #include <process.h> #define ISEOL(c) (c == '\r' || c == '\n') void print_error() { char bf[1000]; int n,i; n = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), LANG_USER_DEFAULT, //MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), bf, 1000, NULL); for(; n > 0 && ISEOL(bf[n-1]); n--) bf[n-1] = '\0'; for(i = n; i >= 0; i--) { if (!ISEOL(bf[i])) continue; if (bf[i] == '\n') bf[i] = ' '; if (bf[i] == '\r') memmove(&bf[i], &bf[i+1], n-i); } printf("ERROR: %s\n",bf); } void init() { WSADATA wd; if (WSAStartup(0x2020, &wd)) { print_error(); exit(1); } } void main(int argc, char* args[]) { SOCKET l, request; SOCKADDR_IN sin; unsigned long num = 0; int size; init(); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); sin.sin_port = htons(atoi(args[1])); sin.sin_family = AF_INET; printf("%s:%d\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port)); l = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); printf("%d\n",l); printf("%d\n",ioctlsocket(l, FIONBIO, &num)); printf("%d\n",bind(l, (SOCKADDR*)&sin, sizeof(sin))); printf("%d\n",listen(l, SOMAXCONN)); request = accept(l, NULL, NULL); size = sizeof(sin); getsockname(request, (SOCKADDR*)&sin, &size); printf("%s:%d\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port)); printf("%d\n",request); printf("%d\n",ioctlsocket(request, FIONBIO, &num)); printf("%d\n",send(request,"TEST\r\n",6,0)); Sleep(5000); printf("%d\n",shutdown(request, SD_BOTH)); printf("%d\n",closesocket(request)); printf("%d\n",closesocket(l)); } ----------- Compile with: dmc bug.c wsock32.lib Run as: bug 80 Telnet with: telnet 127.0.0.1 80 Results: 127.0.0.1:80 924 0 0 0 127.0.0.1:80 916 0 6 0 0 0 Telnet window connects and closes, no data is seen. Odd. Regan Heath
May 12 2007
Yes, um, I kind of solved it for miniserv in the meantime ^^ But my solution is specific to HTTP. The solution is simply to do another attempt at receiving data right before shutting down. ^^ Now, there are two possibilities. a) The client transmits the header, then shuts down the connection (SocketShutdown.SEND). In this case, the server-side recv call will return zero - connection closed. BUT, strangely, it will also block until all the missing data is sent. b) The client transmits the header, then keeps the connection open (to send another header?) In this case, we simply re-enqueue the socket and get persistent connections practically for free :D The respective miniserv code looks like thus:try { push; /// if this succeeds, we have a persistant connection on our hands scope(success) first(this, (BufferingLayer buf) { buf.push_back(recv); logln("Re-enqueue!"); reenq(buf); }); } catch (Exception e) close; /// nope; the client already shutdown his SEND. Ah well. Terminate.Push basically asks the lowest member of the filter-chain (the socket back-end) to "push" received data to the next higher element. It's basically a receive call. Now, if there was a problem in-between, my socket back-end code will throw an exception (after the blocking recv returns). In this case, we simply close the socket and are done with it. Otherwise, we add the additional data we received to the front of our incoming buffer and re-enqueue the socket. Easy, huh? ^^ I'm not sure how well this method translates to other protocols though, mainly because I don't completely understand why it works myself :p Well, good luck ~ -- downs, happy
May 12 2007
Downs Wrote:Yes, um, I kind of solved it for miniserv in the meantime ^^ But my solution is specific to HTTP. The solution is simply to do another attempt at receiving data right before shutting down. ^^ Now, there are two possibilities. a) The client transmits the header, then shuts down the connection (SocketShutdown.SEND). In this case, the server-side recv call will return zero - connection closed. BUT, strangely, it will also block until all the missing data is sent. b) The client transmits the header, then keeps the connection open (to send another header?) In this case, we simply re-enqueue the socket and get persistent connections practically for free :D The respective miniserv code looks like thus: > try { > push; /// if this succeeds, we have a persistant connection on our hands > scope(success) first(this, (BufferingLayer buf) { buf.push_back(recv); logln("Re-enqueue!"); reenq(buf); }); > } catch (Exception e) close; /// nope; the client already shutdown his SEND. Ah well. Terminate. Push basically asks the lowest member of the filter-chain (the socket back-end) to "push" received data to the next higher element. It's basically a receive call. Now, if there was a problem in-between, my socket back-end code will throw an exception (after the blocking recv returns). In this case, we simply close the socket and are done with it. Otherwise, we add the additional data we received to the front of our incoming buffer and re-enqueue the socket. Easy, huh? ^^ I'm not sure how well this method translates to other protocols though, mainly because I don't completely understand why it works myself :pWell, I'm glad you're situation is resolved but I'm still puzzled as to what I am/was doing wrong. Maybe I'll take another look at it now, with fresh eyes, to figure out where I am going wrong. Regan Heath
May 13 2007
Aha! Don't I feel like a fool. The problems I was having were caused by the Norton internet security product installed on this (not my own) machine. After disabling them it all worked as expected.
May 13 2007