Hello,
I have a bug in my application (or somewhere else) that uses
QTcpServer, and I didn't figure out where it comes from.
Here is the code of the method incomingConnection of the Server:
def __init__(self, parent=None):
self.games = []
self.gameId = 0
super(GameServer, self).__init__(parent)
self.players = {}
self.idList = [0]
self.startLck.lock()
def incomingConnection(self, socketId):
if len(self.idList) > 2:
return
i = 0
playerId = random.randint(10,300000)
while i < len(self.idList):
if self.idList[i] == playerId:
playerId = random.randint(10,300000)
else:
i += 1
self.idList[i-1] = playerId
self.idList.append(0)
if not socket.setSocketDescriptor(socketId):
self.emit(SIGNAL("error(int)"), socket.error())
return
self.players[playerId] = socket
stream.writeUInt16(0)
stream.writeUInt32(playerId)
stream.device().seek(0)
stream.writeUInt16(reply.size() - SIZEOF_UINT16)
socket.write(reply)
socket.waitForBytesWritten()
thread = GameServerThread(self, self.players, playerId, self.startLck)
self.connect(thread, SIGNAL("finished()"),
thread, SLOT("deleteLater()"))
self.connect(thread, SIGNAL("debugPymagic"), self.DEBUG_GameServer)
thread.start()
if len(self.idList) == 3:
self.startLck.unlock()
class GameServer(QTcpServer):
def __init__(self, parent=None):
self.games = []
self.gameId = 0
super(GameServer, self).__init__(parent)
self.players = {}
self.idList = [0]
self.startLck = QMutex()
self.startLck.lock()
def incomingConnection(self, socketId):
if len(self.idList) > 2:
return
i = 0
playerId = random.randint(10,300000)
while i < len(self.idList):
if self.idList[i] == playerId:
playerId = random.randint(10,300000)
else:
i += 1
self.idList[i-1] = playerId
self.idList.append(0)
socket = QTcpSocket()
if not socket.setSocketDescriptor(socketId):
self.emit(SIGNAL("error(int)"), socket.error())
return
self.players[playerId] = socket
reply = QByteArray()
stream = QDataStream(reply, QIODevice.WriteOnly)
stream.setVersion(QDataStream.Qt_4_6)
stream.writeUInt16(0)
stream.writeUInt32(playerId)
stream.device().seek(0)
stream.writeUInt16(reply.size() - SIZEOF_UINT16)
socket.write(reply)
socket.waitForBytesWritten()
thread = GameServerThread(self, self.players, playerId, self.startLck)
self.connect(thread, SIGNAL("finished()"),
thread, SLOT("deleteLater()"))
self.connect(thread, SIGNAL("debugPymagic"), self.DEBUG_GameServer)
thread.start()
if len(self.idList) == 3:
self.startLck.unlock()
To copy to clipboard, switch view to plain text mode
Here is the code of the thread that is started at incoming connection:
def __init__(self, parent, players, playerId, startLck):
super(GameServerThread, self).__init__(parent)
self.startLck = startLck
self.players = players
self.playerId = playerId
self.nextBlockSize = 0
def run(self):
self.startLck.lock()
self.startLck.unlock()
socketToNotify = []
for key, socket in self.players.items():
if key != self.playerId:
socketToNotify.append(socket)
else:
self.socket = socket
self.nextBlockSize = 0
if self.socket.bytesAvailable() < SIZEOF_UINT16:
while True:
self.socket.waitForReadyRead(-1)
if self.socket.bytesAvailable() >= SIZEOF_UINT16:
break
self.nextBlockSize = stream.readUInt16()
if self.socket.bytesAvailable() < self.nextBlockSize:
while True:
self.socket.waitForReadyRead(-1)
if self.socket.bytesAvailable() >= self.nextBlockSize:
break
action = stream.readUInt16()
gameId = stream.readUInt16()
playerId = stream.readUInt32()
params = []
if action in (Action.SetCardPos, Action.SetCardZone):
cardId = stream.readUInt32()
params.append(Parameter(cardId, Parameter.UInt32))
if action == Action.SetCardPos:
stream >> pos
params.append(Parameter(pos, Parameter.Position))
elif action == Action.SetCardZone:
cardName = stream.readQString()
zone = stream.readUInt16()
params.append(Parameter(cardName, Parameter.String))
params.append(Parameter(zone, Parameter.UInt16))
if cardName == "token":
color = stream.readQString()
power = stream.readUInt16()
toughness = stream.readUInt16()
params.append(Parameter(color, Parameter.String))
params.append(Parameter(power, Parameter.UInt16))
params.append(Parameter(toughness, Parameter.UInt16))
else:
print("GameSeverThread: Unknow action!")
continue
else:
print("GameSeverThread: action [%d] unhandled!" % action)
continue
for sck in socketToNotify:
self.sendRequest(sck, action, gameId, playerId, params)
class GameServerThread(QThread):
def __init__(self, parent, players, playerId, startLck):
super(GameServerThread, self).__init__(parent)
self.startLck = startLck
self.players = players
self.playerId = playerId
self.nextBlockSize = 0
def run(self):
self.startLck.lock()
self.startLck.unlock()
socketToNotify = []
for key, socket in self.players.items():
if key != self.playerId:
socketToNotify.append(socket)
else:
self.socket = socket
while self.socket.state() == QAbstractSocket.ConnectedState:
stream = QDataStream(self.socket)
stream.setVersion(QDataStream.Qt_4_6)
self.nextBlockSize = 0
if self.socket.bytesAvailable() < SIZEOF_UINT16:
while True:
self.socket.waitForReadyRead(-1)
if self.socket.bytesAvailable() >= SIZEOF_UINT16:
break
self.nextBlockSize = stream.readUInt16()
if self.socket.bytesAvailable() < self.nextBlockSize:
while True:
self.socket.waitForReadyRead(-1)
if self.socket.bytesAvailable() >= self.nextBlockSize:
break
pos = QPoint()
action = stream.readUInt16()
gameId = stream.readUInt16()
playerId = stream.readUInt32()
params = []
if action in (Action.SetCardPos, Action.SetCardZone):
cardId = stream.readUInt32()
params.append(Parameter(cardId, Parameter.UInt32))
if action == Action.SetCardPos:
stream >> pos
params.append(Parameter(pos, Parameter.Position))
elif action == Action.SetCardZone:
cardName = stream.readQString()
zone = stream.readUInt16()
params.append(Parameter(cardName, Parameter.String))
params.append(Parameter(zone, Parameter.UInt16))
if cardName == "token":
color = stream.readQString()
power = stream.readUInt16()
toughness = stream.readUInt16()
params.append(Parameter(color, Parameter.String))
params.append(Parameter(power, Parameter.UInt16))
params.append(Parameter(toughness, Parameter.UInt16))
else:
print("GameSeverThread: Unknow action!")
continue
else:
print("GameSeverThread: action [%d] unhandled!" % action)
continue
for sck in socketToNotify:
self.sendRequest(sck, action, gameId, playerId, params)
To copy to clipboard, switch view to plain text mode
I use PyQt 4.8.1 with SIP 4.11.2 (and was experiencing the same
problem with older versions) under python 3.1.2.
To summarize, it's a client-server application, and the approach is a
lot inspired from the multi-threaded example in the "Rapid GUI
Programming With Python and Qt" book,
with minor corrections to avoid packet shifting at reception (which is
not related to the problem, as it was here before these corrections).
The problem is that during execution the server seems to behave
strangely at some point in time. I printed some information to monitor
what happens, and I saw that
at the beginning the server receives packets correctly (the size of
packets is around 30 bytes, and I use, like in the book example, a
quint16 at the beginning of each block to store the size of data).
But at some point (which seems to be random), the server gets packets
that seems corrupted, where the quint16 read from the socket (which
correspond to the "nextBlockSize") by the server (for processing the
"nextBlock") is the value 0!
The problem seems to be at the server (reception) side, because when I
print the size written in the packet at the client side, all is ok.
Besides, I always test the connection state, and there is no problem
at this side.
The emission of packets (at the client side) depends on the movement
of a QGraphicsItem which is controlled by the mouse. Thus the
throughput of sent network packets depends on the user which interact
with the QGraphicsItems.
Note that I have the same problem, either testing this application
(client + server) on the same machine (connecting to localhost), or
using two machines on the same LAN.
Any help will be welcome. Don't hesitate to ask me more questions.
Thanks in advance.
Bookmarks