以下是服务器的代码 -
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 套接字的实例,但我也尝试了这个并且它没有用完。
答案 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
套接字的代码。