python编写makefile_[Python 网络编程] makefile (三)

本文介绍了如何在Python中使用socket的makefile方法创建与套接字关联的文件对象,用于读写数据。示例展示了通过makefile实现的TCP服务器,接收并响应客户端消息,支持'quit'命令关闭连接。服务器会监控客户端状态,当客户端发送'quit'时,关闭套接字并从客户端列表中移除。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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]\t [%(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]

[19:09:49] [MainThread,3544]

[19:09:49] [Thread-1,6044] hi?

[19:09:51] [MainThread,3544]

[19:09:53] [MainThread,3544]

[19:09:55] [MainThread,3544]

[19:09:57] [MainThread,3544]

[19:09:59] [MainThread,3544]

[19:10:01] [MainThread,3544]

[19:10:03] [MainThread,3544]

[19:10:05] [MainThread,3544]

[19:10:07] [Thread-1,6044] Are you ok?

[19:10:07] [MainThread,3544]

[19:10:09] [MainThread,3544]

[19:10:11] [MainThread,3544]

[19:10:13] [MainThread,3544]

[19:10:13] [Thread-1,6044] quit

[19:10:15] [MainThread,3544]

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]\t [%(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} {}:{}\n{}\n".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] -('127.0.0.1', 3507)

[15:18:49] [showthreads,8384] [, , , <_mainthread started>, ]

[15:18:52] [show_client,4284] {('127.0.0.1', 3507): <_io.textiowrapper mode="rw" encoding="utf8">}

[15:18:52] [showthreads,8384] [, , , <_mainthread started>, ]

[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] [, , , <_mainthread started>, ]

[15:18:58] [show_client,4284] {('127.0.0.1', 3507): <_io.textiowrapper mode="rw" encoding="utf8">}

[15:18:58] [showthreads,8384] [, , , <_mainthread started>, ]

[15:19:01] [show_client,4284] {('127.0.0.1', 3507): <_io.textiowrapper mode="rw" encoding="utf8">}

[15:19:01] [showthreads,8384] [, , , <_mainthread started>, ]

[15:19:04] [show_client,4284] {('127.0.0.1', 3507): <_io.textiowrapper mode="rw" encoding="utf8">}

[15:19:04] [showthreads,8384] [, , , <_mainthread started>, ]

[15:19:07] [show_client,4284] {('127.0.0.1', 3507): <_io.textiowrapper mode="rw" encoding="utf8">}

[15:19:07] [showthreads,8384] [, , , <_mainthread started>, ]

[15:19:10] [show_client,4284] {('127.0.0.1', 3507): <_io.textiowrapper mode="rw" encoding="utf8">}

[15:19:10] [showthreads,8384] [, , , <_mainthread started>, ]

[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] [, , , <_mainthread started>]

总结:

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值