5

これが私のサーバーです

"""Server using epoll method"""

import os
import select
import socket
import time

from oodict import OODict

addr = ('localhost', 8989)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(addr)
s.listen(8)
s.setblocking(0) # Non blocking socket server
epoll = select.epoll()
epoll.register(s.fileno(), select.EPOLLIN) # Level triggerred

cs = {}
data = ''
while True:
    time.sleep(1)
    events = epoll.poll(1) # Timeout 1 second
    print 'Polling %d events' % len(events)
    for fileno, event in events:
        if fileno == s.fileno():
            sk, addr = s.accept()
            sk.setblocking(0)
            print addr
            cs[sk.fileno()] = sk
            epoll.register(sk.fileno(), select.EPOLLIN)

        elif event & select.EPOLLIN:
            data = cs[fileno].recv(4)
            print 'recv ', data
            epoll.modify(fileno, select.EPOLLOUT)
        elif event & select.EPOLLOUT:
            print 'send ', data
            cs[fileno].send(data)
            data = ''
            epoll.modify(fileno, select.EPOLLIN)

        elif event & select.EPOLLERR:
            print 'err'
            epoll.unregister(fileno)

クライアント側の入力

ideer@ideer:/home/chenz/source/ideerfs$ telnet localhost 8989
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
123456
123456
^]

telnet> q
Connection closed.

サーバー側出力

ideer@ideer:/chenz/source/ideerfs$ python epoll.py 
Polling 0 events
Polling 0 events
Polling 1 events
('127.0.0.1', 53975)
Polling 0 events
Polling 1 events
recv  1234
Polling 1 events
send  1234
Polling 1 events
recv  56

Polling 1 events
send  56

Polling 0 events
Polling 0 events
Polling 0 events
Polling 1 events
recv  
Polling 1 events
send  
Polling 1 events
recv  
Polling 1 events
send  
Polling 1 events
recv  
Polling 1 events
send  
Polling 1 events
recv  
^CTraceback (most recent call last):
  File "epoll.py", line 23, in <module>
    time.sleep(1)
KeyboardInterrupt

クライアントが接続を閉じた後でも、epoll が recv をポーリングしてイベントを送信できるのは奇妙です! EPOLLERR イベントが発生しないのはなぜですか? EPOLLHUP を使用する場合も同様です。

EPOLLERR イベントは、閉じた接続を書き込もうとしたときにのみ発生することに気付きました。これ以外に、接続が閉じられたかどうかを確認する別の方法はありますか?

EPOLLIN イベントで何も得られない場合、接続を閉じたものとして扱うのは正しいですか?

4

10 に答える 10

5

EPOLLERR と EPOLLHUP は、投稿に貼り付けられたコードでは決して発生しません。これは、常に EPOLLIN または EPOLLOUT (これらのいくつかを一度に設定できます) と組み合わせて発生するためです。そのため、if/then/else は常にEPOLLIN または EPOLLOUT。

実験の結果、EPOLLHUP は EPOLLERR と組み合わせてのみ発生することがわかりました。これは、python が epoll および低レベル IO とインターフェイスする方法にある可能性があります。通常、recv は -1 を返し、非recv をブロックしますが、python は '' (何も返されない) を使用して EOF を通知します。

telnet-session を閉じると、tcp 接続のその端だけが閉じられるため、recv を呼び出すことは依然として完全に有効です。tcp 受信バッファーには、アプリケーションがまだ読み取っていない保留中のデータがある可能性があります。エラー状態を引き起こします。

EPOLLIN と空の文字列を返す recv は、相手側が接続を閉じたことを示しているようですが、古いバージョンの Python (epoll が導入される前) とパイプでの単純な選択を使用すると、 '' を返した read は、利用可能なデータが不足しているだけで、EOF を示していませんでした。

于 2009-05-05T17:56:54.047 に答える
2

ソケットがまだ開いているが、読み取り/書き込みが利用できない場合、epoll.pollはタイムアウトになります。

ピアからデータが利用できる場合は、EPOLLINを取得し、データを利用できるようになります。

ソケットがピアによって閉じられている場合、EPOLLINを取得しますが、それを読み取ると「」が返されます。

次に、ソケットをシャットダウンし、結果のEPOLLHUPイベントを取得して内部構造をクリーンアップすることにより、ソケットを閉じることができます。

または、クリーンアップを実行して、epollの登録を解除します。

elif event & select.EPOLLIN:
    data = cs[fileno].recv(4)

if not data:
    epoll.modify(fileno, 0)
    cs[fileno].shutdown(socket.SHUT_RDWR)
于 2009-11-17T15:49:38.810 に答える
1

この問題を回避するためのアドホックソリューション

--- epoll_demo.py.orig  2009-04-28 18:11:32.000000000 +0800
+++ epoll_demo.py   2009-04-28 18:12:56.000000000 +0800
@@ -18,6 +18,7 @@
 epoll.register(s.fileno(), select.EPOLLIN) # Level triggerred

 cs = {}
+en = {}
 data = ''
 while True:
     time.sleep(1)
@@ -29,10 +30,18 @@
             sk.setblocking(0)
             print addr
             cs[sk.fileno()] = sk
+            en[sk.fileno()] = 0
             epoll.register(sk.fileno(), select.EPOLLIN)

         elif event & select.EPOLLIN:
             data = cs[fileno].recv(4)
+            if not data:
+                en[fileno] += 1
+                if en[fileno] >= 3:
+                    print 'closed'
+                    epoll.unregister(fileno)
+                continue
+            en[fileno] = 0
             print 'recv ', data
             epoll.modify(fileno, select.EPOLLOUT)
         elif event & select.EPOLLOUT:
于 2009-04-28T10:17:46.050 に答える
0

EPOLLHUPとEPOLLINを同時に使用するために、マスクを組み合わせる必要はありません。


epoll.register(sk.fileno(), select.EPOLLIN | select.EPOLLHUP)

正直なところ、私はepollライブラリにあまり詳しくないので、それは単なる提案にすぎません...

于 2009-04-27T15:53:41.710 に答える
0

別のアプローチがあります。

try:
    data = s.recv(4096)
except socket.error:
    if e[0] in (errno.EWOULDBLOCK, errno.EAGAIN): # since this is a non-blocking socket..
        return # no error
    else:
        # error
        socket.close()

if not data: #closed either
    socket.close() 
于 2011-06-17T00:47:19.380 に答える
0

select.EPOLLHUP処理コードをselect.EPOLLINの前の行に移動した後も、hupイベントを「telnet」で取得できません。しかし、偶然にも、自分のクライアントスクリプトを使用すると、hupイベントが発生することがわかりました。変...

そして男によるとepoll_ctl

   EPOLLRDHUP (since Linux 2.6.17)
          Stream socket peer closed connection, or shut down writing half of connection.  (This flag is especially useful for writing simple code  to
          detect peer shutdown when using Edge Triggered monitoring.)

   EPOLLERR
          Error  condition  happened on the associated file descriptor.  epoll_wait(2) will always wait for this event; it is not necessary to set it
          in events.

   EPOLLHUP
          Hang up happened on the associated file descriptor.  epoll_wait(2) will always wait for this event; it  is  not  necessary  to  set  it  in
          events.

Pythonによって実装されていないリモート側の閉じた接続時にEPOLLRDHUPイベントが発生するようですが、理由がわかりません

于 2009-05-15T04:01:51.587 に答える
0
elif event & (select.EPOLLERR | select.EPOLLHUP):
    epoll.unregister(fileno)
    cs[fileno].close()
    del cs[fileno]
于 2012-02-23T11:36:01.183 に答える
0
if event & select.EPOLLHUP:
    epoll.unregister(fd)
于 2011-10-18T06:01:59.460 に答える
0

EPOLLRDHUPフラグは、理由もなく Python で定義されていません。Linux カーネルが >= 2.6.17 の場合、次のように定義してソケットを epoll に登録できます。

import select
if not "EPOLLRDHUP" in dir(select):
    select.EPOLLRDHUP = 0x2000
...
epoll.register(socket.fileno(), select.EPOLLIN | select.EPOLLRDHUP)

その後、同じフラグ ( EPOLLRDHUP )を使用して、必要なイベントをキャッチできます。

elif event & select.EPOLLRDHUP:
     print "Stream socket peer closed connection"
     # try shutdown on both side, then close the socket:
     socket.close()
     epoll.unregister(socket.fileno())

詳細については、python のリポジトリでselectmodule.cを確認してください。

于 2010-10-21T13:53:54.037 に答える