我的TCP服务器是使用Tornado的异步TCP制作的。客户端是用C语言编写的。
服务器代码:
#! /usr/bin/env python
#coding=utf-8
from tornado.tcpserver import TCPServer
from tornado.ioloop import IOLoop
class TcpConnection(object):
def __init__(self,stream,address):
self._stream=stream
self._address=address
self._stream.set_close_callback(self.on_close)
self.send_message(b'hello \n')
self.send_message(b'world \n')
def read_message(self):
self._stream.read_until(b'\n', self.handle_message)
def handle_message(self,data):
print(data)
def send_message(self,data):
self._stream.write(data)
self.read_message()
def on_close(self):
print("the monitored %d has left",self._address)
class MonitorServer(TCPServer):
def handle_stream(self,stream,address):
print("new connection",address,stream)
TcpConnection(stream,address)
if __name__=='__main__':
print('server start .....')
server=MonitorServer()
server.listen(20000)
IOLoop.instance().start()
客户代码:
#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
typedef struct SytemInit
{
char computer[32];
char user[32];
char os[256];
char processor[256];
char mem[128];
char disk[128];
}SYSTEMINIT;
typedef struct Command
{
int commandType;
char commandInfo[256];
}COMMAND;
void main()
{
int err;
SYSTEMINIT message;
COMMAND recvBuf;
SOCKET sockClient;
SOCKADDR_IN addrServer;
WSADATA wsaData;
WORD wVersionRequested;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
return;
}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 2 )
{
WSACleanup( );
return;
}
sockClient = socket(AF_INET, SOCK_STREAM, 0);
addrServer.sin_addr.S_un.S_addr = inet_addr("172.16.110.1");
addrServer.sin_family = AF_INET;
addrServer.sin_port = htons(20000);
connect(sockClient, (SOCKADDR *)&addrServer, sizeof(SOCKADDR));
recv(sockClient, (char*)&recvBuf, 100, 0);
strcpy(message.computer,"zz-pc");
strcpy(message.disk,"zz-disk");
strcpy(message.mem,"zz-men");
strcpy(message.os,"zz-os");
strcpy(message.processor,"zz-processor");
strcpy(message.user,"zz-user");
send(sockClient, (char*)&message, sizeof(message) + 1, 0);
closesocket(sockClient);
WSACleanup();
}
执行时出现以下错误:
ERROR:tornado.application:Error in connection callback
Traceback (most recent call last):
File "/usr/local/lib/python3.4/dist-packages/tornado/tcpserver.py", line 269, in _handle_connection
future = self.handle_stream(stream, address)
File "/home/zz/PycharmProjects/monitor/test.py", line 34, in handle_stream
TcpConnection(stream,address)
File "/home/zz/PycharmProjects/monitor/test.py", line 15, in __init__
self.send_message(b'world \n')
File "/home/zz/PycharmProjects/monitor/test.py", line 25, in send_message
self.read_message()
File "/home/zz/PycharmProjects/monitor/test.py", line 18, in read_message
self._stream.read_until(b'\n', self.handle_message)
File "/usr/local/lib/python3.4/dist-packages/tornado/iostream.py", line 270, in read_until
future = self._set_read_callback(callback)
File "/usr/local/lib/python3.4/dist-packages/tornado/iostream.py", line 658, in _set_read_callback
assert self._read_callback is None, "Already reading"
AssertionError: Already reading
我想这个错误是因为self.send_message(b'hello \n')
和self.send_message(b'world \n')
同时从套接字读取。
我该如何解决这个问题?
答案 0 :(得分:4)
您的此代码:
date_histogram
导致以下结果:
self.send_message(b'hello \n')
self.send_message(b'world \n')
由于您使用回调呼叫self._stream.write(b'hello \n')
self._stream.read_until(b'\n', self.handle_message)
self._stream.write(b'world \n')
self._stream.read_until(b'\n', self.handle_message)
,因此您尝试同时并行执行两个read_until
。但这是无稽之谈,因为它们是通过TCP连接一个接一个地来的。您必须先阅读一条消息,然后阅读另一条消息。
我觉得使用read_until
会让这更容易。你也可以用回调来做;我稍后会说明。
gen.coroutine
以下是我如何使用协同程序更改gen.coroutine
类:
TcpConnection
通过使用协同程序,您可以按照希望它执行的顺序编写代码,并且可以将响应读取为返回值到局部变量中,而不必使用处理程序方法。每当你class TcpConnection(object):
def __init__(self,stream,address):
self._stream=stream
self._address=address
self._stream.set_close_callback(self.on_close)
@gen.coroutine
def send_messages(self):
yield self.send_message(b'hello \n')
response1 = yield self.read_message()
print(response1)
yield self.send_message(b'world \n')
# You can receive the result in-line, but you need to wrap with ( ):
print((yield self.read_message()))
def read_message(self):
return self._stream.read_until(b'\n')
def send_message(self,data):
return self._stream.write(data)
def on_close(self):
print("the monitored %d has left",self._address)
class MonitorServer(TCPServer):
@gen.coroutine
def handle_stream(self,stream,address):
print("new connection",address,stream)
conn = TcpConnection(stream,address)
yield conn.send_messages()
某事时,你就会暂停等待它完成。
我还将yield
和send_message()
分开了,因为我觉得它更清晰了。如果你认为最好将它们放在receive_message()
中,你就可以用这样的方法做到这一点:
send_message()
如果你想首先发送这两个消息,然后然后等待接收这两个消息,你也可以这样做:
@gen.coroutine
def send_message(self,data):
yield self._stream.write(data)
return (yield self.receive_message())
你可以使用协同程序编写任何代码,你可以使用回调进行编码。但是,您需要做的是跟踪回调之间的状态(您所处的位置)。这可以通过在不同的回调之间跳转来完成。例如:
@gen.coroutine
def send_messages(self):
yield self.send_message(b'hello \n')
yield self.send_message(b'world \n')
print((yield self.read_message()))
print((yield self.read_message()))
或通过其他方式跟踪您在通信中的位置,例如将某些内容存储在类实例的字段中。