TCP聊天服务器套接字v1.1
文章目录
| 1. 服务器代码改进 / bug改进
(1).发送函数改为@function
class Server():
...
def send(self, sock, user, mes):
self.QUIT(user, lambda: sock.sendall(mes.encode(self.encode)))()
def send(self, sock, user, mes):
@self.QUIT(user)
def func():
sock.sendall(mes.encode(self.encode))
return func()
(2).异常运行函数改为三叠函数
def QUIT(self, user, command):
sock = list(self.connect.keys())[list(self.connect.values()).index(user)]
def logs(*args, **kargs):
try:
command(*args, **kargs)
except:
self.errs.append(sock)
return logs
def QUIT(self, user):
sock = list(self.connect.keys())[list(self.connect.values()).index(user)]
def prop(command, *args, **kwargs):
def logs(*args, **kwargs):
try:
command(*args, **kwargs)
return True
except:
logging.exception(str())
if self.errduring is False:
self.errs.append((sock,user)) #在遍历时列表增加 -> 触发RuntimeError
return False
return logs
return prop
(3).服务端在下线时列表在遍历时 增加下线的服务端 -> 触发RuntimeError
bug改进方法:
增加errduring
; inc
参数
def ServerMessage(self, mes):
for sock, user in self.connect.items():
if not sock in self.errs:
self.send(sock, user, mes)
def UserMessage(self, address, _user, mes):
if not mes:
return
mes = mes.decode(encoding="utf8")
for sock, user in self.connect.items():
if not sock in self.errs:
send_message = Server.user_message % ("brown" if _user == user else "red",
_user,
address,
"(我自己)" if _user == user else "",
mes)
self.send(sock, user, send_message)
logger.info(f"{
address}[{
_user}] : {
mes}")
self.error_handle()
def error_handle(self):
for sock in self.errs:
sock.close()
logger.exception(msg = str())
Q = Server.quit_message % user
logger.info(Q)
self.connect.pop(sock)
self.ServerMessage(Q)
def ServerMessage(self, mes, inc=True):
self.errduring = True
for sock, user in self.connect.items():
if not sock in self.errs:
self.send(sock, user, mes)
if inc:
self.error_handle()
self.errduring = False
def UserMessage(self, address, _user, mes,inc=True):
if not mes:
return
self.errduring = True
for sock, user in self.connect.items():
if not sock in self.errs:
send_message = Server.user_message % ("brown" if _user == user else "red",
_user,
address,
"(我自己)" if _user == user else "",
mes)
self.send(sock, user, send_message)
logger.info(f"{
address}[{
_user}] : {
mes}")
if inc:
self.error_handle()
self.errduring = False
def error_handle(self):
for _, user in self.errs: #使用变量_ : 为了防止触发KeyError: <socket.socket [closed] fd=-1, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0>
if user in list(self.connect.values()):
sock = list(self.connect.keys())[list(self.connect.values()).index(user)]
logger.exception(msg = str())
Q = Server.quit_message % (user, self._str_sockets())
logger.info(Q)
self.connect.pop(sock)
self.ServerMessage(Q, False) #防止多次调用error_handle函数
sock.close()
(4)获取真正本机的ip地址
如果直接用socket.gethostbyname(socket.gethostname())
获取地址,很有可能是错误的(Vmware虚拟机的地址
、127.0.0.1
等)
搜索后我得到了下面这段精巧的代码:
...
def get_host_ip() -> str:
"""get current IP address"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
finally:
s.close()
return ip
通过UDP
尝试连接8.8.8.8:80
,不管是否连接成功,获得的本机IP一定是正确的。在Ubuntu
(kivydev)和Windows
上都可用,还省去了判断操作系统的大段代码.
那么代码可以改为:
server = Server(socket.gethostname(),429)
server = Server(get_host_ip(),429)
| 2.新增命令功能 (在输入框添加"/")
class Command_Handler(object):
def __init__(self, bind=None):
"""Bind Server class"""
self.bind = bind
def _function(self, _list, client):
data = {
"/info" : {
"-v": self.get_version(),
"-id" : self.get_id(client),
"-i" : self.info(),
"-h" : self.help()},
}
_dict = data
for n in range(len(_list)):
if type(_dict) == dict:
_dict = _dict.get(_list[n], self.unknown(" ".join(_list)))
else:
break
if type(_dict) == dict:
_dict = "Error:\n<font color='blue'>This command must take more arguments. Such as %s.</font>" % list(_dict.keys())
return _dict
@staticmethod
def help():
return """/info [-v] [-id] [-i]
-v : get version of program.
-id : get your id.
-i : get information.
-h : help.
For example, <font color=red>/info -id</font>"""
@staticmethod
def get_version():
return "version : " + str(__version__)
def get_id(self, client):
return "Your id is {}.".format(id(client))
def info(self):
return f"Socket Server[version {
self.get_version()}] By zmh."
def unknown(self,s):
return """Error:
No command named "%s". Please search [/info -h] to help.
%s""" % (s, self.help()