bytesAvailable, readUTF() and ProgressEvent.SOCKET_DATA
I have a multiplayer game, which reads XML data from the server:
_socket = new Socket();
_socket.addEventListener(ProgressEvent.SOCKET_DATA, handleTcpData);
.....
private function handleTcpData(event:Event):void {
while (_socket.bytesAvailable) {
var str:String = _socket.readUTF();
updateGUI(str);
}
}
In most cases it works ok, but sometimes users complain that
the game hangs for them. They are able to send in commands to
the server, but from their descriptions I suspect, that the function
above doesn't work well for them and the updateGUI()
stops being called.
This issue is difficult to debug... I think my server (in Perl/C, non-forking, uses poll()) works well, I must be doing something wrong at the Flash side.
Is it a good idea to call readUTF()
here? It could well be that
only part of the UTF string arrives and then the Flash movie
would freeze in readUTF()
, wouldn't it? (I've never seen that though)
UPDATE:
Yes thanks, I need to catch EOFError
, also I've got a good advise
in a mailing list that I must read _socket.bytesAvailable
bytes
into a ByteArray
and work with that (check if my complete message
arrived) - anything else is unreliable.
So I've come up with this, but still have a flaw there:
private function handleTcpData(event:Event):void {
var len:uint;
if (0 == _socket.bytesAvailable)
return;
try {
_socket.readBytes(_ba, _ba.bytesAvailable, _socket.bytesAvailable);
// read the length of the UTF string to follow
while (_ba.bytesAvailable >= 2) {
len = _ba.readUnsignedShort();
// invalid length - reset
if (0 == len) {
trace('XXX invalid len = ' + len);
_ba.position = 0;
_ba.length = 0;
return;
}
if (_ba.bytesAvailable >= len) {
var str:String = _ba.readUTFBytes(len);
开发者_如何转开发 updateGUI(str);
// copy the remaining bytes
// (does it work? can it be improved?)
var newBA:ByteArray = new ByteArray();
newBA.readBytes(_ba);
_ba = newBA;
}
}
} catch(e:Error) {
trace('XXX ' + e);
_ba.position = 0;
_ba.length = 0;
return;
}
}
- when there are too many bytes available, for example 800 and my messages are only 400 bytes long, then it stucks.
Also I wonder, if copying remaining bytes could be improved (i.e. without allocating a new ByteArray each time)?
I can reproduce this flaw often, luckily
I think, I've found the solution. Due to TCP-nature there can be 2 problematic cases:
1) several UTF-strings with prefixes arrive at once 2) an incomplete UTF-string with prefix arrives
This code should handle both:
private function handleTcpData(event:Event):void {
// CASE 1, JUST KEEP EXTRACTING UTF-STRINGS
while(_socket.bytesAvailable) {
try{
var str:String = _socket.readUTF();
updateGUI(str);
}catch(e:Error){
// CASE 2 INCOMPLETE STRING,
// WILL BE READ BY LATER handleTcpData CALL
return;
}
}
}
Also, in my original code I was copying the ByteArray wrong way. I should have:
var newBA:ByteArray = new ByteArray();
_ba.readBytes(newBA);
_ba = newBA;
or even do it in-place without creating newBA.
All socket.read*
methods may throw EOFError
and IOError
, so make sure you are catching them. It may be something as simple as that?
Another thought, the readUTF()
call assumes that the first two bytes (a short) should contain the length in bytes of the UTF-string. If your perl server doesn't send that length, it may be that the readUTF()
call interprets the first UTF character as the byte length, and thus ends up waiting for more data which never arrives.
Try using:
var str:String = _socket.readUTFBytes(_socket.bytesAvailable);
Other than that, you really need to be able to reproduce the issue or it's quite impossible to know if you have fixed it.
精彩评论