I'd like to know if there's any way to establish a P2P connection between two browsers using socket.io-client (but I'm willing to use anything else that may do the trick).
我想知道是否有任何方法可以使用Socket.IO-Client在两个浏览器之间建立P2P连接(但我愿意使用任何其他方法)。
Both browsers are currently connected to a node.js app serving HTTP requests with Express, which stores both clients's IP addresses (and ports when running locally). What I'd like to do is add a third connection that links both clients (let's call them A and B) directly, so that messages/data will go straight from one client to another, without transiting through the node.js server.
这两个浏览器目前都连接到一个node.js应用程序,该应用程序通过Express服务HTTP请求,该应用程序存储两个客户端的IP地址(以及在本地运行时的端口)。我想要做的是添加第三个连接,它直接链接两个客户端(让我们称它们为A和B),这样消息/数据将直接从一个客户端传递到另一个客户端,而不需要通过node.js服务器。
Is that feasible? If so, how?
这可行吗?如果是这样的话,是如何做到的呢?
So far, I've tried connecting the two clients (let's call them A and B) with the following code:
到目前为止,我已经尝试使用以下代码连接这两个客户端(让我们称它们为A和B):
Client A:
客户端A:
A_to_server_socket = io();
A_to_server_socket.on('p2p', function(address_B){
A_to_B_socket = io(address_B); // Instantiates a new socket
A_to_B_socket.on('connect', function() {
console.log('Connected!');
});
});
I'm not sure about the code for client B. However I've tried:
我不确定客户B的代码。不过,我尝试过:
repeat the above code for B, using B's own address (to override the default of connecting to the server)
repeat the above code for B, this time using A's address
having B_to_server_socket listen for a new connect event
However regardless of B's code, when running A's code I'm confronted with a "Cross-Origin Request blocked" error on Firefox, or "Failed to load resource: net::ERR_CONNECTION_REFUSED" followed by "net::ERR_CONNECTION_REFUSED" on Chrome.
然而,无论B的代码是什么,当运行A的代码时,我在Firefox上都会遇到“跨域请求被阻止”的错误,或者在Chrome上会遇到“无法加载资源:NET::ERR_CONNECTION_REJECTED”,然后是“Net::ERR_CONNECTION_REJECTED”。
Any hints towards a solution, or insights for better understanding the problem and how sockets work would be most welcome.
对于解决方案的任何提示,或者更好地理解问题和套接字如何工作的见解,都将是最受欢迎的。
更多回答
The client-side socket.io library doesn't listen for incoming connections and, if it did, incoming connections would be blocked by whatever firewall the browser is behind. The whole principle of WebSockets is that the client connects outbound to the server and that's how you deal with the firewall. The server (or another client) can't connect to the a client.
客户端Socket.IO库不侦听传入连接,如果侦听,则传入连接将被浏览器所在的任何防火墙阻止。WebSockets的整个原则是客户端向外连接到服务器,这就是您处理防火墙的方式。服务器(或其他客户端)无法连接到客户端。
Oh, and I should add that browsers don't allow for listening to incoming connections so this isn't just a socket.io limitation.
哦,我应该补充一点,浏览器不允许监听传入的连接,所以这不仅仅是Socket.IO的限制。
OK. But let's say I don't have a firewall, like when I'm running the app locally. There's really no way to "force" a connection between two client sockets by telling them to listen to one another's IP+PORT?
好的。但假设我没有防火墙,就像我在本地运行应用程序时一样。真的没有办法通过告诉两个客户端套接字侦听彼此的IP+端口来“强制”它们之间的连接吗?
No, there is not because the browser will not listen for a new connection. It simply doesn't do that for webSockets (for security reasons). It only makes outbound connections. One side has to listen for new a connection in order to directly connect two endpoints, so if no browser is ever listening, then you can't make a direct webSocket connection between two browsers - that's just how TCP works. You can connect each browser to an intermediary server and exchange data through the server.
不,这并不是因为浏览器不会侦听新连接。它只是不能为WebSockets做这件事(出于安全原因)。它只建立出站连接。为了直接连接两个端点,一端必须侦听新的连接,因此如果没有浏览器在侦听,那么您就不能在两个浏览器之间建立直接的WebSocket连接--这就是TCP的工作方式。您可以将每个浏览器连接到中间服务器,并通过该服务器交换数据。
Also, there wouldn't be any way around this limitation, as in having client A connect to the server, who then passes it (= socket parameters) down to B, or overwriting a client's packets's 'origin' field with the server's IP's address? I don't mind getting my hands dirty if I can make it work :)
此外,没有任何方法可以绕过这一限制,比如让客户端A连接到服务器,然后服务器将其向下传递(=套接字参数)到B,或者使用服务器的IP地址覆盖客户端的数据包的“源”字段?我不介意弄脏我的手,如果我能做到的话:)
I'll try to summarize my comments into an answer.
我会试着把我的评论总结成一个答案。
In TCP, a connection is made when one endpoint A connects to another endpoint B. To connect to endpoint B, that host must be "listening" for incoming connections originating from other hosts. In a typical web request, the browser establishes a TCP connection to the web server and this works because the web server is listening for incoming requests on a specific port and when one of those requests comes in, it accepts the incoming request to establish an active TCP connection. So, you must have one side initiating the request and another side listening for the request.
在TCP中,当一个端点A连接到另一个端点B时,就会建立连接。要连接到端点B,该主机必须“侦听”来自其他主机的传入连接。在典型的Web请求中,浏览器建立到Web服务器的TCP连接,这是因为Web服务器正在侦听特定端口上的传入请求,当其中一个请求传入时,它会接受传入请求以建立活动的TCP连接。因此,您必须让一端发起请求,另一端监听请求。
For various security reasons, browsers themselves don't "listen" for incoming connections so you can't connect directly to them. They only allow you to connect outbound to some other listening agent on the internet. So, if a browser never listens for an incoming webSocket connection, then you can't establish a true peer-to-peer (e.g. browser-to-browser) webSocket connection.
出于各种安全原因,浏览器本身不会“侦听”传入的连接,因此您无法直接连接到它们。它们只允许您出站连接到Internet上的其他侦听代理。因此,如果浏览器从不侦听传入的webSocket连接,那么您就无法建立真正的对等(例如浏览器到浏览器)webSocket连接。
Furthermore, TCP is designed so that you can't have two browsers both connect to a common server and then somehow have that server connect up their pipelines such that the two browser are now just wired directly to each other. TCP just doesn't work that way. It is possible to have an agent in the middle forwarding packets from one client to another via a separate connection to each (that's how Chat applications generally work), but the agent in the middle can't simply plug the two TCP connections together such that the packets go directly from one client to the other (and no longer go through the server) as a fireman would connect two firehoses. TCP just doens't work that way. It might be possible to have some complicated scheme that involved rewriting packet headers to a packet sent from endPoint A was forwarded to endPoint B and looked like it came from the server instead, but that still involves the server as the middleman or proxy.
此外,根据设计,您不能让两个浏览器都连接到一台公共服务器,然后让该服务器以某种方式连接到它们的管道上,这样两个浏览器现在就直接连接到彼此。Tcp根本不是这样工作的。中间的代理可以通过单独的连接将信息包从一个客户端转发到另一个客户端(这是聊天应用程序通常的工作方式),但中间的代理不能简单地将两个TCP连接插入在一起,从而使信息包直接从一个客户端到达另一个客户端(而不再通过服务器),就像消防员连接两个消防管一样。Tcp不是这样工作的。可能有一些复杂的方案,该方案涉及将分组报头重写为从端点A发送的分组,该分组被转发到端点B,并且看起来像是来自服务器,但这仍然涉及作为中间人或代理的服务器。
The usual way to solve this problem is to just let each browser connect to a common server and have the server act as the middleman or traffic cop, forwarding packets from one browser to another.
解决这个问题的通常方法是让每个浏览器连接到一个公共服务器,并让服务器充当中间人或交通警察,将数据包从一个浏览器转发到另一个浏览器。
Existing P2P applications (outside of browsers) work by having each client actually listen for incoming connections (act like a server) so that another client can connect directly to them. P2P itself is more complicated than this because one needs to have a means of discovering an IP address to connect to (since clients typically aren't in DNS) and often there are firewalls in the way that need some cooperation between the two ends in order to make the firewall allow the incoming connection. But, alas, this capability of listening for an incoming connection is not something a browser from plain javascript will allow you to do.
现有的P2P应用程序(在浏览器之外)的工作方式是让每个客户端实际侦听传入的连接(充当服务器),以便另一个客户端可以直接连接到它们。P2P本身要比这复杂得多,因为人们需要有一种方法来发现要连接的IP地址(因为客户端通常不在DNS中),而且通常存在防火墙,需要在两端之间进行一些合作,以便使防火墙允许传入的连接。但是,遗憾的是,这种监听传入连接的功能不是普通的javascript浏览器所允许的。
There is no something like "connection between two browsers using socket.io-client."
不存在“使用Socket.IO-CLIENT在两个浏览器之间建立连接”之类的东西。
But there is "Both browsers are connected to a node.js app serving HTTP requests with Express, which keeps track of both clients's IP addresses (and ports when running locally)."
但也有“两个浏览器都连接到一个node.js应用上,通过Express处理HTTP请求,它会跟踪两个客户端的IP地址(以及在本地运行时的端口)。”
If you want to have P2P connection between two browser, following may be a way to do so.
如果您想在两个浏览器之间建立P2P连接,以下可能是一种方法。
- when you get client A connection, join to a room "P2P"
- when you get client B connection, join to a room "P2P"
to exchange between client A and client B, use
socket.broadcast.to('P2P').emit("message", "Good Morning");
Hope this may help.
希望这能有所帮助。
You might consider https://github.com/socketio/socket.io-p2p
您可能会考虑https://github.com/socketio/socket.io-p2p
This module provides an easy and reliable way to setup a WebRTC
connection between peers and communicate using events (the
socket.io-protocol).
Socket.IO is used to transport signaling data and as a fallback for
clients where WebRTC PeerConnection is not supported.
In short, this would allow you to use the WebRTC p2p library to coordinate connecting clients p2p at the server, and from there, proceed with what is essentially a peer to peer websocket/socket.io-client connection.
简而言之,这将允许您使用WebRTC P2P库来协调在服务器上连接客户端P2P,并从那里继续进行本质上是对等的WebSocket/Socket.IO-Client连接。
This would allow p2p audio/video/text/binary over what is basically socket.io.
这将允许P2P音频/视频/文本/二进制文件在基本上是Socket.IO的文件上传输。
Just to throw it out there, google chrome and other browsers will block audio/video if it is not over HTTPS (except for localhost url) as a security precaution. This is a little off topic, but if you run into this problem during development or testing, I would consider setting-up a VPS (digital ocean droplet runs like $6/mo). Setup an Nginx reverse proxy: We will connect your dev machine port 3000 to the servers port 3000 using SSH. So configure Nginx to grab port 3000 and kick it out it over HTTPS on port 443. Now use Let's Encrypt to grab a free TLS Certificate and install it for Nginx. From there, finally, use SSH to forward your development machine's HTTP port 3000 to the servers port 3000. Make sure any firewall you have on the server is open for port 443. Now you, and others, can do local development, accessing it through a legitimate domain name over HTTPS.
简单地说,谷歌Chrome和其他浏览器将屏蔽音频/视频,如果它不是通过HTTPS(除了本地主机URL)作为安全预防措施。这是一个有点离题的问题,但如果你在开发或测试过程中遇到这个问题,我会考虑设置一个VPS(数字海洋水滴运行大约6美元/月)。设置Nginx反向代理:我们将使用SSH将您的开发机器端口3000连接到服务器端口3000。因此,将Nginx配置为GRAB端口3000,并在端口443上通过HTTPS将其踢出。现在使用We‘s Encrypt获取一个免费的TLS证书,并为Nginx安装它。最后,使用SSH将开发机器的HTTP端口3000转发到服务器端口3000。确保服务器上的所有防火墙都已为端口443打开。现在,您和其他人可以进行本地开发,通过HTTPS上的合法域名访问它。
Good luck!
祝好运!
更多回答
So that's what I aalready have set up. Now what I'm trying to achieve is a real P2P connection between two browsers, so that data sent over this connection will not transit through the browser anymore. If that's possible at all.
这就是我已经设置好的。现在我想实现的是两个浏览器之间的真正的P2P连接,这样通过此连接发送的数据将不再通过浏览器传输。如果可能的话。
我是一名优秀的程序员,十分优秀!