以网络安全工具为脉络,分类解析攻防、检测、防护等各类工具的原理与实操,构建工具应用体系,助力读者掌握工具使用技巧;
今天给大家带来的是TCP客户端以及服务端简单通信的python代码实现;
文章目录
简介
今天给大家带来的是TCP客户端以及服务端简单通信的python代码实现,这个专栏文章我会尽量用大白话讲解每一步代码的作用;
代码实现(第一版---只能实现一次会话)
TCP客户端
在渗透测试过程中,经常需要创建一个TCP客户端,用来测试服务、发送垃圾数据、进行fuzz等等。
- 如果黑客潜伏在某大型企业的内网环境中,则不太可能直接获取网络工具或编译器,有时甚至连复制/粘贴或者连接外网这种最基本的功能都用不了。
- 在这种情况下,能快速创建一个TCP客户端将会是一项极其有用的能力。

而通常来说双方通信的基础条件是需要有客户端 以及服务端,双方进行通信。所以话不多说,
- 我们先开始实现TCP客户端的代码:
python
import socket
target_host = "127.0.0.1"
target_port = 9990
# 创建一个socket项目
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# AF_INET参数表示我们将使用标准的IPv4地址或主机名
# SOCK_STREAM表示这是一个TCP客户端
# 建立链接
client.connect((target_host,target_port))
# 发送数据
# client.send(b"GET / HTTP/1.1\r\nTell Me:I am your baby!\r\n\r\n" )
message = 'Hello World, I want to go HVV.'
client.send(message.encode('utf-8'))
# 接收数据
respond = client.recv(1024)
print(respond.decode())
client.close()
- 代码解释:
- socket.socket(socket.AF_INET,socket.SOCK_STREAM):
AF_INET参数表示我们将使用标准的IPv4地址 或主机名,SOCK_STREAM表示这是一个TCP客户端; - 通过
connect()函数进行连接,并send()发送一次数据到服务端; - 我们接收到服务端发送的数据后,直接
close()关闭连接 (不再进行后续的通信,这也是为什么只能进行一次通话的重要原因)
- socket.socket(socket.AF_INET,socket.SOCK_STREAM):
TCP服务端
bash
import socket
import threading
ip = '0.0.0.0'
port = 9990
def main():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 服务器指定监听的地址与端口
server.bind((ip,port))
# 让服务器开始监听,(最大等待连接数为5)
server.listen(5)
print(f'[*] Listening on {ip}:{port}')
while(True):
client,address = server.accept() # 当连接建立的时候
# client:接收到的客户端socket对象
# address:远程连接的详细信息
print(client)
print(address)
print(f'[*] Accepted data from {address[0]}:{address[1]}')
# 创建一个新的线程,让它指向handle_client函数,并传入client变量
client_handler = threading.Thread(target=handle_client,args=(client,))
client_handler.start()
def handle_client(client_socket):
with client_socket as sock:
request = sock.recv(1024)
print(f'[*] Receive:{request.decode("utf-8")}')
sock.send(b'ACK')
if __name__ == '__main__':
main()
- 代码解释:
server = socket.socket:很简单的道理,既然客户端 要建立socket对象,那么服务端自然也是要有相应的服务;server.listen(5):绑定{ip}:{port}后,我们进行listen监听,并等待客户端发送消息;- 原理:客户端发送消息------>服务端接受消息------>代表双方的连接已经建立;
request = sock.recv(1024):表示接受数据的大小,可以是1024,2048,4096,都可以;- 将接收到的客户端socket对象保存到
client变量中,将远程连接的详细信息保存到address变量中。然后,创建一个新的线程,让它指向handle_client函数,并传入client变量;
(是不是有点难理解 ,没关系,我也不是很懂这个,所以我才要讲后面的第二版代码)
效果显示
我们分别执行服务端以及客户端的代码,得到如下结果:

服务端接收结果:

客户端结果:


不出意外,客户端发送一次信息后就自己结束了会话;
- 注意:只有客户端发送数据,服务端接收到数据并返回数据后,才能算一次完整的会话(TCP三次握手)
- 不然的话只能算是TCP半连接;
代码实现(第二版---可以实时通信,并由客户端主动结束会话)
第二版代码优化地方:
- TCP服务端 不再使用
handle_client这种难理解的函数操作;- 双方可以实现实时通信,并由客户端主动结束会话;
TCP客户端:
python
import socket
ip = '127.0.0.1'
port = 9990
def Tcp_Client():
# 还是老样子,像创建socket对象
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect((ip,port))
print(f'已成功连接到服务端:{ip}:{port}')
try:
while(True):
# 正常情况------发送信息到服务端,并将信息encode
send_msg = input("请输入客户端要发送的信息:")
client.send(send_msg.encode('utf-8'))
# 异常情况------若发送bye,等待服务端恢复后结束会话
if send_msg.lower() == 'bye':
data = client.recv(4096)
# 解析data
# data_decode = data.decode()
# (这里我就不创建data_encode变量了,两者效果一样)
print(f"已收到服务端的回复:{data.decode('utf-8')},即将结束对话")
# 正常情况------接受服务端的数据
data = client.recv(4096)
if not data:
# 如果服务端返回数据为空
print("服务端已异常断开连接")
break
# 正常情况------双方还在正常通信
# data_decode = data.decode('utf-8')
print(f"收到服务端发来的信息:{data.decode('utf-8')}")
finally:
client.close()
print("客户端已关闭")
if __name__ == "__main__":
Tcp_Client()
作用:客户端负责连接服务端、发送消息并接收回复,流程为:创建 socket → 连接服务端 → 收发数据 → 关闭连接。
改进之处:

TCP服务端:
python
import socket
# 0.0.0.0表示任何人都可访问TCP服务端
ip = "0.0.0.0"
port = 9990
def Tcp_Server():
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 绑定IP和port
server.bind((ip,port))
# 开始监听连接(最大等待连接数为5)
server.listen(5)
print(f"[*] Listening on {ip}:{port}")
# 当连接建立的时候
client,address = server.accept() # 好奇这两个变量的内容是什么
# 所以打印出来看看
print(f'Client including {client}')
print(f'address including {address}')
print(f'Accept data from {address}') # address的内容:{ip}:{port}
try:
while(True):
# 接收客户端信息
data = client.recv(4096)
if not data:
print("客户端已断开连接")
break
# 解析data
data_decode = data.decode()
print(f"收到客户端信息:{data_decode}")
# 如果客户端发送bye,则通信结束
if data_decode.lower() == 'bye':
send_msg = "Receive your message,looking forward to next time"
client.send(send_msg.encode('utf-8'))
break
# 发送回复的消息
send_msg_server = input("请输入回复给客户端的消息:")
client.send(send_msg_server.encode('utf-8'))
finally:
client.close()
server.close()
print("服务端已关闭")
if __name__ == "__main__":
Tcp_Server()
作用:服务端负责监听连接、接收客户端消息并回复,流程为:创建 socket → 绑定地址 → 监听 → 接受连接 → 收发数据 → 关闭连接。
改进之处 :
可以看到处理数据的部分我们将其从hander_client函数变成了更加简洁易懂的代码:

--
效果显示:
客户端开启:

服务端监听:

客户端发送信息:Hello,I Love you


服务端回复:I konw

客户端发送:bye
(主动结束对话)


异常情况:
如果之前打开过代码,想要再次打开时,遇到错误:
Tcp_Server
server.bind((ip,port))
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
这个错误的原因是:端口9990已被其他程序占用,导致当前服务端无法绑定到该端口(同一端口在同一时间只能被一个程序占用)。
解决方法(任选一种):
方法1:更换一个未被占用的端口
直接修改代码中的port变量,使用一个未被占用的端口(例如9991、9992等,确保端口号>1024):
python
# 将原来的port = 9990 改为其他端口,比如:
port = 9991 # 新端口
修改后重新运行服务端即可(客户端连接时也需要使用新端口)。
方法2:关闭占用9990端口的程序
如果必须使用9990端口,需要找到并关闭占用该端口的进程:
-
打开命令提示符(CMD)或PowerShell(以管理员身份运行);
-
输入命令查找占用
9990端口的进程ID(PID):cmdnetstat -ano | findstr :9990输出类似:
TCP 0.0.0.0:9990 0.0.0.0:0 LISTENING 1234,其中1234就是进程ID; -
关闭该进程:
- 方法1(命令行):输入
taskkill /PID 1234 /F(1234替换为实际PID,/F表示强制关闭); - 方法2(图形化):打开「任务管理器」→ 「详细信息」→ 找到对应PID的进程,右键结束任务。
- 方法1(命令行):输入
总结
我知道网络安全编程是有点无聊,但我个人觉得,正是这些无聊甚至烦躁理解代码的过程,才是你之后超越别人的重要优势;
所以,请坚持下去,期待下次再见!