套接字没有监听多个请求

socket is not listening for multiple request

这是服务器的代码 -

import socket, select,re


def getSocket( idd):
  return CONNECTION_LIST[idd]


def broadcast_data (sock, message):
    for socket in CONNECTION_LIST:
        if socket != server_socket and socket != sock :
            try :
                socket.send(message)
            except :
                socket.close()
                CONNECTION_LIST.remove(socket)


def single_client (sock , message , idd):
  socket = getSocket ( idd )
  if socket :
    socket.send(message)
  else:
    print "chudap"


if __name__ == "__main__":

    CONNECTION_LIST = []
    RECV_BUFFER = 4096
    PORT = 5000
    PORTC = 2225

    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(("0.0.0.0", PORT))
    server_socket.listen(10)

    listen = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    listen.bind(("0.0.0.0" , PORTC))
    #listen.listen(10)

    CONNECTION_LIST.append(server_socket)
    CONNECTION_LIST.append(listen)
    print "Chat server started on port " + str(PORT)

    idd = 1
    while 1:
        # Get the list sockets which are ready to be read through select
        read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])

        for sock in read_sockets:
            if sock == server_socket:
                sockfd, addr = server_socket.accept()
                CONNECTION_LIST.append(sockfd)
                #name = sockfd.recv(RECV_BUFFER)
                print "connected from ip %s, id assigned is %d" % (addr[0] , idd)
                broadcast_data(sockfd, "client with IP %s has entered with id = %d\n" % (addr[0] , idd))
                idd += 1
            elif sock == listen:
                print "debugging"
                data,addr = listen.recvfrom(RECV_BUFFER)
                print "Received server probe request from [%s:%s]"%addr
                listen.sendto("iam" , addr)#(addr[0] , 2624))
                listen.close()
                CONNECTION_LIST.remove(listen)
                listen = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                listen.bind(("0.0.0.0" , PORTC))
                CONNECTION_LIST.append(listen)
            else:
                try:
                    data = sock.recv(RECV_BUFFER)
                    if re.findall(r'.*/msg\d+' , data):
                      #print "got single client message request" + data

                      name = "private message from " + re.findall('([^:]+): /msg(\d+)([^"]+)'  , data)[0][0] + ": "
                      #print name

                      eid = int(re.findall('([^:]+): /msg(\d+)([^"]+)'  , data)[0][1])
                      #print eid

                      data = re.findall('([^:]+): /msg(\d+)([^"]+)'  , data)[0][2]
                      #print data

                      data = name + data

                      #print "single client message sent with id = %d" %eid

                      single_client( sock , data , int(eid))
                    elif data:
                        broadcast_data(sock, data)

                except:
                    broadcast_data(sock, "Client (%s, %s) is offline" % addr)
                    print "Client (%s, %s) is offline" % addr
                    sock.close()
                    CONNECTION_LIST.remove(sock)
                    continue

    server_socket.close()

这是客户端的代码 -

import socket, select, string, sys

def prompt() :
    sys.stdout.write('<You>: ')
    sys.stdout.flush()

def exit(sock):
  print "\n Thank you for using chat application\nBye"
  sock.close()
  sys.exit()

def printUsage():
  print "1. By default your message will be sent to all clients sitting on the chat server"
  print "2. You can send a private message to a person by starting your message as \"/msg{id}{Your message}\" for example /msg2Hi will send \"hi\" to client with id 2"
  print "3. For quitting simply type \"/q\" or \"/quit\""
  prompt()

PORTS = 2225
PORTC = 2624


if __name__ == "__main__":


    broad = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    broad.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    broad.bind(( '0.0.0.0' , 2624) )
    broad.sendto(b'whoisserver', 0, ("255.255.255.255", PORTS))
    broad.settimeout(10)
    print 15*"-" + "WELCOME TO CHATVILLE" + 15*"-" + "\nFinding the server"
    try:
      data , addr = broad.recvfrom(10)
    except:
      print "Can't find server ! Please ensure that server is up"
      broad.close()
      sys.exit()
    broad.close()


    if data <> "iam":
      print "Can't find a valid server !"
      sys.exit()
    host = addr[0]
    port = 5000
    print addr


#    host = sys.argv[1]
#    port = int(sys.argv[2])
#    print host,port
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(2)

    name = raw_input("Please Enter your name: ")
    try :
        s.connect((host, port))
        s.send(name)
    except :
        print 'Unable to connect'
        sys.exit()

    print 'Connected to remote host. Enjoy...............................'
    name = "<" + name + ">" + ": "
    print " - type /h to see usage instructions any time :) - "
    prompt()

    while 1:
        socket_list = [sys.stdin, s]

        read_sockets, write_sockets, error_sockets = select.select(socket_list , [], [])

        for sock in read_sockets:
            if sock == s:
                data = sock.recv(4096)
                if not data :
                    print '\nDisconnected from chat server'
                    sys.exit()
                else :
                    print ""
                    sys.stdout.write(data)
                    prompt()

            else :
                msg = sys.stdin.readline()
                if str.startswith(msg, "/h") or str.startswith(msg,"/help"):
                  printUsage()
                elif str.startswith(msg, "/quit") or str.startswith(msg,"/q"):
                  exit(s)
                else:
                  msg = name + msg
                  s.send(msg)
                  prompt()

主要问题是只有一个客户端能够在第一个客户端连接到服务器后立即连接,其他客户端无法发现该服务器。

我尝试通过 tcpdump 查看客户端代码,我可以看到数据包在端口号 2225 上发送,但是套接字 listen 在第一次连接后完全没有响应。

PS - 早些时候我没有一次又一次地制作 listen 套接字的实例,但我也尝试了这个,但没有成功。

在服务器中 broadcast_data() 不会从要写入的套接字中排除 UDP 套接字 (listen),因此它会在该套接字上调用 send()。这失败了异常

socket.error: [Errno 89] Destination address required

因为没有提供地址(并且不能与 socket.send() 一起)。异常处理程序然后关闭 UDP 套接字,并且无法接收来自新客户端的进一步消息。这就是其他客户端无法连接到服务器的原因。

这是一个完美的例子,说明了为什么使用裸 except 不是一个好主意,即处理所有异常的 except 语句。在这种情况下,处理程序甚至没有记录事实就关闭了 UDP 套接字。您的代码中还有其他裸 except 语句实例。我建议您处理特定的异常以避免此类错误。您可以通过将 UDP 套接字添加到要忽略的套接字列表来修复它:

def broadcast_data(sock, message):
    for socket in CONNECTION_LIST:
        if socket not in (server_socket, sock, listen):
            try :
                socket.send(message)
            except socket.error as exc:
                print '!!! An error occurred while writing to client. !!!'
                print exc
                socket.close()
                CONNECTION_LIST.remove(socket)

现在不会尝试向 UDP listen 套接字发送消息,套接字也不会因错误而关闭。

P.S。主循环中关闭和重新打开 listen 套接字的代码不是必需的。