Проблема с Python Sockets: как получить надежно отправленные данные независимо от браузера? - PullRequest
1 голос
/ 03 октября 2008

Я написал небольшие программы на Python + Ajax (перечислены в конце) с модулем сокета для изучения концепции асинхронной связи COMET.

Идея состоит в том, чтобы позволить браузерам отправлять сообщения друг другу в режиме реального времени через мою программу python.

Хитрость заключается в том, чтобы открыть соединение "GET messages / ..." в ожидании ответа на сообщение.

Моя проблема в основном в надежности того, что у меня есть через socket.recv ...

Когда я POST из Firefox, он работает хорошо.

Когда я отправляю POST из Chrome или IE, «данные», которые я получаю в Python, пусты.

Кто-нибудь знает об этой проблеме между браузерами?

Некоторые браузеры вводят некоторые EOF или символы, убивающие получение "recv"?

Есть ли какое-либо решение этой проблемы?

server.py в Python:

 import socket
connected={}
def inRequest(text):
   content=''
   if text[0:3]=='GET':
      method='GET'
   else:
      method='POST'
      k=len(text)-1
      while k>0 and text[k]!='\n' and text[k]!='\r':
         k=k-1
      content=text[k+1:]
   text=text[text.index(' ')+1:]
   url=text[:text.index(' ')]
   return {"method":method,"url":url,"content":content}

mySocket = socket.socket ( socket.AF_INET, socket.SOCK_STREAM )
mySocket.bind ( ( '', 80 ) )
mySocket.listen ( 10 )
while True:
   channel, details = mySocket.accept()
   data=channel.recv(4096)
   req=inRequest(data)
   url=req["url"]
   if url=="/client.html" or url=="/clientIE.html":
      f=open('C:\\async\\'+url)
      channel.send ('HTTP/1.1 200 OK\n\n'+f.read())
      f.close()
      channel.close()
   elif '/messages' in url:
      if req["method"]=='POST':
         target=url[10:]
         if target in connected:
            connected[target].send("HTTP/1.1 200 OK\n\n"+req["content"])
            print req["content"]+" sent to "+target
            connected[target].close()
            channel.close()
      elif req["method"]=='GET':
         user=url[10:]
         connected[user]=channel
         print user+' is connected'

client.html в HTML + Javascript:

<html>
<head>
    <script>
        var user=''
        function post(el) {
            if (window.XMLHttpRequest) {
                var text=el.value;
                var req=new XMLHttpRequest();
                el.value='';
                var target=document.getElementById('to').value
            }
            else if (window.ActiveXObject) {
                var text=el.content;
                var req=new ActiveXObject("Microsoft.XMLHTTP");
                el.content='';
            }
            else 
                return;
            req.open('POST','messages/'+target,true)
            req.send(text);
        }
        function get(u) {
            if (user=='')
                user=u.value
            var req=new XMLHttpRequest()
            req.open('GET','messages/'+user,true)
            req.onload=function() {
                var message=document.createElement('p');
                message.innerHTML=req.responseText;
                document.getElementById('messages').appendChild(message);
                get(user);
            }
            req.send(null)
        }
    </script>
</head>
<body>
<span>From</span>
<input id="user"/>
<input type="button" value="sign in" onclick="get(document.getElementById('user'))"/>
<span>To</span>
<input id="to"/>
<span>:</span>
<input id="message"/>

<input type="button" value="post" onclick="post(document.getElementById('message'))"/>
<div id="messages">
</div>
</body>
</html>

Ответы [ 3 ]

1 голос
/ 04 октября 2008

Проблема в том, что

  • ваша обработка сокета tcp не читает столько, сколько должна
  • обработка http не завершена

Я рекомендую следующие лекции:

См. Пример ниже для работающего http-сервера, который может обрабатывать сообщения

index = '''
<html>
    <head>
    </head>
    <body>
        <form action="/" method="POST">
            <textarea name="foo"></textarea>
            <button type="submit">post</button>
        </form>
        <h3>data posted</h3>
        <div>
            %s
        </div>
    </body>
</html>
'''

bufsize = 4048
import socket
import re
from urlparse import urlparse

class Headers(object):
    def __init__(self, headers):
        self.__dict__.update(headers)

    def __getitem__(self, name):
        return getattr(self, name)

    def get(self, name, default=None):
        return getattr(self, name, default)

class Request(object):
    header_re = re.compile(r'([a-zA-Z-]+):? ([^\r]+)', re.M)

    def __init__(self, sock):
        header_off = -1
        data = ''
        while header_off == -1:
            data += sock.recv(bufsize)
            header_off = data.find('\r\n\r\n')
        header_string = data[:header_off]
        self.content = data[header_off+4:]

        lines = self.header_re.findall(header_string)
        self.method, path = lines.pop(0)
        path, protocol = path.split(' ')
        self.headers = Headers(
            (name.lower().replace('-', '_'), value)
            for name, value in lines
        )

        if self.method in ['POST', 'PUT']:
            content_length = int(self.headers.get('content_length', 0))
            while len(self.content) <  content_length:
                self.content += sock.recv(bufsize)

        self.query = urlparse(path)[4]

acceptor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
acceptor.setsockopt(
    socket.SOL_SOCKET,
    socket.SO_REUSEADDR,
    1,
)
acceptor.bind(('', 2501 ))
acceptor.listen(10)

if __name__ == '__main__':
    while True:
        sock, info = acceptor.accept()
        request = Request(sock)
        sock.send('HTTP/1.1 200 OK\n\n' + (index % request.content) )
        sock.close()
0 голосов
/ 04 октября 2008

Большое спасибо Флориан, ваш код работает !!!!
Я повторно использую шаблон и дополняю main своим механизмом COMET, и он работает намного лучше
Chrome и Firefox работают очень хорошо
У IE все еще есть проблема с системой "long GET"
Получив ответ на запрос GET, он не прекращает повторное выполнение цикла для печати сообщений.
Сейчас расследуем вопрос

Вот мой обновленный код для очень простой кросс-браузерной системы JQuery + Python.

Программа Python, основанная на коде Флориана:

bufsize = 4048
import socket
import re
from urlparse import urlparse
connected={}
class Headers(object):
    def __init__(self, headers):
        self.__dict__.update(headers)

    def __getitem__(self, name):
        return getattr(self, name)

    def get(self, name, default=None):
        return getattr(self, name, default)

class Request(object):
    header_re = re.compile(r'([a-zA-Z-]+):? ([^\r]+)', re.M)

    def __init__(self, sock):
        header_off = -1
        data = ''
        while header_off == -1:
            data += sock.recv(bufsize)
            header_off = data.find('\r\n\r\n')
        header_string = data[:header_off]
        self.content = data[header_off+4:]
        furl=header_string[header_string.index(' ')+1:]
        self.url=furl[:furl.index(' ')]
        lines = self.header_re.findall(header_string)
        self.method, path = lines.pop(0)
        path, protocol = path.split(' ')
        self.headers = Headers(
            (name.lower().replace('-', '_'), value)
            for name, value in lines
        )
        if self.method in ['POST', 'PUT']:
            content_length = int(self.headers.get('content_length', 0))
            while len(self.content) <  content_length:
                self.content += sock.recv(bufsize)
        self.query = urlparse(path)[4]

acceptor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
acceptor.setsockopt(
    socket.SOL_SOCKET,
    socket.SO_REUSEADDR,
    1,
)
acceptor.bind(('', 8007 ))
acceptor.listen(10)

if __name__ == '__main__':
    while True:
        sock, info = acceptor.accept()
        request = Request(sock)
        m=request.method
        u=request.url[1:]
        if m=='GET' and (u=='client.html' or u=='jquery.js'):
            f=open('c:\\async\\'+u,'r')
            sock.send('HTTP/1.1 200 OK\n\n'+f.read())
            f.close()
            sock.close()
        elif 'messages' in u:
            if m=='POST':
                target=u[9:]
                if target in connected:
                    connected[target].send("HTTP/1.1 200 OK\n\n"+request.content)
                    connected[target].close()
                    sock.close()
            elif m=='GET':
                user=u[9:]
                connected[user]=sock
                print user+' is connected'

А HTML с Jquery сжимается:

   <html>
<head>
    <style>
        input {width:80px;}
        span {font-size:12px;}
        button {font-size:10px;}
    </style>
    <script type="text/javascript" src='jquery.js'></script>
    <script>
        var user='';
        function post(el) {$.post('messages/'+$('#to').val(),$('#message').val());}
        function get(u) {
            if (user=='') user=u.value
            $.get('messages/'+user,function(data) { $("<p>"+data+"</p>").appendTo($('#messages'));get(user);});
        }
    </script>
</head>
<body>
<span>From</span><input id="user"/><button onclick="get(document.getElementById('user'))">log</button>
<span>To</span><input id="to"/>
<span>:</span><input id="message"/><button onclick="post()">post</button>
<div id="messages"></div>
</body>
</html>
0 голосов
/ 03 октября 2008

Я бы порекомендовал использовать библиотеку JS / Ajax на стороне клиента, чтобы исключить возможность кросс-браузерной проблемы с вашим кодом. По той же причине я бы рекомендовал использовать библиотеку Python http-сервера, такую ​​как SimpleHTTPServer или что-то из Twisted , если первая не допускает низкоуровневое управление.

Другая идея - использовать что-то вроде Wireshark, чтобы проверить, что было отправлено браузерами.

...