Python socket non-blocking with SSL 的問題

最近要直接用Socket做一個簡單的Server,想使用non-blocking的Scoket,但是遇到一些問題硼被,解決了所以在這里總結(jié)一下惶岭。

簡單的Server端代碼片段(只有接受數(shù)據(jù)的):

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import ssl
import select
import socket

DEFAULT_SERVER_HOST = "0.0.0.0"
DEFAULT_SERVER_PORT = 14443

class Server(object):

    def __init__(self, host, port, is_ssl=False, cert_file=None, key_file=None):
        self.host = host
        self.port = port
        self.is_ssl = is_ssl
        self.cert_file = cert_file
        self.key_file = key_file
        self.context = None

        self.__socket = None
        self.running = False
        self.multiplex = None

        self.read_set = set()
        self.write_set = set()
        self.error_set = set()

    def __initialize(self):

        if self.is_ssl and (self.cert_file is None or self.key_file is None):
            raise Exception("If you want to enable ssl, please set cert_file and key_file")

        if self.is_ssl:
            self.context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
            self.context.load_cert_chain(certfile=self.cert_file, keyfile=self.key_file)

        self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT & socket.SO_REUSEADDR, 1)

        self.__socket.bind((self.host if self.host is not None else DEFAULT_SERVER_HOST,
                            self.port if self.port is not None else DEFAULT_SERVER_PORT))

        self.__socket.setblocking(0)
        self.__socket.listen(5)

    def start(self):
        self.__initialize()

        server_fd = self.__socket.fileno()
        self.read_set.add(server_fd)

        while True:
            read_list, write_list, error_list = select.select(self.read_set, self.write_set, self.error_set, 2)

            if server_fd in read_list:
                conn, addr = self.__socket.accept()
                conn.setblocking(0)
                if self.is_ssl:
                    conn = self.context.wrap_socket(conn, server_side=True, do_handshake_on_connect=False)
                    i = 0

                    while True:
                        i += 1
                        print(i)
                        try:
                            conn.do_handshake()
                            select.select([conn], [], [])
                            break
                        except ssl.SSLError as err:
                            if err.args[0] == ssl.SSL_ERROR_WANT_READ:
                                print("read")
                                select.select([conn], [], [])
                            elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE:
                                print("write")
                                select.select([], [conn], [])
                            else:
                                raise

                # rfile = conn.makefile("rb")
                # a = rfile.read(1024*8)
                a = conn.recv(1024*8)
                print(a)

if __name__ == "__main__":
    cs = Server("0.0.0.0", 14443, True, "snakeoil.crt", "snakeoil.key")
    cs.start()

由于self.__socket.setblocking(0) conn.setblocking(0)都設(shè)置為非阻塞寿弱,所以conn = self.context.wrap_socket(conn, server_side=True, do_handshake_on_connect=False) 不能設(shè)置為連接時自動握手犯眠。

在成功握手后按灶,發(fā)現(xiàn)一個問題,調(diào)用

rfile = conn.makefile("rb")
a = rfile.read(1024*8)

如果讀取范圍較大筐咧,會出現(xiàn)

Traceback (most recent call last):
  File "/home/ming/Application/pycharm-2017.1.4/helpers/pydev/pydevd.py", line 1591, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/home/ming/Application/pycharm-2017.1.4/helpers/pydev/pydevd.py", line 1018, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/home/ming/Application/pycharm-2017.1.4/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/home/ming/PycharmProjects/Test/server.py", line 86, in <module>
    cs.start()
  File "/home/ming/PycharmProjects/Test/server.py", line 80, in start
    a = rfile.read(1024*8)
  File "/home/ming/.pyenv/versions/3.5.3/lib/python3.5/socket.py", line 576, in readinto
    return self._sock.recv_into(b)
  File "/home/ming/.pyenv/versions/3.5.3/lib/python3.5/ssl.py", line 937, in recv_into
    return self.read(nbytes, buffer)
  File "/home/ming/.pyenv/versions/3.5.3/lib/python3.5/ssl.py", line 799, in read
    return self._sslobj.read(len, buffer)
  File "/home/ming/.pyenv/versions/3.5.3/lib/python3.5/ssl.py", line 583, in read
    v = self._sslobj.read(len, buffer)
ssl.SSLWantReadError: The operation did not complete (read) (_ssl.c:2090)

這個錯誤鸯旁,表示讀未完成。
而使用a = conn.recv(1024*8)則不會發(fā)生錯誤量蕊。

發(fā)現(xiàn)使用makefile()后的讀操作铺罢,將會多次調(diào)用ssl.pySSLSocket.classrecv_into方法,最后到ssl.pySSLObject

    def read(self, len=1024, buffer=None):
        """Read up to 'len' bytes from the SSL object and return them.

        If 'buffer' is provided, read into this buffer and return the number of
        bytes read.
        """
        if buffer is not None:
            v = self._sslobj.read(len, buffer)    //makefile 后執(zhí)行這句
        else:
            v = self._sslobj.read(len)
        return v

残炮,直至錯誤出現(xiàn)韭赘。

而直接使用socketread方法,則是直接調(diào)用ssl.pySSLObject

    def read(self, len=1024, buffer=None):
        """Read up to 'len' bytes from the SSL object and return them.

        If 'buffer' is provided, read into this buffer and return the number of
        bytes read.
        """
        if buffer is not None:
            v = self._sslobj.read(len, buffer)   
        else:
            v = self._sslobj.read(len) // socket 的read執(zhí)行這句
        return v

更深入的原因還沒找出势就,目前覺得應(yīng)該是makefile后把socket當(dāng)成文件讀取泉瞻,會嘗試讀直至無法繼續(xù)讀取,所以才會導(dǎo)致錯誤發(fā)生苞冯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末袖牙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子舅锄,更是在濱河造成了極大的恐慌鞭达,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皇忿,死亡現(xiàn)場離奇詭異畴蹭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鳍烁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門叨襟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人老翘,你說我怎么就攤上這事芹啥。” “怎么了铺峭?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵墓怀,是天一觀的道長。 經(jīng)常有香客問我卫键,道長傀履,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮钓账,結(jié)果婚禮上碴犬,老公的妹妹穿的比我還像新娘。我一直安慰自己梆暮,他們只是感情好服协,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啦粹,像睡著了一般偿荷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唠椭,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天跳纳,我揣著相機(jī)與錄音,去河邊找鬼贪嫂。 笑死寺庄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的力崇。 我是一名探鬼主播斗塘,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼餐曹!你這毒婦竟也來了逛拱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤台猴,失蹤者是張志新(化名)和其女友劉穎朽合,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饱狂,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曹步,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了休讳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讲婚。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖俊柔,靈堂內(nèi)的尸體忽然破棺而出筹麸,到底是詐尸還是另有隱情,我是刑警寧澤雏婶,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布物赶,位于F島的核電站,受9級特大地震影響留晚,放射性物質(zhì)發(fā)生泄漏酵紫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奖地。 院中可真熱鬧橄唬,春花似錦、人聲如沸参歹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泽示。三九已至缸血,卻和暖如春蜜氨,著一層夾襖步出監(jiān)牢的瞬間械筛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工飒炎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留埋哟,地道東北人。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓郎汪,卻偏偏與公主長得像赤赊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子煞赢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容

  • iPhone的標(biāo)準(zhǔn)推薦是CFNetwork 庫編程抛计,其封裝好的開源庫是 cocoa AsyncSocket庫,用它...
    Ethan_Struggle閱讀 2,239評論 2 12
  • 最近在學(xué)習(xí)Python看了一篇文章寫得不錯照筑,是在腳本之家里的吹截,原文如下,很有幫助: 一凝危、網(wǎng)絡(luò)知識的一些介紹 soc...
    qtruip閱讀 2,708評論 0 6
  • 代碼不好排版波俄,可以到微信訂閱號(xuanhun521)查看原文。 Python黑帽編程2.8套接字編程 套接字編程...
    玄魂閱讀 655評論 1 2
  • 大綱 一.Socket簡介 二.BSD Socket編程準(zhǔn)備 1.地址 2.端口 3.網(wǎng)絡(luò)字節(jié)序 4.半相關(guān)與全相...
    VD2012閱讀 2,335評論 0 5
  • 今天我們畫第四個公主乾戏,難度在慢慢增加遏佣,你發(fā)現(xiàn)了嗎殿托? 自動鉛筆打底稿。應(yīng)該已經(jīng)很熟練了冬念。大家有沒有發(fā)現(xiàn),為了降低難度...
    Ann苳杭杭閱讀 6,053評論 31 174