2016-03-20 99 views
0

這裏是服務器的代碼 -插座沒有偵聽多個請求

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,但插座第一個連接後不響應。

PS - 早些時候我沒有製作實例一次又一次地聽着套接字,但我也嘗試過這種方式,結果沒有奏效。

回答

1

在服務器broadcast_data()不排除UDP套接字(listen)從套接字寫入,所以它在該套接字上調用send()。這種情況除外

socket.error: [Errno 89] Destination address required 

因爲沒有地址提供(並且不能與socket.send())。異常處理程序然後關閉UDP套接字,並且不會收到來自新客戶端的更多消息。這就是爲什麼其他客戶端無法連接到服務器。

這是一個很好的例子,爲什麼使用bare除外,即處理所有異常的except語句不是一個好主意。在這種情況下,處理程序關閉UDP套接字,甚至不記錄事實。代碼中還有其他裸露的except語句實例。我建議你處理特定的異常以避免這種錯誤。

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插座和插座不會因封閉錯誤:您可以通過添加UDP套接字套接字的列表忽略修復。

P.S.您主循環中關閉並重新打開listen套接字的代碼不是必需的。

+0

哇!從字面上哇,我的腦海裏從來沒有想過這個問題可以發生在這裏。謝謝 :) –