我想创建一个同时处理的服务器(处理:从客户端接收数据,同时向所有客户端发送数据!)
实际上我在尝试创建一个聊天室。该项目的工作方式如下:
1)将有一个处理客户端的服务器。
2)多个客户端可以加入服务器。
3)客户端向服务器发送消息(字符串)。
4)服务器从客户端接收消息,然后将消息发送给所有客户端,但从客户端获取消息的客户端除外。
这就是客户之间交流的方式。没有私人信息可用。当有人点击enter时,所有客户端都会在他们的屏幕上看到消息。
客户端模块易于制作,因为客户端只与一个套接字(服务器)通信。
另一方面,服务器模块非常复杂,我不知道怎么做(我也知道线程)。
这是我的头像:
import socket, threading
class Server:
def __init__(self, ip = "", port = 5050):
'''Server Constructor. If __init__ return None, then you can use
self.error to print the specified error message.'''
#Error message.
self.error = ""
#Creating a socket object.
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Trying to bind it.
try:
self.server.bind( (ip, port) )
pass
#Failed, because socket has been shuted down.
except OSError :
self.error = "The server socket has been shuted down."
return None
#Failed, because socket has been forcibly reseted.
except ConnectionResetError:
self.error = "The server socket has been forcibly reseted."
return None
#Start Listening.
self.server.listen()
#_____Other Variables_____#
#A flag to know when to shut down thread loops.
self.running = True
#Store clients here.
self.clients = []
#_____Other Variables_____#
#Start accepting clients.
thread = threading.thread(target = self.acceptClients)
thread.start()
#Start handling the client.
self.clientHandler()
#Accept Clients.
def acceptClients(self):
while self.running:
self.clients.append( self.server.accept() )
#Close the server.
self.server.close()
#Handle clients.
def clientHandler(self):
while self.running:
for client in self.clients:
sock = client[0]
addr = client[1]
#Receive at most 1 mb of data.
#The problem is that recv will block the loop!!!
data = sock.recv(1024 ** 2)如您所见,我接受使用线程的客户端,这样server.accept()就不会阻塞程序。然后我把客户储存在一张名单里。
但问题在clientHandler.上我怎么才能同时从所有的客户那里得到回复呢?第一个recv将阻塞循环!
我还尝试为每个新客户端启动新线程(clientHandlers),但问题是同步问题。
那么发送呢?服务器必须向所有客户端发送数据,因此clientHandler尚未完成。但是如果我将、recv、和发送的方法混合在一起,那么问题就变得更加复杂了。
那么,做这件事的正确和最好的方法是什么呢?我也想举个例子。
发布于 2016-09-21 14:50:40
当不同的客户端相互独立时,多线程是很棒的:您编写代码时就好像只有一个客户端一样,并且为每个客户端启动了一个线程。
但是在这里,来自一个客户的东西必须发送给其他客户。每个客户端一个线程肯定会导致同步噩梦。因此,让我们调用select来拯救!select.select允许轮询一个套接字列表,并在一个套接字准备好后立即返回。在这里,您只需构建一个包含侦听套接字和所有可接受套接字的列表(该部分最初为空.):
代码可以是(或多或少):
main = socket.socket() # create the listening socket
main.bind((addr, port))
main.listen(5)
socks = [main] # initialize the list and optionaly count the accepted sockets
count = 0
while True:
r, w, x = select.select(socks, [], socks)
if main in r: # a new client
s, addr = main.accept()
if count == mx: # reject (optionaly) if max number of clients reached
s.close()
else:
socks.append(s) # appends the new socket to the list
elif len(r) > 0:
data = r[0].recv(1024) # an accepted socket is ready: read
if len(data) == 0: # nothing to read: close it
r[0].close()
socks.remove(r[0])
else:
for s in socks[1:]: # send the data to any other socket
if s != r[0]:
s.send(data)
elif main in x: # close if exceptional condition met (optional)
break
elif len(x) > 0:
x[0].close()
socks.remove(x[0])
# if the loop ends, close everything
for s in socks[1:]:
s.close()
main.close()您当然需要实现一种机制来要求服务器停止,并测试所有这些,但这应该是一个起点。
发布于 2016-09-21 17:12:59
这是我的最后一个节目,而且很有魅力。
Server.py
导入套接字,选择
类服务器:
def __init__(self, ip = "", port = 5050):
'''Server Constructor. If __init__ return None, then you can use
self.error to print the specified error message.'''
#Error message.
self.error = ""
#Creating a socket object.
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Trying to bind it.
try:
self.server.bind( (ip, port) )
pass
#Failed, because socket has been shuted down.
except OSError :
self.error = "The server socket has been shuted down."
#Failed, because socket has been forcibly reseted.
except ConnectionResetError:
self.error = "The server socket has been forcibly reseted."
#Start Listening.
self.server.listen()
#_____Other Variables_____#
#A flag to know when to shut down thread loops.
self.running = True
#Store clients here.
self.sockets = [self.server]
#_____Other Variables_____#
#Start Handling the sockets.
self.handleSockets()
#Handle Sockets.
def handleSockets(self):
while True:
r, w, x = select.select(self.sockets, [], self.sockets)
#If server is ready to accept.
if self.server in r:
client, address = self.server.accept()
self.sockets.append(client)
#Elif a client send data.
elif len(r) > 0:
#Receive data.
try:
data = r[0].recv( 1024 )
#If the client disconnects suddenly.
except ConnectionResetError:
r[0].close()
self.sockets.remove( r[0] )
print("A user has been disconnected forcible.")
continue
#Connection has been closed or lost.
if len(data) == 0:
r[0].close()
self.sockets.remove( r[0] )
print("A user has been disconnected.")
#Else send the data to all users.
else:
#For all sockets except server.
for client in self.sockets[1:]:
#Do not send to yourself.
if client != r[0]:
client.send(data)
server = Server()
print("Errors:",server.error)Client.py
导入套接字,线程
从tkinter进口*
类客户端:
def __init__(self, ip = "192.168.1.3", port = 5050):
'''Client Constructor. If __init__ return None, then you can use
self.error to print the specified error message.'''
#Error message.
self.error = ""
#Creating a socket object.
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Trying to bind it.
try:
self.server.connect( (ip, port) )
pass
#Failed, because socket has been shuted down.
except OSError :
self.error = "The client socket has been shuted down."
return
#Failed, because socket has been forcibly reseted.
except ConnectionResetError:
self.error = "The client socket has been forcibly reseted."
return
#Failed, because socket has been forcibly reseted.
except ConnectionRefusedError:
self.error = "The server socket refuses the connection."
return
#_____Other Variables_____#
#A flag to know when to shut down thread loops.
self.running = True
#_____Other Variables_____#
#Start the GUI Interface.
def startGUI(self):
#Initialiazing tk.
screen = Tk()
screen.geometry("200x100")
#Tk variable.
self.msg = StringVar()
#Creating widgets.
entry = Entry( textvariable = self.msg )
button = Button( text = "Send", command = self.sendMSG )
#Packing widgets.
entry.pack()
button.pack()
screen.mainloop()
#Send the message.
def sendMSG(self):
self.server.send( str.encode( self.msg.get() ) )
#Receive message.
def recvMSG(self):
while self.running:
data = self.server.recv(1024)
print( bytes.decode(data) )
#New client.
main = Client()
print("Errors:", main.error)
#Start a thread with the recvMSG method.
thread = threading.Thread(target = main.recvMSG)
thread.start()
#Start the gui.
main.startGUI()
#Close the connection when the program terminates and stop threads.
main.running = False
main.server.close()这个程序运行得很好,完全符合我的要求。
但我还有一些问题。
r,w,x= select.select(self.sockets,[],self.sockets)
r是包含所有就绪套接字的列表。但我并没有弄清w和x是什么。
第一个参数是套接字列表,第二个是可接受的客户端,第三个参数是什么?为什么我要再给一次套接字列表?
https://stackoverflow.com/questions/39618547
复制相似问题