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

而通常来说双方通信的基础条件是需要有客户端以及服务端,双方进行通信。所以话不多说,
- 我们先开始实现TCP客户端的代码:
import sockettarget_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服务端
import socket
import threadingip = '0.0.0.0'
port = 9990def 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客户端:
import socketip = '127.0.0.1'
port = 9990def Tcp_Client():# 还是老样子,像创建socket对象client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)client.connect((ip,port))print(f'已成功连接到服务端:{ip}:{port}')try:while(True):# 正常情况——发送信息到服务端,并将信息encodesend_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服务端:
import socket# 0.0.0.0表示任何人都可访问TCP服务端
ip = "0.0.0.0"
port = 9990def Tcp_Server():server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 绑定IP和portserver.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# 解析datadata_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):
# 将原来的port = 9990 改为其他端口,比如:
port = 9991 # 新端口
修改后重新运行服务端即可(客户端连接时也需要使用新端口)。
方法2:关闭占用9990端口的程序
如果必须使用9990端口,需要找到并关闭占用该端口的进程:
- 打开命令提示符(CMD)或PowerShell(以管理员身份运行);
- 输入命令查找占用
9990端口的进程ID(PID):
输出类似:netstat -ano | findstr :9990TCP 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(命令行):输入
总结
我知道网络安全编程是有点无聊,但我个人觉得,正是这些无聊甚至烦躁理解代码的过程,才是你之后超越别人的重要优势;
所以,请坚持下去,期待下次再见!
