14

考虑以下 R 脚本:

con <- socketConnection(host = "localhost", port = 8, server = TRUE, blocking = TRUE, open = "a+b")
close(con = con)

将这些行保存为 .R 文件,然后从命令行运行它会产生(在 Windows 上)防火墙警告。至少,如果“高级安全 Windows 防火墙”下没有 R 规则,第一次出现之后。我被告知在 Mac 上也会发生同样的情况,但我自己无法验证这一点。如何更改它以允许本地主机环回,但避免弹出?

背景:我为使用并行处理的人(在一台本地机器上)编写了一些代码。然而,这个警告突然出现在他们的屏幕上,他们产生了怀疑。愚蠢的是,即使人们单击否或忽略弹出窗口,并行处理似乎仍然有效。我认为这是可以修改此代码以不提供此弹出窗口并仍然起作用的标志。

我在其他语言(123)中看到了非常相似的问题,我想知道是否可以对 R 做同样的事情。

Windows 10 防火墙首次提示示例:

在此处输入图像描述

4

4 回答 4

12

我的感觉是,解决这个问题的最简单方法是在应用程序安装过程中添加防火墙规则。

  • 您可以使用netsh添加规则(需要管理员权限)以编程方式启用防火墙访问。

我在下面提供了一个示例脚本,希望这有助于为您指明正确的方向。

示例防火墙配置脚本

netsh advfirewall firewall add rule name="RScript" action=allow program="C:\Program Files\Microsoft\R Client\R_SERVER\bin\x64\Rscript.exe" enable=yes Localip="127.0.0.1" localport="9999" protocol=tcp interfacetype=any profile=private dir=in

命令输出:

PS <hidden>\dev\stackoverflow\47353848> netsh advfirewall firewall add rule name="RScript" action=allow program="C
:\Program Files\Microsoft\R Client\R_SERVER\bin\x64\Rscript.exe" enable=yes Localip="127.0.0.1" localport="9999" protoco
l=tcp interfacetype=any profile=private dir=in
Ok.

添加了防火墙规则

在此处输入图像描述


假设您使用 RScript 运行 R 文件,上述netsh脚本将使 RScript 应用程序能够使用专用网络上的 TCP 访问端口 9999 上的环回地址 127.0.0.1。从此时起,您不应收到防火墙提示。

没有防火墙提示的命令行

c:\<hidden>\dev\stackoverflow\47353848> Rscript.exe .\server.R
Listening...

为什么要这样做?好吧,据我所知base::socketConnection,即使使用环回连接器,也无法在不触发 Windows Defender 防火墙提示的情况下在 Windows 上使用 R。有趣的是,如果您使用 Java,则不会收到提示。我查看了这两种实现,但我无法确定为什么不这样做。

测试服务器代码:

server <- function() {
  while (TRUE) {
    writeLines("Listening...")
    con <- socketConnection(host = "loopback",
                            port = 9999,
                            server = TRUE,
                            blocking = TRUE,
                            timeout = 0,
                            open = "r+")
    data <- readLines(con, 1)
    print(data)
    response <- toupper(data)
    writeLines(response, con)
    close(con)
  }
}
server()

测试客户端代码

client <- function() {
  while (TRUE) {
    con <- socketConnection(host = "loopback",
                            port = 9999,
                            server = FALSE,
                            blocking = TRUE,
                            open = "r+")
    f <- file("stdin")
    open(f)
    print("Enter text to be upper-cased, q to quit")
    sendme <- readLines(f, n = 1)
    if (tolower(sendme) == "q") {
      break
    }

    write_resp <- writeLines(sendme, con)
    server_resp <- readLines(con, 1)
    print(paste("Your upper cased text: ", server_resp))
    close(con)
  }
}
client()
于 2017-11-26T00:20:23.470 回答
5

(关于我对防火墙规则的看法,见最后)

该功能似乎根本不存在。

socket在 C 中,您使用,bind和调用创建服务器套接字,并listen通过调用获取传入连接accept。src\modules\internet\sock.c 是套接字处理程序代码,它有两个打开套接字的函数,Sock_connect打开和连接套接字,所以这是客户端的,int Sock_open(Sock_port_t port, Sock_error_t perr)是打开服务器套接字的(和实际的接受电话在Sock_listen)。问题是这Sock_open只有一个port参数,并且主机/接口是硬编码的:

/* open a socket for listening */
int Sock_open(Sock_port_t port, Sock_error_t perr)
{
    int sock;
    struct sockaddr_in server;

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    return Sock_error(perr, errno, 0);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons((short)port);

    if ((bind(sock, (struct sockaddr *)&server, sizeof(server)) < 0) ||
        (listen(sock, MAXBACKLOG) < 0)) {
        close(sock);
        return Sock_error(perr, errno, 0);
    }
    return sock;
}

它绑定并侦听 INADDR_ANY,这意味着您 PC 的所有接口(不仅仅是环回),它肯定会触发防火墙。

该函数是从相邻的 Rsock.c 调用的,仍然使用一个端口参数,并且在 sockconn.c 中丢失其他所有内容的地方似乎要早一步:

static Rboolean sock_open(Rconnection con)
{
    Rsockconn this = (Rsockconn)con->private;
    int sock, sock1, mlen;
    int timeout = this->timeout;
    char buf[256];

    if(timeout == NA_INTEGER || timeout <= 0) timeout = 60;
    this->pend = this->pstart = this->inbuf;

    if(this->server) {
        sock1 = R_SockOpen(this->port);

最后一行是 RSockconn 的主机部分被忽略的地方,尽管它包含这样的字段:

/* used in internet module */
typedef struct sockconn {
    int port;
    int server;
    int fd;
    int timeout;
    char *host;
    char inbuf[4096], *pstart, *pend;
} *Rsockconn;

(这是在外部定义的,在 src\include\Rconnections.h 中)

不幸的是,这不能解决您的问题,这就是您拥有它的原因。您可以考虑向 R 的开发人员提出错误报告。评论表明他们从远古时代就获得了网络代码,当时防火墙和互联网安全不像现在那样受到关注:

/* Simple sockets interface derived from the sockets UICI
   implementation in Appendix B of Practical UNIX Programming,
   K. A. Robbins and S. Robbins, Prentice Hall, 1996. */

这很好,就在 21 年前。


本来我是不想从别人那里偷netsh的东西,但是我想你可能会得到错误的建议。实际上你不应该允许任何东西,而是阻止一切:

netsh advfirewall firewall add rule name="Rtest" dir=in action=block program="<location and name of your executable>"

就是这样。问题是环回接口根本没有防火墙(所以到 127.0.0.1 的连接总是有效的——我也测试了它,只是为了安全起见),你不希望其他人访问你的程序。我在其他答案中看到了“允许”,而您不希望这样。根据其他用途,您可能必须使用“localport=8”和/或“protocol=tcp”来限制规则,但块部分是肯定的。

于 2017-11-28T20:57:46.410 回答
5

你基本上被迫使用 Windows 防火墙,因为它是 Windows 自带的。因此,也许在您的 .R 文件中包含一个 .bat 文件,该文件会创建一个异常并告诉用户运行它?或者也许用 IExpress 制作一个 .exe 安装程序?这可能会奏效。

不过,我会推荐 .exe 安装程序路线,因为 .bat 文件似乎也有点可疑,因为非精通技术的用户在要求管理员权限时会哭泣。如果您希望使用 .bat 文件路由,netsh 命令可以为任何程序创建防火墙例外。

这个通过使用 dir=out 开关接受传出连接,并通过 action=allow 开关启用异常

netsh advfirewall firewall add rule name="PROGRAM_NAME" dir=out action=allow program="C:\PROGRAMPATH" enable=yes

这个通过使用 dir=in 开关接受传入连接,并通过 action=allow 开关启用异常

netsh advfirewall firewall add rule name="PROGRAM_NAME" dir=out action=allow program="C:\PROGRAMPATH" enable=yes

如何向 Windows 防火墙添加规则 - DigitalCitizen

在线技术提示 - 调整 Windows 10 防火墙规则和设置

于 2017-11-28T21:58:24.343 回答
4

使用 PSOCK 集群的替代方法是使用callr在后台运行多个 R 会话。future.callr(*) 为未来框架提供并行后端(免责声明:我是作者)。它将允许您在不通过套接字(因此没有防火墙)的情况下并行化包括 Windows 在内的所有操作系统。例子:

library("future")
plan(future.callr::callr)
y <- future_lapply(x, FUN = my_fcn_in_parallel)

它也适用于foreach框架的doFuture 。例子:

library("doFuture")
plan(future.callr::callr)
registerDoFuture()
y <- foreach(i in seq_along(x)) %dopar% my_fcn_in_parallel(x[[i]])

仅供参考,对于 PSOCK 集群,请使用plan(multisession).

(*)future.callr 在 2018-02-13 上位于 CRAN 上,只要它所依赖的开发者版本的 callr 提交给 CRAN,就会立即提交给 CRAN。

于 2017-11-28T23:49:39.447 回答