创建多线程TCP服务器 Python 3
你好,我试着做一个简单的服务器,可以同时接收多个客户端的连接。我刚学Python,对这个有点难以理解……我尝试把我的代码改成多线程的应用,但没有成功……这是我的代码:
import socket, threading
def message():
while 1:
data = connection.recv(1024)
if not data: break
#connection.sendall(b'-- Message Received --\n')
print(data.decode('utf-8'))
connection.close()
def connection():
address = input("Insert server ip")
port = 44444
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((address, port))
s.listen(1)
print("Server started! Waiting for connections...")
def accept connection():
connection, address = s.accept()
print('Client connected with address:', address)
t=thread.Threading(target=message,args=(connection))
t.run()
我知道我的代码有很多错误,但我刚学Python,抱歉 :(
原来的没有线程的代码是:
import socket
address = input("Insert server ip:")
port = 44444
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((address, port))
s.listen(1)
print("Server started! Waiting for connections...")
connection, address = s.accept()
print('Client connected with address:', address)
while 1:
data = connection.recv(1024)
if not data: break
#connection.sendall(b'-- Message Received --\n')
print(data.decode('utf-8'))
connection.close()
3 个回答
这里有一些示例代码,展示了一个使用线程的套接字连接。
def sock_connection( sock, host ):
"Handle socket"
pass
while 1:
try:
newsock = sock.accept()
thread = Thread( target=sock_connection, args=newsock )
thread.start()
except Exception, e:
print "error on socket connection: " % e)
这是一篇比较老的帖子,不过里面有个很好的方法可以实现你说的功能。这里有个我之前发的例子链接:
https://bitbucket.org/matthewwachter/tcp_threadedserver/src/master/
还有这个脚本:
from datetime import datetime
from json import loads, dumps
from pprint import pprint
import socket
from threading import Thread
class ThreadedServer(Thread):
def __init__(self, host, port, timeout=60, debug=False):
self.host = host
self.port = port
self.timeout = timeout
self.debug = debug
Thread.__init__(self)
# run by the Thread object
def run(self):
if self.debug:
print(datetime.now())
print('SERVER Starting...', '\n')
self.listen()
def listen(self):
# create an instance of socket
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# bind the socket to its host and port
self.sock.bind((self.host, self.port))
if self.debug:
print(datetime.now())
print('SERVER Socket Bound', self.host, self.port, '\n')
# start listening for a client
self.sock.listen(5)
if self.debug:
print(datetime.now())
print('SERVER Listening...', '\n')
while True:
# get the client object and address
client, address = self.sock.accept()
# set a timeout
client.settimeout(self.timeout)
if self.debug:
print(datetime.now())
print('CLIENT Connected:', client, '\n')
# start a thread to listen to the client
Thread(target = self.listenToClient,args = (client,address)).start()
# send the client a connection message
# res = {
# 'cmd': 'connected',
# }
# response = dumps(res)
# client.send(response.encode('utf-8'))
def listenToClient(self, client, address):
# set a buffer size ( could be 2048 or 4096 / power of 2 )
size = 1024
while True:
try:
# try to receive data from the client
data = client.recv(size).decode('utf-8')
if data:
data = loads(data.rstrip('\0'))
if self.debug:
print(datetime.now())
print('CLIENT Data Received', client)
print('Data:')
pprint(data, width=1)
print('\n')
#send a response back to the client
res = {
'cmd': data['cmd'],
'data': data['data']
}
response = dumps(res)
client.send(response.encode('utf-8'))
else:
raise error('Client disconnected')
except:
if self.debug:
print(datetime.now())
print('CLIENT Disconnected:', client, '\n')
client.close()
return False
if __name__ == "__main__":
ThreadedServer('127.0.0.1', 8008, timeout=86400, debug=True).start()
你的基本设计已经很接近了,但有很多小问题让你难以继续前进。
首先,你的函数名中有空格,这是不允许的。而且你有一个IndentationError
(缩进错误),因为你没有正确缩进函数的内容。
接下来,在accept_connection
这个函数里,你对threading
的使用不对。
thread.Threading
是不存在的;你可能是想写threading.Thread
。
args
必须是一个值的序列(比如元组、列表等)。你可能以为(connection)
是一个包含一个值的元组,但实际上不是;元组是用逗号来定义的,而不是用括号。你现在的写法只是把connection
用多余的括号包起来了。你应该写成(connection,)
。
另外,直接在线程对象上调用run
只会在当前线程中运行线程的代码。你应该调用start
,这样才能启动一个新线程,并在那个线程上调用run
方法。
同时,你实际上并没有在任何地方调用这个函数,所以它当然无法执行任何操作。想想你想在哪里调用它。在创建监听套接字后,你想循环调用accept
,为每个接受的连接启动一个新的客户端线程,对吧?所以,你想在一个循环中调用它,可以在connection
内部,或者在最外层(那样的话connection
需要return s
)。
最后,你的accept_connection
函数无法访问其他函数的局部变量;如果你想让它使用一个名为s
的套接字,你必须把它作为参数传递。
所以:
def connection():
address = input("Insert server ip")
port = 44444
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((address, port))
s.listen(1)
print("Server started! Waiting for connections...")
while True:
accept_connection(s)
def accept_connection(s):
connection, address = s.accept()
print('Client connected with address:', address)
t=thread.Threading(target=message, args=(connection,))
t.start()
顺便提一下,使用sock.recv(1024)
时要小心,不要假设你会收到对方用send(msg)
发送的完整消息。你可能会收到完整消息,也可能只收到一半,或者收到完整消息加上客户端稍后发送的另一条消息的一半。套接字只是字节流,就像文件一样,并不是独立消息的流;你需要某种协议来分隔消息。
最简单的协议就是每条消息单独发送一行。这样你可以使用socket.makefile()
和for line in f:
,就像处理真实文件一样。当然,如果你的消息中可以包含换行符,这样就不太适用了,但你可以在一边用反斜杠转义它们,在另一边再解开它们。