网络安全编程——TCP客户端以及服务端Python实现

以网络安全工具为脉络,分类解析攻防、检测、防护等各类工具的原理与实操,构建工具应用体系,助力读者掌握工具使用技巧;

今天给大家带来的是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()关闭连接 (不再进行后续的通信,这也是为什么只能进行一次通话的重要原因

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变量,使用一个未被占用的端口(例如99919992等,确保端口号>1024):

python 复制代码
# 将原来的port = 9990 改为其他端口,比如:
port = 9991  # 新端口

修改后重新运行服务端即可(客户端连接时也需要使用新端口)。

方法2:关闭占用9990端口的程序

如果必须使用9990端口,需要找到并关闭占用该端口的进程:

  1. 打开命令提示符(CMD)或PowerShell(以管理员身份运行);

  2. 输入命令查找占用9990端口的进程ID(PID):

    cmd 复制代码
    netstat -ano | findstr :9990

    输出类似:TCP 0.0.0.0:9990 0.0.0.0:0 LISTENING 1234,其中1234就是进程ID;

  3. 关闭该进程:

    • 方法1(命令行):输入 taskkill /PID 1234 /F1234替换为实际PID,/F表示强制关闭);
    • 方法2(图形化):打开「任务管理器」→ 「详细信息」→ 找到对应PID的进程,右键结束任务。

总结

我知道网络安全编程是有点无聊,但我个人觉得,正是这些无聊甚至烦躁理解代码的过程,才是你之后超越别人的重要优势;

所以,请坚持下去,期待下次再见!

相关推荐
AntBlack4 小时前
不当韭菜 : 好像真有点效果 ,想藏起来自己用了
前端·后端·python
无聊的小坏坏4 小时前
从零开始:C++ TCP 服务器实战教程
服务器·c++·tcp/ip
darkfive4 小时前
构建大模型安全自动化测试框架:从手工POC到AI对抗AI的递归Fuzz实践
人工智能·安全·ai·自动化
百锦再5 小时前
破茧成蝶:全方位解析Java学习难点与征服之路
java·python·学习·struts·kafka·maven·intellij-idea
可触的未来,发芽的智生5 小时前
触摸未来2025-10-25:蓝图绘制
javascript·python·神经网络·程序人生·自然语言处理
新手村领路人5 小时前
python opencv gpu加速 cmake msvc cuda编译问题和设置
开发语言·python·opencv
白帽子黑客罗哥5 小时前
Redis实战深度剖析:高并发场景下的架构设计与性能优化
redis·网络安全·性能优化·高并发·分布式锁·秒杀系统·缓存架构
TG_yunshuguoji6 小时前
亚马逊云渠道商:本地SSD缓存如何保障数据安全?
运维·服务器·安全·云计算·aws
暴风鱼划水6 小时前
卡码网语言基础课(Python) | 19.洗盘子
python·算法