7

小さなBluetoothサーバーアプリケーションをPyS60のNokia電話に書き込みたいのですが。クライアントの要求に対する応答を送信でき、クライアントにもデータをプッシュできる必要があります。

オプション1:を使用するsocket.recv(1024)と、プログラムは何かが受信されるまで待機するため、サーバーはデータをクライアントにプッシュできません。Python for S60の実装にはsocket.settimeout()メソッドがないため、適切な非ブロッキングコードを記述できませんでした。

oprion 2socket.makefile()アプローチは良さそうだったが、うまくいかなかった。conn.recv(1024)toを交換したところfd = socket.makefile() fd.readline()、何も読めませんでした。

オプション3select()関数を調べましたが、うまくいきませんでした。を「など」に変更するconn.recv()r,w,e = select.select([conn],[],[])、クライアントが接続すらしないことが示唆されました。「クライアントを待っています...」でハングします。変...

非常に優れたサーバー実装と非同期APIもあることは知っていますが、ここで必要なのは本当に基本的なものだけです。前もって感謝します!

これが私が持っているものです:

sock = btsocket.socket(btsocket.AF_BT, btsocket.SOCK_STREAM)
channel = btsocket.bt_rfcomm_get_available_server_channel(sock)
sock.bind(("", channel))                                     
sock.listen(1)
btsocket.bt_advertise_service(u"name", sock, True, btsocket.RFCOMM)

print "Waiting for the client..."                                     
conn, client_mac = sock.accept()
print "connected: " + client_mac

while True:
    try:
        data = conn.recv(1024)
        if len(data) != 0:
           print "received [%s]" % data
           if data.startswith("something"): conn.send("something\r\n")
        else:
           conn.send("some other data \r\n")
    except:
           pass

明らかにブロックしているので、「他のデータ」は送信されませんが、これまでのところ最高です。少なくとも私はクライアントに返信するために何かを送ることができます。

4

3 に答える 3

2

ついに解決策を見つけました!

選択機能は、新しいPyS60ポートのbtsocketモジュールでは機能しませんでした。誰かが、機能するselect関数を使用してnew_btsocket(ここで入手可能)を作成しました。

于 2011-12-08T21:42:07.113 に答える
1

これはエコーサーバーに基づく簡単な例です

#!/usr/bin/python                                                                                                                                                                                                                                                    

import socket
import select

server = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
server.bind( ('localhost', 12556) )
server.listen( 5 )

toread = [server]

running = 1

# we will shut down when all clients disconenct                                                                                                                                                                                                                      
while running:

    rready,wready,err = select.select( toread, [], [] )
    for s in rready:
        if s == server:
            # accepting the socket, which the OS passes off to another                                                                                                                                                                                               
            # socket so we can go back to selecting.  We'll append this                                                                                                                                                                                              
            # new socket to the read list we select on next pass                                                                                                                                                                                                     

            client, address = server.accept()
            toread.append( client )  # select on this socket next time                                                                                                                                                                                               
        else:
            # Not the server's socket, so we'll read                                                                                                                                                                                                                 
            data = s.recv( 1024 )
            if data:
                print "Received %s" % ( data  )
            else:
                print "Client disconnected"
                s.close()

                # remove socket so we don't watch an invalid 
                # descriptor, decrement client count                                                                                                                                                                      
                toread.remove( s )
                running = len(toread) - 1

# clean up                                                                                                                                                                                                                                                           
server.close()

そうは言っても、私はまだソケットサーバーがよりクリーンで簡単だと思っています。handle_requestを実装し、serve_foreverを呼び出します

于 2011-12-06T18:49:52.857 に答える
0

これがEpollサーバーの実装です(ノンブロッキング)

http://pastebin.com/vP6KPTwH (以下と同じですが、これはコピーしやすいかもしれないと感じました)

サーバーの起動に使用python epollserver.pyします。

を使用してテストしますwget localhost:8888

sysをインポート
ソケットをインポートし、選択します
fcntlをインポートします
email.parserをインポートします
StringIOをインポートする
インポート日時


"" "
見る:
http://docs.python.org/library/socket.html
"" "

__author__ = ['Caleb Burns'、'Ben DeMott']

def main(argv = None):
    EOL1 ='\ n \ n'
    EOL2 ='\ n \ r \ n'
    response ='HTTP / 1.0 200 OK \ r \ n日付:1996年1月1日月曜日01:01:01 GMT \ r \ n'
    response + ='Content-Type:text / plain \ r \ nContent-Length:13 \ r \ n \ r \ n'
    response + ='Hello、world!'
    serversocket = socket.socket(socket.AF_INET、socket.SOCK_STREAM)
    #このプログラムが終了したときに自身を破棄するようにサーバーソケットファイル記述子に指示します。
    socketFlags = fcntl.fcntl(serversocket.fileno()、fcntl.F_GETFD)
    socketFlags | = fcntl.FD_CLOEXEC
    fcntl.fcntl(serversocket.fileno()、fcntl.F_SETFD、socketFlags)

    serversocket.setsockopt(socket.SOL_SOCKET、socket.SO_REUSEADDR、1)
    serversocket.bind(( '0.0.0.0'、8888))
    serversocket.listen(1)
    #非同期ソケットを使用します。
    serversocket.setblocking(0)
    #最大128のリクエスト(接続)のキューを許可します。
    serversocket.listen(128)
    #上記のbind()呼び出しで定義されたサーバーソケットでソケットイベントをリッスンします。
    epoll = select.epoll()
    epoll.register(serversocket.fileno()、select.EPOLLIN)
    「EpollServerStarted...」を印刷します

    試す:
        #接続ディクショナリは、ファイル記述子(整数)を対応するネットワーク接続オブジェクトにマップします。
        接続={}
        リクエスト={}
        応答={}
        Trueの場合:
            #ソケットにイベントがあるかどうかをepollに確認し、イベントが存在しない場合は最大1秒待ちます。
            イベント=epoll.poll(1)
            #filenoはファイル記述子です。
            #eventはイベントコード(タイプ)です。
            filenoの場合、イベント内のイベント:
                #新しい接続が存在する可能性があるため、ソケットで読み取りイベントを確認します。
                fileno == serversocket.fileno()の場合:
                    #接続は新しいソケットオブジェクトです。
                    #アドレスはクライアントのIPアドレスです。アドレスの形式は、ソケットのアドレスファミリ(つまり、AF_INET)によって異なります。
                    接続、アドレス= serversocket.accept()
                    #新しいソケット接続を非ブロッキングモードに設定します。
                    connection.setblocking(0)
                    #新しいソケット接続で読み取りイベントをリッスンします。
                    epoll.register(connection.fileno()、select.EPOLLIN)
                    connection [connection.fileno()]=接続
                    requests [connection.fileno()] = b''
                    応答[connection.fileno()]=応答
                #読み取りイベントが発生した場合は、クライアントから送信された新しいデータを読み取ります。
                elif event&select.EPOLLIN:
                    リクエスト[fileno]+=接続[fileno].recv(1024)
                    #読み取りが終了したら、読み取りイベントのリッスンを停止し、EPOLLOUTイベントのリッスンを開始します(これにより、クライアントにデータを送り返すことができる時期がわかります)。
                    リクエスト[fileno]のEOL1またはリクエスト[fileno]のEOL2の場合:
                        epoll.modify(fileno、select.EPOLLOUT)
                        #リクエストデータをコンソールに出力します。
                        epoll.modify(fileno、select.EPOLLOUT)

                        データ=リクエスト[ファイル番号]
                        eol = data.find( "\ r \ n")#これは最初の行の終わりです
                        start_line = data [:eol] #最初の行の内容(プロトコル情報)を取得します
                        #メソッドはPOST|GETなどです
                        メソッド、uri、http_version = start_line.split( "")
                        #Facebookのhttputilライブラリを再利用しました(ヘッダーの正規化と解析に適しています)
                        ヘッダー=HTTPHeaders.parse(data [eol:])
                        print "\ nCLIENT:FD:%s%s:'%s'%s"%(fileno、method、uri、datetime.datetime.now())


                #クライアントがデータを受信する準備ができている場合は、応答を送信します。
                elif event&select.EPOLLOUT:
                    #完全な応答が送信されるまで、一度に1ビットずつ応答を送信します。
                    #注:ここでsendfile()を使用します。
                    byteswritten = connections [fileno] .send(responses [fileno])
                    応答[fileno]=応答[fileno][byteswritten:]
                    len(responses [fileno])== 0の場合:
                        #読み取り/書き込みイベントに関心がなくなったことをソケットに通知します。
                        epoll.modify(fileno、0)
                        #データの送信が完了し、接続を閉じることができることをクライアントに通知します。(良い形)
                        connection [fileno] .shutdown(socket.SHUT_RDWR)
                #EPOLLHUP(ハングアップ)イベントは、クライアントが切断されたため、ソケットをクリーンアップ/クローズしたことを意味します。
                elif event&select.EPOLLHUP:
                    epoll.unregister(fileno)
                    connection [fileno] .close()
                    デル接続[fileno]
    ついに:
        #プログラムの完了時に、残っている開いているソケットを閉じます。
        epoll.unregister(serversocket.fileno())
        epoll.close()
        serversocket.close()


#!/ usr / bin / env python
#
#Copyright 2009 Facebook
#
#Apache Licenseバージョン2.0(「ライセンス」)に基づいてライセンス供与されます。してもいいです
#ライセンスに準拠する場合を除いて、このファイルを使用しないでください。あなたは得るかもしれません
#ライセンスのコピー
#
#http://www.apache.org/licenses/LICENSE-2.0
#
#適用法で要求されている場合、または書面で合意されている場合を除き、ソフトウェア
#ライセンスに基づいて配布されるものは、「現状有姿」で配布されます。
#明示または黙示を問わず、あらゆる種類の保証または条件。を参照してください
#許可と制限を管理する特定の言語のライセンス
#ライセンスの下で。

"""クライアントとサーバーで共有されるHTTPユーティリティコード"""

クラスHTTPHeaders(dict):
    """すべてのキーのHttp-Header-Caseを維持する辞書。

    新しいメソッドのペアを介して、キーごとに複数の値をサポートします。
    add()およびget_list()。通常の辞書インターフェースは単一を返します
    キーごとの値。複数の値がコンマで結合されています。

    >>> h = HTTPHeaders({"content-type": "text / html"})
    >>> h.keys()
    ['Content-Type']
    >>> h ["Content-Type"]
    'text / html'

    >>> h.add( "Set-Cookie"、 "A = B")
    >>> h.add( "Set-Cookie"、 "C = D")
    >>> h ["set-cookie"]
    'A = B、C = D'
    >>> h.get_list( "set-cookie")
    ['A = B'、'C = D']

    >>> for(k、v)insorted(h.get_all()):
    ...印刷'%s:%s'%(k、v)
    ..。
    コンテンツタイプ:text / html
    Set-Cookie:A = B
    Set-Cookie:C = D
    "" "
    def __init __(self、* args、** kwargs):
        #argsまたはkwargsをdict.__init__に渡さないでください。バイパスされます。
        #私たちの__setitem__
        dict .__ init __(self)
        self._as_list = {}
        self.update(* args、** kwargs)

    #新しいパブリックメソッド

    def add(self、name、value):
        """指定されたキーに新しい値を追加します。"""
        norm_name = HTTPHeaders._normalize_name(name)
        自己のnorm_nameの場合:
            #_as_listを変更するため、__setitem__のオーバーライドをバイパスします
            dict .__ setitem __(self、norm_name、self [norm_name] +'、' + value)
            self._as_list [norm_name] .append(value)
        そうしないと:
            self [norm_name] = value

    def get_list(self、name):
        """指定されたヘッダーのすべての値をリストとして返します。"""
        norm_name = HTTPHeaders._normalize_name(name)
        self._as_list.get(norm_name、[])を返します

    def get_all(self):
        "" "すべての(名前、値)ペアの反復可能値を返します。

        ヘッダーに複数の値がある場合、複数のペアは
        同じ名前で返されます。
        "" "
        名前については、self._as_list.iteritems()にリストしてください。
            リスト内の値の場合:
                収量(名前、値)


    def items(self):
        キーの場合は[{key:value [0]}を返し、self._as_list.iteritems()の値を返します]

    def get_content_type(self):
        dict.get(self、HTTPHeaders._normalize_name('content-type')、None)を返します

    def parse_line(self、line):
        """単一のヘッダー行で辞書を更新します。

        >>> h = HTTPHeaders()
        >>> h.parse_line( "Content-Type:text / html")
        >>> h.get('content-type')
        'text / html'
        "" "
        名前、値= line.split( ":"、1)
        self.add(name、value.strip())

    @classmethod
    def parse(cls、headers):
        """HTTPヘッダーテキストから辞書を返します。

        >>> h = HTTPHeaders.parse( "Content-Type:text / html \\ r \\ nContent-Length:42 \\ r \\ n")
        >>>sorted(h.iteritems())
        [('Content-Length'、 '42')、('Content-Type'、'text / html')]
        "" "
        h = cls()
        headers.splitlines()の行の場合:
            行の場合:
                h.parse_line(line)
        hを返す

    #dict実装オーバーライド

    def __setitem __(self、name、value):
        norm_name = HTTPHeaders._normalize_name(name)
        dict .__ setitem __(self、norm_name、value)
        self._as_list [norm_name] = [value]

    def __getitem __(self、name):
        dict .__ getitem __(self、HTTPHeaders._normalize_name(name))を返します

    def __delitem __(self、name):
        norm_name = HTTPHeaders._normalize_name(name)
        dict .__ delitem __(self、norm_name)
        del self._as_list [norm_name]

    def get(self、name、default = None):
        dict.get(self、HTTPHeaders._normalize_name(name)、default)を返します

    def update(self、* args、** kwargs):
        #dict.updateは__setitem__をバイパスします
        dict(* args、** kwargs).iteritems()のk、vの場合:
            self [k] = v

    @staticmethod
    def _normalize_name(name):
        """名前をHttp-Header-Caseに変換します。

        >>> HTTPHeaders._normalize_name( "coNtent-TYPE")
        「コンテンツタイプ」
        "" "
        return "-"。join([w.capitalize()for w in name.split( "-")])


if(__ name__ =='__main__'):
    sys.exit(main(sys.argv))
于 2011-12-06T19:44:56.660 に答える