Проблемы с реализацией сетевого сервера с SocketServer - PullRequest
0 голосов
/ 02 октября 2010

Я новичок в программировании сокетов и мне нужна ваша помощь.Я реализовал простой эхо-сервер, используя пример ThreadedTCPServer из документации по Python.Он работает нормально, но у меня есть следующие проблемы:

  1. Сервер зависает (в socket.recv), когда Клиент пытается отправить данные нулевой длины.
  2. Сервер зависает (в сокете.recv) когда данные, отправленные Клиентом, кратны BUF_SIZE.
  3. Я не знаю, как правильно остановить сервер извне.Я хотел бы иметь скрипт, например, stopServer.py, который может быть запущен с хоста сервера, когда нужно остановить сервер.Я реализовал команду "STOP", которая отправляется на порт сервера.Этот подход выглядит хорошо для меня, но имеет риск для безопасности.Обратите внимание, что мне нужно кроссплатформенное решение.Так что, вероятно, сигналы не подходят.

Буду признателен, если у вас есть идеи, как решить проблемы, перечисленные выше.Код примера приведен ниже.

Хорошего дня!Захар

client.py

import sys
import socket
from common import recv

if len (sys.argv) == 1 :
    print "The file to be sent is not specified"
    sys.exit (1)
fname = sys.argv [1]

print "Reading '%s' file..." % fname,
f = open (sys.argv [1])
msg = f.read ()
f.close()
print "OK"

if len (msg) == 0 :
    print "Nothing to send. Exit."
    sys.exit (0)

print "Client is sending:"
print msg
print "len (msg)=%d" % len (msg)

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.connect ( ('localhost', 9997) )
sock.sendall (msg)
print "Sending has been finished"
print "Waiting for response..."
response = recv (sock)
print "Client received:"
print response
print "len (response)=%d\n" % len (response)
sock.close ()

server.py

import threading
import SocketServer
from common import recv

class ThreadedTCPRequestHandler (SocketServer.BaseRequestHandler):

    def handle(self) :
        recvData = recv (self.request)
        print "NEW MSG RECEIVED from %s" % self.client_address [0]
        print "Data:"
        print recvData
        print "dataSize=%d" % len (recvData)
        if recvData == "!!!exit!!!" :
            print 'Server has received exit command. Exit.'
            self.server.shutdown()
        else :
            curThread = threading.currentThread ()
            response = "%s: %s" % (curThread.getName (), recvData)
            self.request.sendall (response)

class ThreadedTCPServer (SocketServer.ThreadingMixIn, SocketServer.TCPServer) :
    pass

if __name__ == "__main__":
    HOST, PORT = "localhost", 9997

    server = ThreadedTCPServer ((HOST, PORT), ThreadedTCPRequestHandler)
    server.serve_forever ()

common.py

'''
Common code for both: client and server.
'''

def recv (sock) :
    '''
    Reads data from the specified socket and returns them to the caller.
    IMPORTANT:
    This method hangs in socket.recv () in the following situations (at least
    on Windows):
    1. If client sends zero-length data.
    2. If data sent by client is multiple of BUF_SIZE.
    '''
    BUF_SIZE = 4096
    recvData = ""
    while 1 :
        buf = sock.recv (BUF_SIZE)
        if not buf :
            break
        recvData += buf
        if len (buf) < BUF_SIZE : # all data have been read
            break
    return recvData

Ответы [ 2 ]

2 голосов
/ 02 октября 2010

Я не знаю python, но я попытаюсь ответить на основании моих знаний в области программирования сокетов.

Сообщение с нулевым байтом НЕ приведет к выходу "sock.recv".См. здесь для объяснения.Если sock.recv возвращается с длиной 0, это указывает на то, что другая сторона отключила TCP-соединение.

if len (buf) < BUF_SIZE

Как эта строка определяет, что ВСЕ данные были прочитаны.TCP recv call не является отображением 1-1 между отправляющим вызовом TCP, то есть то, что клиент отправляет за 1 отправляющий вызов, может быть получено в нескольких вызовах recv.

Когда вы отправляете несколько из BUF_SIZE, т.е., скажем, 2 * BUF_SIZE, вполне возможно, что вы можете получить все это за один раз.В этот момент ваш сервер зависнет как len(buf) > BUF_SIZE, и вы застрянете в sock.recv.Попробуйте и выясните, как использовать неблокирующие сокеты.

0 голосов
/ 17 октября 2010

После некоторых экспериментов я обнаружил следующее:

  1. Необходимо переименовать common.recv в common.recvall
  2. Нужно прокомментировать следующие 2 строки в common.recv (ошибка):

    if len (buf) < BUF_SIZE : # all data have been read
    break
    
  3. Этот пример будет работать, только если данные, отправленные на сервер, могут быть прочитаны одним вызовом socket.recv на стороне сервера. Таким образом, отправленные данные должны быть меньше, чем BUF_SIZE. В противном случае клиент и сервер находятся в тупике, оба в своем вызове sock.recv. Это происходит из-за одновременного доступа к общему ресурсу (сокету). Чтобы решить эту проблему, клиент должен прослушать ответ на другом порту.
...