首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >通过特定网络接口发送http请求

通过特定网络接口发送http请求
EN

Stack Overflow用户
提问于 2018-02-27 04:22:29
回答 4查看 9.2K关注 0票数 15

我有两个网络接口(wifi和以太网),都可以上网。假设我的接口是eth (以太网)和wlp2 (wifi)。我需要通过eth接口和其他通过wpl2的具体要求。

类似于:

代码语言:javascript
复制
// Through "eth"
request.post(url="http://myapi.com/store_ip", iface="eth")
// Through "wlp2" 
request.post(url="http://myapi.com/log", iface="wlp2")

我使用的是requests,但如果无法用requests实现,我也可以使用pycurlurllib

How to specify source interface in python requests module?引用了Requests, bind to an ip,但它不起作用。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2018-02-27 05:39:37

我找到了一种使用pycurl的方法。这就像一个护身符。

代码语言:javascript
复制
import pycurl
from io import BytesIO
import json


def curl_post(url, data, iface=None):
    c = pycurl.Curl()
    buffer = BytesIO()
    c.setopt(pycurl.URL, url)
    c.setopt(pycurl.POST, True)
    c.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json'])
    c.setopt(pycurl.TIMEOUT, 10)
    c.setopt(pycurl.WRITEFUNCTION, buffer.write)
    c.setopt(pycurl.POSTFIELDS, data)
    if iface:
        c.setopt(pycurl.INTERFACE, iface)
    c.perform()

    # Json response
    resp = buffer.getvalue().decode('UTF-8')

    #  Check response is a JSON if not there was an error
    try:
        resp = json.loads(resp)
    except json.decoder.JSONDecodeError:
        pass

    buffer.close()
    c.close()
    return resp


if __name__ == '__main__':
    dat = {"id": 52, "configuration": [{"eno1": {"address": "192.168.1.1"}}]}
    res = curl_post("http://127.0.0.1:5000/network_configuration/", json.dumps(dat), "wlp2")
    print(res)

我把这个问题留在一边,希望有人能用requests给出答案。

票数 7
EN

Stack Overflow用户

发布于 2020-05-04 04:19:36

这是一个不需要修补任何东西的请求库的解决方案。

此函数将创建绑定到给定IP地址的会话。由您决定所需网络接口的IP地址。

经测试可与requests==2.23.0配合使用。

代码语言:javascript
复制
import requests


def session_for_src_addr(addr: str) -> requests.Session:
    """
    Create `Session` which will bind to the specified local address
    rather than auto-selecting it.
    """
    session = requests.Session()
    for prefix in ('http://', 'https://'):
        session.get_adapter(prefix).init_poolmanager(
            # those are default values from HTTPAdapter's constructor
            connections=requests.adapters.DEFAULT_POOLSIZE,
            maxsize=requests.adapters.DEFAULT_POOLSIZE,
            # This should be a tuple of (address, port). Port 0 means auto-selection.
            source_address=(addr, 0),
        )

    return session


# usage example:
s = session_for_src_addr('192.168.1.12')
s.get('https://httpbin.org/ip')

不过要注意,这种方法与curl--interface选项相同,在某些情况下不会有帮助。根据你的路由配置,即使你绑定到特定的IP地址,请求也会通过其他接口。所以如果这个答案对你不起作用,那么首先检查curl http://httpbin.org/ip --interface myinterface是否会像预期的那样工作。

票数 9
EN

Stack Overflow用户

发布于 2021-08-24 11:31:44

如果你想在Linux上这样做,你可以对setsockopt使用SO_BINDTODEVICE标志(更多细节请查看man 7 socket )。事实上,如果你在linux上使用--interface option,它就是使用by curl的工具。但请记住,SO_BINDTODEVICE需要根权限(CAP_NET_RAW,尽管有some attempts可以更改这一点),如果SO_BINDTODEVICE失败,curl会退回到常规的bind技巧。

下面是失败时的curl代码示例:

代码语言:javascript
复制
strace -f -e setsockopt,bind curl --interface eth2 https://ifconfig.me/
strace: Process 18208 attached
[pid 18208] +++ exited with 0 +++
setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [60], 4) = 0
setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0
setsockopt(3, SOL_SOCKET, SO_BINDTODEVICE, "eth2\0", 5) = -1 EPERM (Operation not permitted)
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("192.168.8.1")}, 16) = 0 # curl fallbacks to regular bind
127.0.0.1+++ exited with 0 +++

另外,我想说的是,使用常规bind并不总是保证流量会通过指定的接口(@MarSoft answer使用普通的bind)。在linux上,只有SO_BINDTODEVICE可以保证流量通过指定的设备。

这里有一个如何在requestsrequests-toolbelt中使用SO_BINDTODEVICE的例子(正如我所说的,它需要CAP_NET_RAW permissions)。

代码语言:javascript
复制
import socket
import requests
from requests_toolbelt.adapters.socket_options import SocketOptionsAdapter


session = requests.Session()
# set interface here
options = [(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, b"eth0")]
for prefix in ('http://', 'https://'):
    session.mount(prefix, SocketOptionsAdapter(socket_options=options))


print(session.get("https://ifconfig.me/").text)

或者,如果您不想使用requests-toolbelt,您可以自己实现适配器类:

代码语言:javascript
复制
import socket
import requests
from requests import adapters
from urllib3.poolmanager import PoolManager


class InterfaceAdapter(adapters.HTTPAdapter):

    def __init__(self, **kwargs):
        self.iface = kwargs.pop('iface', None)
        super(InterfaceAdapter, self).__init__(**kwargs)

    def _socket_options(self):
        if self.iface is None:
            return []
        else:
            return [(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, self.iface)]

    def init_poolmanager(self, connections, maxsize, block=False):
        self.poolmanager = PoolManager(
            num_pools=connections,
            maxsize=maxsize,
            block=block,
            socket_options=self._socket_options()
        )


session = requests.Session()
for prefix in ('http://', 'https://'):
    session.mount(prefix, InterfaceAdapter(iface=b'eth0'))


print(session.get("https://ifconfig.me/").text)
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48996494

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档