首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python套接字,高级聊天框

Python套接字,高级聊天框
EN

Stack Overflow用户
提问于 2016-09-21 13:51:30
回答 2查看 645关注 0票数 1

我想创建一个同时处理的服务器(处理:从客户端接收数据,同时向所有客户端发送数据!)

实际上我在尝试创建一个聊天室。该项目的工作方式如下:

1)将有一个处理客户端的服务器。

2)多个客户端可以加入服务器。

3)客户端向服务器发送消息(字符串)。

4)服务器从客户端接收消息,然后将消息发送给所有客户端,但从客户端获取消息的客户端除外。

这就是客户之间交流的方式。没有私人信息可用。当有人点击enter时,所有客户端都会在他们的屏幕上看到消息。

客户端模块易于制作,因为客户端只与一个套接字(服务器)通信。

另一方面,服务器模块非常复杂,我不知道怎么做(我也知道线程)。

这是我的头像:

代码语言:javascript
复制
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、发送的方法混合在一起,那么问题就变得更加复杂了。

那么,做这件事的正确和最好的方法是什么呢?我也想举个例子。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-09-21 14:50:40

当不同的客户端相互独立时,多线程是很棒的:您编写代码时就好像只有一个客户端一样,并且为每个客户端启动了一个线程。

但是在这里,来自一个客户的东西必须发送给其他客户。每个客户端一个线程肯定会导致同步噩梦。因此,让我们调用select来拯救!select.select允许轮询一个套接字列表,并在一个套接字准备好后立即返回。在这里,您只需构建一个包含侦听套接字和所有可接受套接字的列表(该部分最初为空.):

  • 当侦听套接字准备好阅读时,接受一个新套接字并将它添加到列表中。
  • 当另一个套接字准备好读取时,从它读取一些数据。如果读取0字节,则其对等点已被关闭或关闭:关闭它并从列表中删除它。
  • 如果您已从一个已接受的套接字中读取某些内容,请在列表上循环,跳过侦听套接字和已从该套接字读取的套接字并将数据发送到任何其他套接字。

代码可以是(或多或少):

代码语言:javascript
复制
    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()

您当然需要实现一种机制来要求服务器停止,并测试所有这些,但这应该是一个起点。

票数 1
EN

Stack Overflow用户

发布于 2016-09-21 17:12:59

这是我的最后一个节目,而且很有魅力。

Server.py

导入套接字,选择

类服务器:

代码语言:javascript
复制
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进口*

类客户端:

代码语言:javascript
复制
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是包含所有就绪套接字的列表。但我并没有弄清wx是什么。

第一个参数是套接字列表,第二个是可接受的客户端,第三个参数是什么?为什么我要再给一次套接字列表?

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39618547

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档