所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口
Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的API(应用程序编程接口),也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 Socket中,该 Socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 Socket中,使对方能够接收到这段信息。 Socket是由IP地址和端口结合的,提供向应用层进程传送数据包的机制
一般Socket服务分为三个步骤
1.服务器监听
所谓服务器监听,是指服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态
2.客户端请求
所谓客户端请求,是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端接字提出连接请求 。
3.连接确认
所谓连接确认,是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,就会响应客户端套接字的请求,建立一个新的线程,并把服务器端套接字的描述发送给客户端。一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,接收其他客户端套接字的连接请求。
import socket
server
端
import socket
port = 1314
server = socket.socket()
server.bind(("0.0.0.0", port))
server.listen(10)
connect, address = self.__socket.accept()
print(f"连接方ip为:{address}")
connect.send(byte("Hello World", encoding="utf-8"))
connect.close()
server.close()
client
端
import socket
client = socket.socket()
client.connect(("127.0.0.1", 1314))
get = str(client.recv(1024), encoding="utf-8") # 缓存区为1kb
print(get)
client.close()
server
端函数
函数名 | 参数 | 返回值 |
---|---|---|
bind | (ip,port) | None |
accept | None | socket |
listen | int | None |
client
端函数
函数名 | 参数 | 返回值 |
---|---|---|
connect | (ip,port) | None |
common
函数
函数名 | 参数 | 返回值 |
---|---|---|
send | bytes | None |
recv | int | bytes |
close | None | None |
对于服务端,bind的时候如果ip为’0.0.0.0’,说明绑定ip为全绑定,无论是连接内外网ip,都可以连接。而连接connect的时候,如果服务端在公网,就连接公网ip,port需要选择一个未被占用的port
服务端做消息转发,服务端架设到公网服务器上,内网客户端连接公网服务器后实现转发消息来通讯,这种模式对于少量的连接很方便,但是连接人数多了就会出现拥挤等错误
server.py
class SocketServer:
def __init__(self, logger: Log):
self.__logger = logger
self.__socket = socket.socket()
self.__isrun = False
self.__connects = []
self.__max = 0
def loginfo(self, msg):
print("|INFO|\t" + str(msg))
self.__logger.logmsg(msg)
def createmsg(self, sender, receiver, action, **data) -> str:
msg = {
"code": 200,
"sender": sender,
"receiver": receiver,
"action": action,
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": data}
return json.dumps(msg)
def _logobj(self, data: dict):
action = data["action"]
if action == "msg":
msg = "收到{}发往{}的信息:{}".format(data["sender"], data["receiver"], data["data"]["msg"])
self.loginfo(msg)
if action == "close":
msg = "用户{}关闭了连接".format(data["sender"])
self.loginfo(msg)
if action == "live":
msg = "用户{}查询了当前在线人数:{}".format(data["sender"], len(self.__connects))
self.loginfo(msg)
def _forward(self, data: dict):
try:
goal = data["receiver"]
# 判断是否为服务器消息
if goal == "host":
return
# 判断是否为群发消息
elif goal == "all":
print("|INFO|\t开始转发群消息")
self.__logger.logmsg("开始转发群消息")
for i in self.__connects:
i[1].send(bytes(json.dumps(data), encoding=BufferEncoding))
return
# 监测目标是否上线
else:
for i in self.__connects:
if goal in i:
# 找到目标
receiver: socket.socket = i[1]
# 转发消息
receiver.send(bytes(json.dumps(data), encoding=BufferEncoding))
return
# 未检测到
self.loginfo("目标未上线")
except Exception as e:
self.loginfo(str(e))
def _action(self, index, receiver: socket.socket, address, data: dict):
action = data["action"]
if action == "msg":
self._forward(data)
return 0
if action == "close":
self.__connects.remove((index, receiver, address))
receiver.close()
return -1
if action == "live":
live = []
for i in self.__connects:
live.append(i[0])
msg = self.createmsg("host", data["sender"], "live", count=len(live), index=live)
receiver.send(bytes(msg, encoding=BufferEncoding))
return 0
def _getmessage(self, index, receiver: socket.socket, address):
try:
while self.__isrun:
data = json.loads(str(receiver.recv(BufferSize), encoding=BufferEncoding)) # 4KB缓存
###LOG - Start 服务器Log记录
self._logobj(data)
###End
###Deal - Start 消息处理
result = self._action(index, receiver, address, data)
if result == -1:
return
if result == 0:
continue
###End
except Exception as e:
self.loginfo(str(e))
finally:
self.loginfo(f"{index}的消息接收线程已退出")
def _listen(self):
self.loginfo("成功启动套接字监听")
while self.__isrun:
if len(self.__connects) == self.__max:
break # 达到最大连接数量断开监听
connect, address = self.__socket.accept() # 获取到连接
# 生成标识符
index = ""
for i in range(5):
index += str(random.randint(0, 9))
self.loginfo("接受到:{}的连接,分配的ID:{}".format(address, index))
self.__connects.append((index, connect, address)) # 挂起连接并加入库
msg = self.createmsg("host", index, "msg", index=index, msg="连接成功")
connect.send(bytes(msg, encoding="utf-8")) # 发生返回信息
_thread.start_new_thread(self._getmessage, (index, connect, address,)) # 打开信息监听进程
continue
self.loginfo("监听线程已退出")
def startlisten(self, port, ip="0.0.0.0", count=10):
self.__socket.bind((ip, port))
self.__socket.listen(count)
self.__isrun = True
self.__max = count
self.loginfo("监听初始化完毕,正在启动")
self.loginfo(f"开启端口:{port},支持最大连接数量:{count}")
_thread.start_new_thread(self._listen, ())
def send(self, address, msg):
if not self.__isrun:
return
# 监测目标是否上线
for i in self.__connects:
if address in i:
msg = self.createmsg("host", address, "msg", msg=msg)
i[1].send(bytes(msg, encoding=BufferEncoding))
self.loginfo("发生成功")
return
self.loginfo("目标不在线")
def sendall(self, msg):
if not self.__isrun:
return
for i in self.__connects:
msg = self.createmsg("host", i[0], "msg", msg=msg)
i[1].send(bytes(msg, encoding=BufferEncoding))
self.loginfo("发送成功")
return
def close(self):
self.__isrun = False
# 释放log对象
self.__logger.close()
# 等待未完成的任务
time.sleep(5)
# 释放所有套接字
self.__socket.close()
for i in self.__connects:
i[1].close()
log.py
FileHeader = '''
Socket Log
StartTime:{}
----------------------------------------------------------------
'''
FileTail = '''
----------------------------------------------------------------
EndTime:{}
Count:{}
'''
InfoTemplate = "|INFO|\t[{}]{}"
class Log:
def __init__(self, path):
self.__path = "{}\\{}.log".format(path, time.strftime("%Y%m%d%H%M%S", time.localtime()))
self.__lock = threading.Lock()
self.__counter = 0
def _writer(self, message):
self.__lock.acquire()
self.__counter += 1
stream = open(self.__path, "a+")
stream.write(message + "\n")
stream.close()
self.__lock.release()
def open(self):
self._writer(FileHeader.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime())))
def close(self):
self._writer(FileTail.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), self.__counter - 1))
def logmsg(self, msg):
data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), msg)
self._writer(data)
def logobj(self, obj):
data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), json.loads(obj))
self._writer(data)
main.py
import json
import _thread
import random
import socket
import threading
import time
BufferSize = 4096 # 最大缓存4KB
BufferEncoding = "utf-8" # 缓存编码
reader = open("C:\\0_data\\config.json", "r")
data = json.loads(reader.read())
reader.close()
log = Log(data["path"])
log.open()
server = SocketServer(log)
server.startlisten(data["port"])
try:
while True:
get = input()
if "Close" in get:
server.close()
print("|INFO|\t已关闭所有进程")
break
if "Send" in get:
data = get.split("/")
server.send(data[1], data[2])
else:
print("|INFO|\t键盘输出:", get)
except Exception as e:
server.close()
finally:
print("|INFO|\t程序已退出")
config.json
{
"path": "C:\\0_data",
"port": 1234
}
与服务端相似,只不过仅仅只有一个获取数据的线程
client.py
class SocketClient:
def __init__(self):
self.__socket = socket.socket()
self.__isrun = False
self.__index = -1
def close(self):
msg = {
"code": 200,
"sender": self.__index,
"receiver": "host",
"action": "close",
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": {
}}
self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))
self.__isrun = False
time.sleep(5)
self.__socket.close()
def send(self, address, msg):
if not self.__isrun: return
msg = {
"code": 200,
"sender": self.__index,
"receiver": address,
"action": "msg",
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": {
"msg": msg
}} # 构造返回字符串
self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))
print("发送成功")
def checklive(self):
if not self.__isrun: return
msg = {
"code": 200,
"sender": self.__index,
"receiver": "host",
"action": "live",
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": {
}} # 构造返回字符串
self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))
print("发送成功")
def _recive(self):
try:
while self.__isrun:
data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))
if data["action"] == "live":
print("目前在线人数为", data["data"]["count"])
for i in data["data"]["index"]:
print(i)
continue
msg = "[{}]{}:{}".format(data["time"], data["sender"], data["data"]["msg"])
print(msg)
# 退出消息
print("已退出消息接收")
except Exception as e:
print("接收错误", str(e))
def startconnect(self, ip, port):
self.__isrun = True
self.__socket.connect((ip, port))
# 连接成功
# 获取序列号
data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))
self.__index = data["data"]["index"]
print("连接成功,您的序列号为:", self.__index)
# 开启消息监听线程
_thread.start_new_thread(self._recive, ())
main.py
import _thread
import json
import socket
import time
BufferSize = 4096
client = SocketClient()
client.startconnect("127.0.0.1", 3414)
while True:
get = input()
if get == "Close":
client.close()
break
if "Send" in get:
data = get.split("/")
client.send(data[1], data[2])
if "Check" in get:
client.checklive()
print("程序已结束")
log
类会在服务端处理时记录下所有的信息,在客户端连接后,服务端会生成一个index作为标识符保存在服务器,并且返回一个json字符串到客户端告知index,而客户端想要发送消息的时候,只需要向服务器说明对方的index即可,比如
你的index:12345,对方的index:96354,在控制台里面输入
Send/96354/Hello World
然后程序会创建一个json字符串并发送到服务端
{
"code": 200,
"sender": "12345",
"receiver": "96354",
"action": "msg",
"time": "2022/4/8 11:09:23",
"data":
{
"msg": "Hello World"
}
} # 构造返回字符串
然后服务端会先判断消息的action
标签,如果是msg
标签,那么服务器会调用_forward
函数,首先检测receiver的标识符是否存在,然后找到对应的socket,然后使用该socket把这条msg发送过去
如果你想要查询有哪儿些其他用户,可以在控制台输入
Check
然后程序会创建一个json字符串,不同于上面的,该字符串的action
为live
{
"code": 200,
"sender": "12345",
"receiver": "host",
"action": "live",
"time": "2022/4/8 11:09:23",
"data":
{
}
}
当服务器检测到action
不为msg
的json串,会调用_action
函数进行检测然后返回数据
而服务端和客户端关闭的时候也仅需要输入
Close
服务端输入会释放掉所有连接的socket,而客户端会先构造一个action
为close
的json串传到服务器,服务器会释放掉对于的socket,然后客户端才会释放socket
server.py
import json
import _thread
import random
import socket
import threading
import time
BufferSize = 4096 # 最大缓存4KB
BufferEncoding = "utf-8" # 缓存编码
FileHeader = '''
Socket Log
StartTime:{}
----------------------------------------------------------------
'''
FileTail = '''
----------------------------------------------------------------
EndTime:{}
Count:{}
'''
InfoTemplate = "|INFO|\t[{}]{}"
class Log:
def __init__(self, path):
self.__path = "{}\\{}.log".format(path, time.strftime("%Y%m%d%H%M%S", time.localtime()))
self.__lock = threading.Lock()
self.__counter = 0
def _writer(self, message):
self.__lock.acquire()
self.__counter += 1
stream = open(self.__path, "a+")
stream.write(message + "\n")
stream.close()
self.__lock.release()
def open(self):
self._writer(FileHeader.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime())))
def close(self):
self._writer(FileTail.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), self.__counter - 1))
def logmsg(self, msg):
data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), msg)
self._writer(data)
def logobj(self, obj):
data = InfoTemplate.format(time.strftime("%Y/%m/%d/ %H:%M:%S", time.localtime()), json.loads(obj))
self._writer(data)
class SocketServer:
def __init__(self, logger: Log):
self.__logger = logger
self.__socket = socket.socket()
self.__isrun = False
self.__connects = []
self.__max = 0
def loginfo(self, msg):
print("|INFO|\t" + str(msg))
self.__logger.logmsg(msg)
def createmsg(self, sender, receiver, action, **data) -> str:
msg = {
"code": 200,
"sender": sender,
"receiver": receiver,
"action": action,
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": data}
return json.dumps(msg)
def _logobj(self, data: dict):
action = data["action"]
if action == "msg":
msg = "收到{}发往{}的信息:{}".format(data["sender"], data["receiver"], data["data"]["msg"])
self.loginfo(msg)
if action == "close":
msg = "用户{}关闭了连接".format(data["sender"])
self.loginfo(msg)
if action == "live":
msg = "用户{}查询了当前在线人数:{}".format(data["sender"], len(self.__connects))
self.loginfo(msg)
def _forward(self, data: dict):
try:
goal = data["receiver"]
# 判断是否为服务器消息
if goal == "host":
return
# 判断是否为群发消息
elif goal == "all":
print("|INFO|\t开始转发群消息")
self.__logger.logmsg("开始转发群消息")
for i in self.__connects:
i[1].send(bytes(json.dumps(data), encoding=BufferEncoding))
return
# 监测目标是否上线
else:
for i in self.__connects:
if goal in i:
# 找到目标
receiver: socket.socket = i[1]
# 转发消息
receiver.send(bytes(json.dumps(data), encoding=BufferEncoding))
return
# 未检测到
self.loginfo("目标未上线")
except Exception as e:
self.loginfo(str(e))
def _action(self, index, receiver: socket.socket, address, data: dict):
action = data["action"]
if action == "msg":
self._forward(data)
return 0
if action == "close":
self.__connects.remove((index, receiver, address))
receiver.close()
return -1
if action == "live":
live = []
for i in self.__connects:
live.append(i[0])
msg = self.createmsg("host", data["sender"], "live", count=len(live), index=live)
receiver.send(bytes(msg, encoding=BufferEncoding))
return 0
def _getmessage(self, index, receiver: socket.socket, address):
try:
while self.__isrun:
data = json.loads(str(receiver.recv(BufferSize), encoding=BufferEncoding)) # 4KB缓存
###LOG - Start 服务器Log记录
self._logobj(data)
###End
###Deal - Start 消息处理
result = self._action(index, receiver, address, data)
if result == -1:
return
if result == 0:
continue
###End
except Exception as e:
self.loginfo(str(e))
finally:
self.loginfo(f"{index}的消息接收线程已退出")
def _listen(self):
self.loginfo("成功启动套接字监听")
while self.__isrun:
if len(self.__connects) == self.__max:
break # 达到最大连接数量断开监听
connect, address = self.__socket.accept() # 获取到连接
# 生成标识符
index = ""
for i in range(5):
index += str(random.randint(0, 9))
self.loginfo("接受到:{}的连接,分配的ID:{}".format(address, index))
self.__connects.append((index, connect, address)) # 挂起连接并加入库
msg = self.createmsg("host", index, "msg", index=index, msg="连接成功")
connect.send(bytes(msg, encoding="utf-8")) # 发生返回信息
_thread.start_new_thread(self._getmessage, (index, connect, address,)) # 打开信息监听进程
continue
self.loginfo("监听线程已退出")
def startlisten(self, port, ip="0.0.0.0", count=10):
self.__socket.bind((ip, port))
self.__socket.listen(count)
self.__isrun = True
self.__max = count
self.loginfo("监听初始化完毕,正在启动")
self.loginfo(f"开启端口:{port},支持最大连接数量:{count}")
_thread.start_new_thread(self._listen, ())
def send(self, address, msg):
if not self.__isrun:
return
# 监测目标是否上线
for i in self.__connects:
if address in i:
msg = self.createmsg("host", address, "msg", msg=msg)
i[1].send(bytes(msg, encoding=BufferEncoding))
self.loginfo("发生成功")
return
self.loginfo("目标不在线")
def sendall(self, msg):
if not self.__isrun:
return
for i in self.__connects:
msg = self.createmsg("host", i[0], "msg", msg=msg)
i[1].send(bytes(msg, encoding=BufferEncoding))
self.loginfo("发送成功")
return
def close(self):
self.__isrun = False
# 释放log对象
self.__logger.close()
# 等待未完成的任务
time.sleep(5)
# 释放所有套接字
self.__socket.close()
for i in self.__connects:
i[1].close()
reader = open("C:\\0_data\\config.json", "r")
data = json.loads(reader.read())
reader.close()
log = Log(data["path"])
log.open()
server = SocketServer(log)
server.startlisten(data["port"])
try:
while True:
get = input()
if "Close" in get:
server.close()
print("|INFO|\t已关闭所有进程")
break
if "Send" in get:
data = get.split("/")
server.send(data[1], data[2])
else:
print("|INFO|\t键盘输出:", get)
except Exception as e:
server.close()
finally:
print("|INFO|\t程序已退出")
client.py
import _thread
import json
import socket
import time
BufferSize = 4096
class SocketClient:
def __init__(self):
self.__socket = socket.socket()
self.__isrun = False
self.__index = -1
def close(self):
msg = {
"code": 200,
"sender": self.__index,
"receiver": "host",
"action": "close",
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": {
}}
self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))
self.__isrun = False
time.sleep(5)
self.__socket.close()
def send(self, address, msg):
if not self.__isrun: return
msg = {
"code": 200,
"sender": self.__index,
"receiver": address,
"action": "msg",
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": {
"msg": msg
}} # 构造返回字符串
self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))
print("发送成功")
def checklive(self):
if not self.__isrun: return
msg = {
"code": 200,
"sender": self.__index,
"receiver": "host",
"action": "live",
"time": time.strftime("%Y/%m/%d %H:%M:%S", time.localtime()),
"data": {
}} # 构造返回字符串
self.__socket.send(bytes(json.dumps(msg), encoding="utf-8"))
print("发送成功")
def _recive(self):
try:
while self.__isrun:
data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))
if data["action"] == "live":
print("目前在线人数为", data["data"]["count"])
for i in data["data"]["index"]:
print(i)
continue
msg = "[{}]{}:{}".format(data["time"], data["sender"], data["data"]["msg"])
print(msg)
# 退出消息
print("已退出消息接收")
except Exception as e:
print("接收错误", str(e))
def startconnect(self, ip, port):
self.__isrun = True
self.__socket.connect((ip, port))
# 连接成功
# 获取序列号
data = json.loads(str(self.__socket.recv(BufferSize), encoding="utf-8"))
self.__index = data["data"]["index"]
print("连接成功,您的序列号为:", self.__index)
# 开启消息监听线程
_thread.start_new_thread(self._recive, ())
client = SocketClient()
client.startconnect("127.0.0.1", 3414)
while True:
get = input()
if get == "Close":
client.close()
break
if "Send" in get:
data = get.split("/")
client.send(data[1], data[2])
if "Check" in get:
client.checklive()
print("程序已结束")