TCP代理的python实现(包括客户端/服务器/TCP代理三部分代码)

最近一直跟随《Python黑帽子》一书学习网络编程,在实现TCP代理功能的这一节,书中是直接使用FTP客户端和服务器进行测试,为了更深刻的理解通信过程,我们可以自己编写一个socket服务端和客户端来进行测试,同时需要对书中的代码进行一些改动,以适应我们自己编写的服务端和客户端。

1.TCP代理

用socket方式实现TCP代理,代理方作为客户端和服务端之间通信的桥梁,连接建立过程大致如下:

1.服务端开始监听服务端的本机端口,等待来自于代理方的socket连接;

2.代理方监听本机端口,等待来自客户端的socket连接;

3.客户端创建socket与代理方进行连接;

4.代理方收到来自客户端的链接后,创建一个socket与服务端进行连接;

5.服务端收到来自代理端的连接;

链接建立后,通信过程如下:

1.客户端发送请求给代理方;

2.代理方对请求进行处理,将处理后的请求发送给服务端;

3.服务端接收请求后,根据请求信息发送回复给代理方;

4.代理方将收到的回复信息进行处理,将处理后的信息发送给客户端;

2.本文TCP代理的具体功能

服务器发送字符串,客户端接收到相同的字符串;

客户端无需发送请求信息,仅接收从服务器发送过来的信息;

当没有信息传输时,不关闭连接,而是等待服务端发送新的字符串;

3.具体实现

3.1 代理方代码(proxy.py),点击获取源代码,注意下面函数的顺序是为了便于理解,如果使用下面的代码进行实验,需自行调整函数定义顺序
#!/usr/bin/python
import socket
import sys
import threading
#主函数:
def main():
       #简单地判断命令格式,如果参数个数不是5个则提示正确的格式
       if len(sys.argv[1:]) !=5:
              print "Usage: ./proxy.py [localhost] [localport] [remotehost] [remoteport] [receivefirst]"
              exit(0)
       #为监听客户端创建的socket
       local_host=sys.argv[1]
       local_port=int(sys.argv[2])
       #为连接服务端创建的socket
       remote_host=sys.argv[3]
       remote_port=int(sys.argv[4])
       #连接建立后是否由服务端先向客户端发送信息,True代表是
       if sys.argv[5] =="True":
              receive_first=True
       else:
              receive_first=False
       #开始代理过程
       server_loop(local_host,local_port,remote_host,remote_port,receive_first)

#定义上面的server_loop函数
def server_loop(local_host,local_port,remote_host,remote_port,receive_first):
       #创建一个TCP socket以监听客户端
       server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)    
       try:
              server.bind((local_host,local_port))
       except:
              print "Failed to listen on %s: %d"%(local_host,local_port)
              assert False,"Please try other socket"
       server.listen(5)
       #开始监听后,等待来自客户端的连接
       while True:
              print "waiting for local client connection"
              client_socket, addr= server.accept()
              print "[*]Received connection from %s:%d"%(addr[0],addr[1])
              #收到来自客户端的链接后,就开启一个代理线程
              proxy_thread=threading.Thread(target=proxy_handler,args=(client_socket,remote_host,remote_port,receive_first))
              proxy_thread.start() 

#定义代理线程
def proxy_handler(client_socket,remote_host,remote_port,receive_first):
       #创建一个socket以连接服务端
       remote_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
       remote_socket.connect((remote_host,remote_port))
       #如果需要服务端先向客户端发送信息
       if receive_first:
              remote_buffer=receive_from(remote_socket)
              print "[<==]Receive %d bytes from remote host receive first"%len(remote_buffer)
              #下面两行是对收到信息的处理,得到有用的信息(做十六进制处理和自定义的处理,如果不需要,可以省略)
              hexdump(remote_buffer)
              remote_buffer=response_handler(remote_buffer)
              #如果处理后仍有有用的信息,则发送给客户端
              if len(remote_buffer):
                     client_socket.send(remote_buffer)
                     print "[==>]Sending %d bytes to local hosts"%len(remote_buffer)
       while True:
              #从客户端接收请求信息,由于本文中的例子不需要客户端发送请求,所以注释掉这一段
'''
              client_buffer=receive_from(client.socket)
              print "[<==]Receive %d bytes from localhost"%len(local_buffer)
              #下面两行是对收到信息的处理,得到有用的信息(做十六进制处理和自定义的处理,如果不需要,可以省略)
              hexdump(client_buffer)
              remote_buffer=request_handler(client_buffer)
              #如果处理后仍有有用的请求信息,则发送给服务端
              if len(client_buffer):
                     remote_socket.send(client_buffer)
                     print "[==>]Sending %d to remote"%len(local_buffer)
'''
              remote_buffer=receive_from(remote_socket)
              print "[<==]Receive %d bytes from remote host receive first"%len(remote_buffer)
              #下面两行是对收到信息的处理,得到有用的信息(做十六进制处理和自定义的处理,如果不需要,可以省略)
              hexdump(remote_buffer)
              remote_buffer=response_handler(remote_buffer)
              #如果处理后仍有有用的信息,则发送给客户端
              if len(remote_buffer):
                     client_socket.send(remote_buffer)
                     print "[==>]Sending %d bytes to local hosts"%len(remote_buffer)
              #如果从服务端和客户端都不再收到信息了,就关闭连接(由于本文的例子需要等待服务端继续发送信息,所以不关闭连接,注释掉这一段)
'''
              if not (len(local_buffer) or len(remote_buffer)):
                     client_socket.close()
                     remote_socket.close()
                     print "[*]No more data. Closing connections"
                     break
''' 
#上面的代码中还有一些小的方法没有定义:
#由于是TCP代理,所以接收的信息块可能比较大,所以不直接用.recv()方法,而是写一个receive_from方法来获取发送的全部信息
def receive_from(connection):
       buffer=""
       #设置一个超时时间,如果超过2s没有接收到消息则进入异常处理(必须设置,如果不设置超时时间,则会阻塞,直到接收到消息为止,在本例中也许不会出现问题,但是如果客户端也需要发送请求时就会出现问题)
       connection.settimeout(2)
       try:
              #将收到的消息拼接成buffer
              while True:
                     data=connection.recv(4096)
                     if not data:
                            break
                     buffer+=data
       #如果2s都没收到消息,就返回空的buffer
       except:
              pass
       return buffer   
 
#对接收信息进行处理的函数,如果不需要特殊处理,直接返回收到的信息即可
def request_handler(buffer):
       #此处添加具体的处理代码
       return buffer
def response_handler(buffer):
       #此处添加具体的处理代码
       return buffer

#十六进制处理函数,无需深究,如果不需十六进制转换,可以省略
def hexdump(src, length=16):
       result = []
       digits = 4 if isinstance(src, unicode) else 2
       for i in xrange(0, len(src), length):
              s = src[i:i + length]
              hexa = b''.join(["%0*X" % (digits, ord(x)) for x in s])
              text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
              result.append(b"%04X %-*s %s" % (i, length * (digits + 1), hexa, text))
       print(b'\n'.join(result))

#运行主函数
main()


3.2 服务端测试代码(echo_socket_server.py),点击获取源代码
#!/usr/bin/python
import socket
import sys
import threading

#初始化请求队列,以便同时对多个客户端进行应答
request_queue=[]
#使用提示
def usage():
       print "./echo_socket_server.py [listen_ip] [listen_port]"

#对请求列表进行操作,对请求队列中的socket连接进行处理
def client_handler(request_queue):
       #如果有已建立的socket连接,则提示用户输入想要发送的信息内容
       if request_queue:
              msg=raw_input("message:")
       #如果用户输入的是"exit",就关闭所有的连接,否则就向所有已连接的客户端发送输入的内容
       if msg=="exit":
              for client_socket in request_queue:
                     client_socket.close()
                     break
       else:
              #由于本例中只是简单地发送消息,所以直接用send方法,如果是比较耗时的操作,可以创建对每个request创建一个线程
              for client_socket in request_queue:
                     client_socket.send(msg)
                     print "sending %s to proxy"%msg

#每1秒检测一次,是否有新的来自客户端的连接,如果有,就放进请求列表中去,然后对请求列表进行操作
def connection(server):
       global request_queue
       while True:
              server.settimeout(1)
              try:
                     client_socket,addr=server.accept()
                     request_queue.append(client_socket)
                     print "[*]Connection received from %s:%d"%(addr[0],addr[1])
              except:
                     pass
              client_handler(request_queue)
#主函数
def main():
       global request_queue
       if "help" in sys.argv:
              usage()
       else:
              server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
              server_ip=host=sys.argv[1]
              server_port = int(sys.argv[2])
              server.bind((server_host,server_port))
              server.listen(5)
       #开启一个线程,检测连接并对现有连接进行操作
       connection_thread=threading.Thread(target=connection,args=(server,))
       connection_thread.start()
main()



3.3 客户端测试代码(异常简单,无需注释),点击获取源代码
#!/usr/bin/python
import sys
import socket
def receive_from(connection):
       buffer="" 
       connection.settimeout(0.01)
       try:
              while True:
                     data=connection.recv(4096)
                     if not data:
                     break
                     buffer+=data
       except:
              #print "no data received !!!!"
              pass
       return buffer

def main():
       server_ip=sys.argv[1]
       server_port=int(sys.argv[2])
       client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
       client.connect((server_ip,server_port))

       while True:
              data=receive_from(client)
              if data:
                     print data
main()


4. 测试方法

服务器主机ip地址为192.168.0.104;

客户端主机ip地址为192.168.0.102;

代理方主机ip地址为192.168.0.107;

1.登录服务器主机,在echo_socket_server,py 文件同级目录下运行:(监听本地所有ip的8000端口,等待代理方连接)

./echo_socket_server.py 0.0.0.0 8000


2.登录代理方主机,在proxy.py文件同级目录下运行:(监听本地所有ip的9000端口等待客户端连接,连接服务器的8000端口)

./proxy.py 0.0.0.0 9000 192.168.0.104 8000 True


3.登录客户端主机,在echo_socket_client.py文件同级目录下运行:(连接代理方的9000端口)

./echo_socket_client.py 192.168.0.107 9000


此时可以看到客户端收到了来自服务器发送的"hello"消息,测试成功。

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
TCP (传输控制协议) 是一种面向连接的协议,用于在网络上可靠地传输数据。在 Python 中,我们可以使用 socket 模块来实现 TCP 客户端服务器。 下面是一个简单的例子: ## TCP 服务器 ```python import socket # 创建一个 TCP 服务器套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 绑定地址和口 server_socket.bind(('127.0.0.1', 8888)) # 开始监听客户端连接 server_socket.listen(1) print('服务器启动,等待连接...') # 接受客户端连接 client_socket, client_address = server_socket.accept() print('客户端已连接:', client_address) # 接收客户端发送的数据 data = client_socket.recv(1024) print('收到数据:', data.decode()) # 发送响应数据给客户端 response = 'Hello, client!' client_socket.send(response.encode()) # 关闭连接 client_socket.close() server_socket.close() ``` ## TCP 客户端 ```python import socket # 创建一个 TCP 客户端套接字 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 连接服务器 client_socket.connect(('127.0.0.1', 8888)) print('连接服务器成功!') # 发送数据给服务器 message = 'Hello, server!' client_socket.send(message.encode()) # 接收服务器响应数据 response = client_socket.recv(1024) print('收到服务器响应:', response.decode()) # 关闭连接 client_socket.close() ``` 在运行服务器客户端代码之前,请确保您已经安装了 Pythonsocket 模块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eponia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值