mIRC Home    About    Download    Register    News    Help

Print Thread
#242523 28/07/13 02:10 PM
Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
I'm using udp socket a lot in a server/clients relationship.
/sockudp -k is used to keep the connection opened, in order to listen for packets.
Sometimes (rare), the udp socket on the server side will close itself (note that one of the client might be running on the same mirc.exe instance)
I don't think the socket closing itself is a normal behavior, and there is no way to catch that event.
Unfortunately I cannot reproduce on demand, but I think it's happening more often if there are several clients.

Edit: I actually can reproduce on demand now, I just let the socket idling (no /sockudp command used from any clients or from the server), and after waiting a bit and releasing the pause (/sockudp used again) the socket closes, this is on mIRC 7.32, Windows 7 64bits, I can reproduce 100% of the time with these steps.

Last edited by Wims; 28/07/13 03:35 PM.

#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
Additional informations:
In the original context where I could reproduce everytime, I had 12 clients on the server, 6 of my own connections, 5 of the same person else, and 1 of someone else.
I tried writting a small code to reproduce it (locally), I couldn't.
I just tried again under the same condition (6 of my own, 5 of the same guy, but the last one was a different person this time) and I cannot reproduce even once.
There must be something else than not sending any data for a few minutes and then sending again.
Note that the original context is a game where the server sends data to all clients each 45 ms (eventually clients send data to the server).


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
If Winsock reports that the socket has been closed, or that there has been an error, mIRC will feed that back to the script. How are you detecting that the socket has been closed? During a sockwrite/sockread/sockclose event? Is an error reported?

Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
When testing with a script supposed to reproduce the behavior, I was running a timer checking if the sockname was there, if not, display a message that it has been closed and stop the timer; after idling, one /sockudp would close the socket.

Inside the real game, I would only notice it because the game use UDP to allow people to move, so nobody would be moving anymore.

Server side, the game use the following on udpread event:
Code:
on *:udpread:pacuserv:{
  var %id $+($sock(pacuserv).saddr,/,$sock(pacuserv).sport)
  if ($sockerr) { echo 4 -s * pac_serv udpread ( %id ) error $v1 $sock(pacuserv).wserr - $sock(pacuserv).wsmsg | return }
  sockread -n &a
  if ($bvar(&a,0)) {
    tokenize 32 $bvar(&a,1-512).text
    ;handle message here
  }
}
The server doesn't use anything else (regarding UDP), no on sockwrite, on sockread is not for UDP, and I checked, on sockclose is not triggered (server side) when it happens. the $sockerr part of that event is not triggered either when it happens.

Client side, there is also only a on udpread event:
Code:
on *:udpread:pacuclient:{
  if ($sockerr) { echo -s udpread client error: $v1 - $sock(pacuclient).wserr $sock(pacuclient).wsmsg | return }
  sockread -n &a
  ;handle message here
}
When it happens, NO error are reported at all, I have the same check for $sockerr inside the TCP events I use, but should be all irrelevant, it's been a long time since I didn't try to reproduce.


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
UDP can trigger on UDPREAD, UDPWRITE, and SOCKCLOSE events, so you would need to check all of those events in case Winsock reports an error. Apart from that, I cannot see anything in the source code that would cause a socket to be closed quietly in the background without triggering a script event. Perhaps adding more debugging might help narrow it down, such as checking if a socket still exists after a /sockread?

Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
It's a chance on UDPWRITE is properly documented in the help file!!! according to uhelp.chm , on UDPWRITE is not triggering, and indeed, I couldn't get it to work. on sockwrite actually works for UDP, I'll use that

I stated it wasn't triggering on sockclose.
I'll try adding a $sockerr check after each udp command and inside on udpwritesockwrite, however, if I stated previously I could reproduce all the time, that was actually with a specific person connected, I can't reproduce just like that otherwise.
I still strongly think it's related to letting the udp socket idling: that's actually the only way I have ever reproduced it. After idling, just one /sockudp from the server would cause the socket to no longer exist.

Last edited by Wims; 13/10/13 05:23 PM.

#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
The on UDPWRITE event should normally not be used, as it has no purpose when using UDP. That is why it is not documented. However, it exists in the code for debugging purposes, so it might help you track down the issue.

I have rechecked the source code and cannot find any situation where a socket will be closed without triggering a script event. There is no code that closes a socket just for idling.

If you find a way to reproduce the issue reliably, please let us know.

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
You should not be able to receive on SOCKWRITE events with a UDP socket. Can you post a minimal script that reproduces this for you?

Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
Sure:
Code:
alias udp_onsw {
  sockclose SERVER
sockclose CLIENT
  sockudp -k SERVER 8000
  sockudp -kn CLIENT 127.0.0.1 8000 this is a message
  .timer 1 2 sockclose SERVER $(|) sockclose CLIENT
}
on *:sockwrite:CLIENT:{
 echo -a CLIENT just sent data
}
If on udpwrite wasn't meant to be used with UDP, then why are you suggesting I use it, in which case is it triggered?

On sockwrite has been working with udp socket since ever: http://www.zigwap.com/mirc/sockets_udp see the getTime alias, which eventually triggers on sockwrite with $sockerr set to 0.

Last edited by Wims; 13/10/13 05:47 PM.

#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Mar 2010
Posts: 57
W
Babel fish
Offline
Babel fish
W
Joined: Mar 2010
Posts: 57
Originally Posted By: Khaled
You should not be able to receive on SOCKWRITE events with a UDP socket.


I really don't think that's correct. The on sockwrite event certainly triggers for UDP sockets where you can send additional data as well as check for errors and socket status/info. I know it works, I've used it to send additional data as well as to check for errors plenty of times before.

Last edited by Wiz126; 13/10/13 09:15 PM.
Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
Code:
If on udpwrite wasn't meant to be used with UDP, then why are you suggesting I use it, in which case is it triggered?

I suggested using it because I noticed it was an event in the source code that might help you track down the issue.

Code:
On sockwrite has been working with udp socket since ever: http://www.zigwap.com/mirc/sockets_udp see the getTime alias, which eventually triggers on sockwrite with $sockerr set to 0. 

That is very odd. As far as I can tell, this event should not trigger for UDP sockets. The on SOCKWRITE event is used with TCP sockets to let you know when you can send more data or to let you know if there is an error. I will have to look into this more closely as it could be related to the issue you are reporting.

Joined: Mar 2010
Posts: 57
W
Babel fish
Offline
Babel fish
W
Joined: Mar 2010
Posts: 57
Originally Posted By: Khaled
That is very odd. As far as I can tell, this event should not trigger for UDP sockets. The on SOCKWRITE event is used with TCP sockets to let you know when you can send more data or to let you know if there is an error. However, UDP events are inherently unreliable and there is no guarantee that you will receive any errors at all if a packet does not reach its destination.

If the on sockwrite should not trigger for UDP sockets, how does one prevents filling up the queue? or send more data once the queue is filled. (and prevent getting things like "* /sockudp: 'test' queue would exceed 16384 bytes").

Even though UDP is unreliable (although it mostly is successful), the $sockerr on sockwrite should surely relay the possible errors returned by winsock, is that not the case?

Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
It sounds like you made the event but implemented both tcp and udp inside on sockwrite event.
Quote:
I will have to look into this more closely as it could be related to the issue you are reporting.
Exactly what I thought! Thanks for looking into it

Last edited by Wims; 13/10/13 06:15 PM.

#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
Code:
If the on sockwrite should not trigger for UDP sockets, how does one prevents filling up the queue? or send more data once the queue is filled. (and prevent getting things like "* /sockudp: 'test' queue would exceed 16384 bytes").

You are quite right, I forgot that I added internal buffer support to UDP as well as TCP. It abstracts away how UDP works for the scripter but because UDP is unreliable, and may or may not report errors, the internal buffer support and SOCKWRITE/UDPWRITE events are of more limited use with UDP. I am still not sure why UDPWRITE was not added to the help file or why SOCKWRITE is triggering in its place. It has been quite a few years since I last looked at the socket code, so I will need to look through it more closely.

Joined: Dec 2002
Posts: 5,411
Hoopy frood
Offline
Hoopy frood
Joined: Dec 2002
Posts: 5,411
Right, it appears that there are some issues/discrepancies with the implementation and/or documentation. Custom sockets support was added in 1997 and UDP support was added in 1999, which is probably what caused this.

(1) When sending UDP data, there is no guarantee that UDP will return an error, so a UDPWRITE event seemed unnecessary. That is why it was never documented. However, due to the custom sockets internal buffer support, on SOCKWRITE was being triggered when data was ready to be sent on a socket. The internal buffer support should really have been triggering on UDPWRITE to be consistent.

(2) An on UDPWRITE event does exist, however it is only triggered when there are socket errors. So on SOCKWRITE will not detect UDP errors. However, as UDP errors are rarely, if ever, reported by winsock, this may not make much difference.

(3) From an implementation point of view, the UDPWRITE/UDPREAD events should probably not have been added as UDP could have continued to use the SOCKWRITE/SOCKREAD events. The scripter would know which sockets were UDP or TCP based on the socket name.

As it stands, the most backward-compatible solution would be to change (2) so that on SOCKWRITE is triggered when an error occurs and to remove the UDPWRITE event as it is not documented any way. This change will be in the next version.

That said, this may not resolve the original issue reported in this post, so we will need to look at this again once a public beta is released.

Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
So I've been working on my game (which had this udp server/client relationship) recently, I eventually got this udp bug again.. After a lot of time spent, I just figured it out.
Basically the issue is that mIRC will close the udp socket when you tell it to send a packet to a "wrong" ip/port combination.

First, two smalls issues I just came accross, this is all tested on 7.39 beta4, but should certainly behave the same on beta6.

1) "/sockudp -k SERVER" doesn't do anything, I think it should report a syntax error.

2) "/sockudp -kn SERVER" triggers on sockwrite with $sockerr set to 3 with the winsock error message: "[10047] Address family not supported by protocol family", should report a syntax error as well

Now the original issue:

Code:
on *:udpread:SERVER:{
  if ($sockerr) { echo -s on udpread error on SERVER: $v1 - $sock(SERVER).wsmsg | return }
  var %a
  sockread %a
  if (!$sockbr) return
  echo -s line recieved on SERVER from $sock(SERVER).saddr $sock(SERVER).sport : %a
  sockudp -kn SERVER $sock(SERVER).saddr $sock(SERVER).sport WORLD
}
on *:sockclose:SERVER:{
  if ($sockerr) { echo -s on sockclose error on SERVER: $v1 - $sock(SERVER).wsmsg | return }
  echo -s on sockclose on SERVER, no error
}
on *:sockwrite:SERVER:{
  if ($sockerr) { echo -s on sockwrite error on SERVER: $v1 - $sock(SERVER).wsmsg | return }
  echo -s on sockwrite on SERVER, no error
}

on *:udpread:CLIENT*:{
  if ($sockerr) { echo -s on udpread error on $sockname $+ : $v1 - $sock(SERVER).wsmsg | return }
  var %a
  sockread %a
  if (!$sockbr) return
  echo -s line recieved on CLIENT $sockname from $sock($sockname).saddr $sock($sockname).sport : %a
}
on *:sockclose:CLIENT*:{
  if ($sockerr) { echo -s on sockclose error on CLIENT $sockname $+ : $v1 - $sock($sockname).wsmsg | return }
  echo -s on sockclose on CLIENT $sockname $+ , no error
}
on *:sockwrite:CLIENT*:{
  if ($sockerr) { echo -s on sockwrite error on CLIENT $sockname $+ : $v1 - $sock($sockname).wsmsg | return }
  echo -s on sockwrite on CLIENT $sockname $+ , no error
}
Execute:
Code:
/sockudp -k SERVER 8001
/sockudp -kn CLIENT1 127.0.0.1 8001 HELLO
/sockudp -kn CLIENT2 127.0.0.1 8001 HELLO
/sockclose CLIENT2
/sockudp -kn SERVER 127.0.0.1 SOURCE_PORT_CLIENT1 TEST
Replace SOURCE_PORT_CLIENT1 with the source port displayed in your test.
Quote:

line recieved on SERVER from 127.0.0.1 SOURCE_PORT_CLIENT1 : HELLO
on sockwrite on CLIENT CLIENT1, no error
on sockwrite on SERVER, no error
line recieved on CLIENT CLIENT1 from 127.0.0.1 8001 : WORLD
line recieved on SERVER from 127.0.0.1 SOURCE_PORT_CLIENT2 : HELLO
on sockwrite on CLIENT CLIENT2, no error
on sockwrite on SERVER, no error
line recieved on CLIENT CLIENT2 from 127.0.0.1 8001 : WORLD
on sockwrite on SERVER, no error
on sockclose error on SERVER: 3 - [10054] Connection reset by peer


So after closing the socket client-side, trying to send a packet to that client results in the server socket itself being closed, which is wrong, mIRC should eventually trigger on sockwrite with $sockerr set, but it should keep the socket (SERVER here) opened because there are others client 'connected' to it. In the actual configuration, You can still use /sockudp -kn SERVER 127.0.0.1 SOURCE_PORT_CLIENT2 TEST to communicate from the server to the client2, but client2 cannot send back a packet.


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Jul 2006
Posts: 4,145
W
Wims Offline OP
Hoopy frood
OP Offline
Hoopy frood
W
Joined: Jul 2006
Posts: 4,145
Eh, this post was meant to bump the thread but actually, Saturn tried it after the bump and found an unfortunate typo/mistake:
Quote:
/sockudp -k SERVER 8001
/sockudp -kn CLIENT1 127.0.0.1 8001 HELLO
/sockudp -kn CLIENT2 127.0.0.1 8001 HELLO
/sockclose CLIENT2
/sockudp -kn SERVER 127.0.0.1 SOURCE_PORT_CLIENT1 TEST
was meant to be:
Quote:
/sockudp -k SERVER 8001
/sockudp -kn CLIENT1 127.0.0.1 8001 HELLO
/sockudp -kn CLIENT2 127.0.0.1 8001 HELLO
/sockclose CLIENT1
/sockudp -kn SERVER 127.0.0.1 SOURCE_PORT_CLIENT1 TEST


#mircscripting @ irc.swiftirc.net == the best mIRC help channel
Joined: Apr 2004
Posts: 871
Sat Offline
Hoopy frood
Offline
Hoopy frood
Joined: Apr 2004
Posts: 871
Originally Posted By: Wims
So after closing the socket client-side, trying to send a packet to that client results in the server socket itself being closed, [..]

This is in fact intended WinSock behavior: if an ICMP "port unreachable" packet arrives for a UDP socket, this will produce an asynchronous WSAECONNRESET error on the UDP socket and close it. Terrible behavior as far as I'm concerned, but it's hard to blame mIRC for closely following WinSock behavior.

While recreating the socket after an error is possible, this will necessarily create a time window in which packets arriving from other hosts would be lost - this is true both when scripted and if mIRC were to do this natively. I think that the best solution would be to use the (relatively new) SIO_UDP_CONNRESET ioctl to disable the behavior on UDP sockets, either always or with a new "don't kill my socket on errors" /sockudp flag.

Edit: upon reading the documentation again, I'm no longer sure whether WinSock actually expects applications to close UDP sockets upon getting ECONNRESET as well. Could it be that the text "should close the socket" text applies to TCP sockets only, and mIRC is unnecessarily closing the UDP socket by incorrectly assuming the error is fatal to it, too? I can't easily test this, but the MSDN text is rather ambiguous I think..

Last edited by Sat; 28/06/17 11:31 PM.

Saturn, QuakeNet staff

Link Copied to Clipboard