我一直在尝试通过我的局域网创建一个TCP python服务器,但我一直遇到这个项目的问题。我的问题是:是否可以从1台服务器向多个客户端发送消息(通过TCP)? (即客户端1发送消息" Hello world"它在所有其他客户端[clients-2,clients-3]上显示消息)。到目前为止,我的服务器代码是:
import socket, time, sys
import threading
TCP_IP = input("Host IP: ")
TCP_PORT = int(input("Host Port: "))
BUFFER_SIZE = 1024
def createNewThread(function):
threading.Thread(target=function).start()
def Listening():
try:
while True:
s.listen(1)
conn,addr = s.accept()
threading.Thread(target=Listening).start()
print("User joined with IP %s" % (addr[0]))
while 1:
data = conn.recv(BUFFER_SIZE)
if not data: break
conn.send(addr[0].encode("utf-8") + b': ' + data)
conn.close()
except ConnectionResetError as e:
print("Connection was closed: ", e)
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP,TCP_PORT))
print("-----Server started-----")
Listening()
except socket.error as e:
print("Socket error occured. More info: ", e)
继承我的客户代码:
import socket, sys, time
TCP_IP = input("Connect to Local IP: ")
TCP_PORT = int(input("Connect to Local Port: "))
BUFFER_SIZE = 1024
running = True
while running == True:
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Connecting...")
s.connect((TCP_IP,TCP_PORT))
print("Connected!")
while True:
MESSAGE = input("Message: ")
if MESSAGE == "exit":
s.close()
raise SystemExit
s.send(MESSAGE.encode('ascii'))
data = s.recv(BUFFER_SIZE)
print(data.decode("utf-8"))
running = False
time.sleep(20)
except:
print(sys.exc_info()[0])
time.sleep(1)
提前感谢任何答案!
修改 我希望输出看起来像这样:
User3's IP: Message they sent
User1's IP: Message they sent
Message: What do you want to send?
答案 0 :(得分:1)
你的代码......非常奇怪。首先,您在accept
上创建一个新线程,但是您将侦听器发送到该线程而不是客户端。因此,您的线程永远不会死亡,并且您有内存和CPU泄漏。更糟糕的是:在您的代码中,线程数等于所有客户端的数量曾已连接到服务器。这很糟糕。
在服务器端试试这个:
def client(conn):
while True:
data = conn.recv(BUFFER_SIZE)
if not data:
break
conn.send(data) # simple ping
def listener():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(5)
while True:
conn, addr = s.accept()
threading.Thread(target=client, args=(conn,)).start()
if __name__ == '__main__':
listener()
代码更短,更简单,没有内存/ CPU泄漏。
现在将数据发送到所有客户端。你必须跟踪它们。你可以通过保持客户的全球命令来实现这一目标:
CLIENTS = {}
现在在听众中你做了:
def listener():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP,TCP_PORT))
s.listen(5)
while True:
conn, addr = s.accept()
# register client
CLIENTS[conn.fileno()] = conn
threading.Thread(target=client, args=(conn,)).start()
并在客户端:
def client(conn):
while True:
data = conn.recv(BUFFER_SIZE)
if not data:
break
# broadcast
for client in CLIENTS.values():
client.send(data)
# the connection is closed: unregister
del CLIENTS[conn.fileno()]
该代码存在一个小问题(实际上有几个问题,例如错误处理)。如果某些客户在我们循环遍历CLIENTS
字典时取消注册,会发生什么? Python将抛出异常。简单的解决方案是在插入,删除和迭代时锁定字典。
如果其他套接字重复使用之前的fileno()
,则会出现竞争条件。在这种情况下,您可能希望手动为套接字生成ID(最好通过使用自定义类包装socket
对象)。
请注意,可以使用set
代替dict
。但是,您最终需要一个dict,因为在某些时候您可能希望将msg发送到特定的客户端(由某个ID标识)。