Я использую python asynchat для реализации сетевого протокола. Во время соединения мне нужно отправить команду и ответ сервера с сеансом.
Моя основная проблема заключается в том, что мне нужно подождать, пока не получу ответ сеанса. но не уверен, как это реализовать. следует ли использовать socket.recv для настройки соединения? это хорошая идея?
При написании сетевого приложения, использующего асинхронные методы, вы ждете, записывая свое состояние где-то, а затем продолжаете основной цикл. В будущем вам будут доступны данные, которые вы ожидаете, основной цикл уведомит вас об этом факте, и вы можете объединить новые данные с записанным состоянием для выполнения любой задачи, над которой вы работаете. В зависимости от конкретной задачи вам может потребоваться пройти этот цикл много раз, прежде чем ваша задача будет выполнена.
Эти идеи в основном одинаковы независимо от того, какую асинхронную систему вы используете. Однако Twisted - это превосходная система для асинчат, поэтому я не буду пытаться объяснить какие-либо детали асинчатча. Вместо этого, вот пример, который делает то, о чем вы спрашиваете, используя Twisted:
from twisted.internet.defer import Deferred
from twisted.internet.protocol import Protocol, Factory
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet import reactor
# Stream-oriented connections like TCP are handled by an instance
# of a Protocol subclass
class SomeKindOfClient(Protocol):
# When a new connection is established, the first thing that
# happens is this method is called.
def connectionMade(self):
# self.transport is set by the superclass, and lets us
# send data over the connection
self.transport.write("GREETING")
# a Deferred is a generic, composable API for specifying
# callbacks
self.greetingComplete = Deferred()
# Here some local state
self._buffer = ""
# Whenever bytes arrive on the TCP connection, they're passed
# to this method
def dataReceived(self, bytes):
# Incorportate the network event data into our local state.
# This kind of buffering is always necessary with TCP, because
# there no guarantees about how many bytes will be delivered
# at once (except that it will be at least 1), regardless of
# the size of the send() the peer did.
self._buffer += bytes
# Figure out if we're done - let say the server response is 32
# bytes of something
if len(self._buffer) >= 32:
# Deliver it to whomever is waiting, by way of the Deferred
# object
greeting, self._buffer = self._buffer[:32], self._buffer[32:]
complete = self.greetingComplete
self.greetingComplete = None
complete.callback(greeting)
# Otherwise we'll keep waiting until dataReceived is called again
# and we have enough bytes.
# One of the normal ways to create a new client connection
f = Factory()
f.protocol = SomeKindOfClient
e = TCP4ClientEndpoint(reactor, "somehost", 1234)
# Connect returns one of those Deferreds - letting us specify a function
# to call when the connection is established. The implementation of
# connect is also doing basically the same kind of thing as you're asking
# about.
d = e.connect(f)
# Execution continues to this point before the connection has been
# established. Define a function to use as a callback when the connection
# does get established.
def connected(proto):
# proto is an instance of SomeKindOfClient. It has the
# greetingComplete attribute, which we'll attach a callback to so we
# can "wait" for the greeting to be complete.
d = proto.greetingComplete
def gotGreeting(greeting):
# Note that this is really the core of the answer. This function
# is called *only* once the protocol has decided it has received
# some necessary data from the server. If you were waiting for a
# session identifier of some sort, this is where you might get it
# and be able to proceed with the remainder of your application
# logic.
print "Greeting arrived", repr(greeting)
# addCallback is how you hook a callback up to a Deferred - now
# gotGreeting will be called when d "fires" - ie, when its callback
# method is invoked by the dataReceived implementation above.
d.addCallback(gotGreeting)
# And do the same kind of thing to the Deferred we got from
# TCP4ClientEndpoint.connect
d.addCallback(connected)
# Start the main loop so network events can be processed
reactor.run()
Чтобы узнать, как это происходит, вы можете запустить простой сервер (например, nc -l 1234
) и указать на него клиента. Вы увидите приветствие, и вы можете отправить несколько байтов обратно. После того, как вы отправите назад 30, клиент распечатает их (а затем повесить на неопределенное время, потому что я не реализовал в этом протоколе больше никакой логики).
recv
и он будет блокироваться, пока не будет что почитать.