[Python 网络编程] makefile (三)

socket.makefile(mode ='r',buffering = None,*,encoding = None,errors = None,newline = None )
返回一个与套接字相关联的文件对象。返回的确切类型取决于给makefile()提供的参数。

这些参数的解释方式与内置open()函数的解释方式相同,除了makefile方法唯一支持的mode值是'r'(默认)'w'和'b'。

套接字必须处于阻塞模式; 它可能有超时,但是如果超时发生,文件对象的内部缓冲区可能会以不一致的状态结束。

关闭返回的文件对象makefile()将不会关闭原始套接字,除非所有其他文件对象已关闭并且 socket.close()已在套接字对象上调用。

 makefie的简单用法:

#makefile
import threading,logging,socket
DATEFMT="%H:%M:%S"
FORMAT = "[%(asctime)s]	 [%(threadName)s,%(thread)d] %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)

sock = socket.socket()
addr = ('127.0.0.1',9999)
event = threading.Event()

sock.bind(addr)
sock.listen()

def _accept(sock:socket.socket):
    s,addrinfo = sock.accept()
    f = s.makefile(mode='rw')

    while True:
        line = f.readline() # read(10) 文本使用readline
        logging.info(line)

        if line.strip() == 'quit':
            break

        msg = "Your msg = {}. ack".format(line)
        f.write(msg)
        f.flush()
    f.close()
    sock.close()


threading.Thread(target=_accept,args=(sock,)).start()

while not event.wait(2):
    logging.info(sock)


#运行结果:
[19:09:47]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:09:49]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:09:49]	 [Thread-1,6044] hi?

[19:09:51]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:09:53]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:09:55]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:09:57]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:09:59]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:01]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:03]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:05]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:07]	 [Thread-1,6044] Are you ok?

[19:10:07]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:09]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:11]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:13]	 [MainThread,3544] <socket.socket fd=288, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999)>
[19:10:13]	 [Thread-1,6044] quit

[19:10:15]	 [MainThread,3544] <socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>

  

TCP Server 改装成makefile:

连接两个客户端分别测试消息是否分发正常,客户端quit指令是否可以正常关闭socket,self.clients字典是否已经移除失联的socket。

客户端分别测试正常退出:quit退出,和异常退出:强制退出。然后观察服务端是否运行正常。

#TCP Server 改装成makefile
import threading,logging,time,random,datetime,socket
DATEFMT="%H:%M:%S"
FORMAT = "[%(asctime)s]	 [%(threadName)s,%(thread)d] %(message)s"
logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt=DATEFMT)

class ChatServer:
    def __init__(self,ip='127.0.0.1',port=9999): #启动服务
        self.addr = (ip,port)
        self.sock = socket.socket()
        self.event = threading.Event()

        self.clients = {} #客户端

    def show_client(self):
        while not self.event.is_set():
            if len(self.clients) > 0:
                logging.info(self.clients)
                self.event.wait(3)


    def start(self):
        self.sock.bind(self.addr)
        self.sock.listen()
        # accept会阻塞主线程,所以开一个新线程
        threading.Thread(target=self._accept,name='accept',daemon=True).start()
        threading.Thread(target=self.show_client,name='show_client',daemon=True).start()


    def stop(self):
        for c in self.clients.values():
            c.close()
        self.sock.close()
        self.event.wait(3)
        self.event.set()

    def _accept(self):
        while not self.event.is_set(): #多人连接
            conn,client = self.sock.accept()  #阻塞
            f = conn.makefile(mode='rw',encoding='utf8')
            self.clients[client] = f

            logging.info("{}-{}".format(conn,client))
            # recv 默认阻塞,每一个连接单独起一个recv线程准备接收数据
            threading.Thread(target=self._recv, args=(f, client), name='recv',daemon=True).start()

    def _recv(self, f, client): #接收客户端数据
        while not self.event.is_set():
            try:
                data = f.readline()
            except Exception:
                data = 'quit'
            finally:
                msg = data.strip()
                # Client通知退出机制
                if msg == 'quit':
                    f.close()
                    self.clients.pop(client)

                    logging.info('{} quit'.format(client))
                    break

            msg = "{:%Y/%m/%d %H:%M:%S} {}:{}
{}
".format(datetime.datetime.now(),*client,data)
            print(msg)
            logging.info(msg)

            for c in self.clients.values():
                c.writelines(msg)
                c.flush()



cs = ChatServer()
print('!!!!!!!!!!!')
cs.start()
print('~~~~~~~~~~~~~~~~~~~~')
e = threading.Event()
def showthreads(e:threading.Event):
    while not e.wait(3):
        logging.info(threading.enumerate())

threading.Thread(target=showthreads,name='showthreads',args=(e,)).start()

while not e.wait(1): # Sever控制台退出方式
    cmd = input('>>> ').strip()
    if cmd == 'quit':
        cs.stop()
        e.wait(3)
        break

#运行结果:
!!!!!!!!!!!
~~~~~~~~~~~~~~~~~~~~
>>> [15:18:49]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:18:49]	 [accept,2820] <socket.socket fd=388, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9999), raddr=('127.0.0.1', 3507)>-('127.0.0.1', 3507)
[15:18:49]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:18:52]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:18:52]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:18:54]	 [recv,10156] 2017/12/24 15:18:54 127.0.0.1:3507
2017/12/24 15:18:54 127.0.0.1:3507
123
123




[15:18:55]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:18:55]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:18:58]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:18:58]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:19:01]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:19:01]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:19:04]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:19:04]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:19:07]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:19:07]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:19:10]	 [show_client,4284] {('127.0.0.1', 3507): <_io.TextIOWrapper mode='rw' encoding='utf8'>}
[15:19:10]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>, <Thread(recv, started daemon 10156)>]
[15:19:12]	 [recv,10156] 2017/12/24 15:19:12 127.0.0.1:3507


[15:19:12]	 [recv,10156] ('127.0.0.1', 3507) quit
2017/12/24 15:19:12 127.0.0.1:3507


[15:19:13]	 [showthreads,8384] [<Thread(showthreads, started 8384)>, <Thread(accept, started daemon 2820)>, <Thread(show_client, started daemon 4284)>, <_MainThread(MainThread, started 932)>]

  

总结:

使用makefile返回一个套接字相关联的文件对象,对该文件对象的操作方法,与普通文件操作方法一致,read,readline,write,writeline

makefile不仅仅可以对accept建立连接后的socketObject使用,也可对主线程的sock和任何socketObject使用。

原文地址:https://www.cnblogs.com/i-honey/p/8094173.html