侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

Python 使用指定的网卡发送HTTP请求的实例

Python  /  管理员 发布于 7年前   240

需求: 一台机器上有多个网卡, 如何访问指定的 URL 时使用指定的网卡发送数据呢?

$ curl --interface eth0 www.baidu.com # curl interface 可以指定网卡

阅读 urllib.py 的源码, 追述到 open_http C> httplib.HTTP C> httplib.HTTP._connection_class = HTTPConnection

HTTPConnection 在创建的时候会指定一个 source_address.

HTTPConnection.connect 时调用 HTTPConnection._create_connection = socket.create_connection

# 先看一下本地网卡信息$ ifconfiglo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384  options=3<RXCSUM,TXCSUM>  inet6 ::1 prefixlen 128   inet 127.0.0.1 netmask 0xff000000   inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1   nd6 options=1<PERFORMNUD>en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500  ether c8:e0:eb:17:3a:73   inet6 fe80::cae0:ebff:fe17:3a73%en0 prefixlen 64 scopeid 0x4   inet 192.168.20.2 netmask 0xffffff00 broadcast 192.168.20.255  nd6 options=1<PERFORMNUD>  media: autoselect  status: activeen1: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500  options=4<VLAN_MTU>  ether 0c:5b:8f:27:9a:64   inet6 fe80::e5b:8fff:fe27:9a64%en8 prefixlen 64 scopeid 0xa   inet 192.168.8.100 netmask 0xffffff00 broadcast 192.168.8.255  nd6 options=1<PERFORMNUD>  media: autoselect (100baseTX <full-duplex>)  status: active

可以看到en0和en1, 这两块网卡都可以访问公网. lo0是本地回环.

直接修改 socket.py 做测试.

def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,           source_address=None):  """If *source_address* is set it must be a tuple of (host, port)  for the socket to bind as a source address before making the connection.  An host of '' or port 0 tells the OS to use the default.  source_address 如果设置, 必须是传递元组 (host, port), 默认是 ("", 0)   """  host, port = address  err = None  for res in getaddrinfo(host, port, 0, SOCK_STREAM):    af, socktype, proto, canonname, sa = res    sock = None    try:      sock = socket(af, socktype, proto)      # sock.bind(("192.168.20.2", 0)) # en0      # sock.bind(("192.168.8.100", 0)) # en1      # sock.bind(("127.0.0.1", 0)) # lo0      if timeout is not _GLOBAL_DEFAULT_TIMEOUT:        sock.settimeout(timeout)      if source_address:        print "socket bind source_address: %s" % source_address        sock.bind(source_address)      sock.connect(sa)      return sock    except error as _:      err = _      if sock is not None:        sock.close()  if err is not None:    raise err  else:    raise error("getaddrinfo returns an empty list")

参考说明文档, 直接分三次绑定不通网卡的 IP 地址, 端口设置为0.

# 测试 en0$ python -c 'import urllib as u;print u.urlopen("http://ip.haschek.at").read()'.148.245.16# 测试 en1$ python -c 'import urllib as u;print u.urlopen("http://ip.haschek.at").read()'.94.115.227# 测试 lo0$ python -c 'import urllib as u;print u.urlopen("http://ip.haschek.at").read()'Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 87, in urlopen  return opener.open(url) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 213, in open  return getattr(self, name)(url) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 350, in open_http  h.endheaders(data) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1049, in endheaders  self._send_output(message_body) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 893, in _send_output  self.send(msg) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 855, in send  self.connect() File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 832, in connect  self.timeout, self.source_address) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 578, in create_connection  raise errIOError: [Errno socket error] [Errno 49] Can't assign requested address

测试通过, 说明在多网卡情况下, 创建 socket 时绑定某块网卡的 IP 就可以, 端口需要设置为0. 如果端口不设置为0, 第二次请求时, 可以看到抛异常, 端口被占用.

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 87, in urlopen  return opener.open(url) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 213, in open  return getattr(self, name)(url) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/urllib.py", line 350, in open_http  h.endheaders(data) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1049, in endheaders  self._send_output(message_body) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 893, in _send_output  self.send(msg) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 855, in send  self.connect() File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 832, in connect  self.timeout, self.source_address) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 577, in create_connection  raise errIOError: [Errno socket error] [Errno 48] Address already in use

如果是在项目中, 只需要把 socket.create_connection 这个函数的形参 source_address 设置为对应网卡的 (IP, 0) 就可以.

# test-interface_urllib.pyimport socketimport urllib, urllib2_create_socket = socket.create_connectionSOURCE_ADDRESS = ("127.0.0.1", 0)#SOURCE_ADDRESS = ("172.28.153.121", 0)#SOURCE_ADDRESS = ("172.16.30.41", 0)def create_connection(*args, **kwargs):  in_args = False  if len(args) >=3:    args = list(args)    args[2] = SOURCE_ADDRESS    args = tuple(args)    in_args = True  if not in_args:    kwargs["source_address"] = SOURCE_ADDRESS  print "args", args  print "kwargs", str(kwargs)  return _create_socket(*args, **kwargs)socket.create_connection = create_connectionprint urllib.urlopen("http://ip.haschek.at").read()

通过测试, 可以发现已经可以通过制定的网卡发送数据, 并且 IP 地址对应网卡分配的 IP.

问题, 爬虫经常使用 requests, requests 是否支持呢. 通过测试, 可以发现, requests 并没有使用 python 内置的 socket 模块.

看源码, requests 是如果创建的 socket 连接呢. 方法和查看 urllib 创建socket 的方式一样. 具体就不写了.

因为我用的是 python 2.7, 所以可以定位到 requests 使用的 socket 模块是 urllib3.utils.connection 的.

修改方法和 urllib 相差不大.

import urllib3.connection_create_socket = urllib3.connection.connection.create_connection# passurllib3.connection.connection.create_connection = create_connection# pass

运行后, 可能会抛出异常. requests.exceptions.ConnectionError: Max retries exceeded with .. Invalid argument

这个异常不是每次出现, 跟 IP 段有关系, 跳转递归层数太多导致, 只需要将 kwargs 中的 socket_options去掉即可. 127.0.0.1肯定会出异常.

import socketimport urllibimport urllib2import urllib3.connectionimport requests as req_default_create_socket = socket.create_connection_urllib3_create_socket = urllib3.connection.connection.create_connectionSOURCE_ADDRESS = ("127.0.0.1", 0)#SOURCE_ADDRESS = ("172.28.153.121", 0)#SOURCE_ADDRESS = ("172.16.30.41", 0)def default_create_connection(*args, **kwargs):  try:    del kwargs["socket_options"]  except:    pass  in_args = False  if len(args) >=3:    args = list(args)    args[2] = SOURCE_ADDRESS    args = tuple(args)    in_args = True  if not in_args:    kwargs["source_address"] = SOURCE_ADDRESS  print "args", args  print "kwargs", str(kwargs)  return _default_create_socket(*args, **kwargs)def urllib3_create_connection(*args, **kwargs):  in_args = False  if len(args) >=3:    args = list(args)    args[2] = SOURCE_ADDRESS    in_args = True    args = tuple(args)  if not in_args:    kwargs["source_address"] = SOURCE_ADDRESS  print "args", args  print "kwargs", str(kwargs)  return _urllib3_create_socket(*args, **kwargs)socket.create_connection = default_create_connection# 因为偶尔会出问题, 所以使用默认的 socket.create_connection# urllib3.connection.connection.create_connection = urllib3_create_connectionurllib3.connection.connection.create_connection = default_create_connectionprint " *** test requests: " + req.get("http://ip.haschek.at").contentprint " *** test urllib: " + urllib.urlopen("http://ip.haschek.at").read()print " *** test urllib2: " + urllib2.urlopen("http://ip.haschek.at").read()

注意: 使用 urllib3.utils.connection 好像不起作用

稍微再完善一下, 就是把根据网卡名自动获取 IP.

import subprocessdef get_all_net_devices():  sub = subprocess.Popen("ls /sys/class/net", shell=True, stdout=subprocess.PIPE)  sub.wait()  net_devices = sub.stdout.read().strip().splitlines()  # ['eth0', 'eth1', 'lo']  # 这里简单过滤一下网卡名字, 根据需求改动  net_devices = [i for i in net_devices if "ppp" in i]  return net_devicesALL_DEVICES = get_all_net_devices()def get_local_ip(device_name):  sub = subprocess.Popen("/sbin/ifconfig en0 | grep '%s ' | awk '{print $2}'" % device_name, shell=True, stdout=subprocess.PIPE)  sub.wait()  ip = sub.stdout.read().strip()  return ipdef random_local_ip():  return get_local_ip(random.choice(ALL_DEVICES))# code ...

只需要把 args[2] = SOURCE_ADDRESS 和 kwargs["source_address"] = SOURCE_ADDRESS改成 random_local_ip() 或者 get_local_ip("eth0")

至于有什么用途, 就全凭想象了.

以上这篇Python 使用指定的网卡发送HTTP请求的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。


  • 上一条:
    详解用Python为直方图绘制拟合曲线的两种方法
    下一条:
    Python turtle绘画象棋棋盘
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客