socket没有监听多个请求

时间:2016-03-20 10:27:57

标签: python sockets networking broadcast

以下是服务器的代码 -

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 套接字的实例,但我也尝试了这个并且它没有用完。

1 个答案:

答案 0 :(得分:1)

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

socket.error: [Errno 89] Destination address required

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

这是一个完美的例子,说明为什么使用一个裸的除外,即处理所有异常的except语句不是一个好主意。在这种情况下,处理程序关闭UDP套接字甚至没有记录事实。您的代码中还有其他bare语句的实例。我建议您处理特定的异常以避免此类错误。您可以通过将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套接字的代码。