从 sgcWebSockets 4.5.2 起,您可以使用 sgcWebSockets 库构建自己的 STUN/TURN 服务器和客户端。
STUN
NAT 会话穿越实用工具(STUN,Session Traversal Utilities for NAT)是一种用于发现公网地址并确定路由器中是否存在阻止与对等节点直接连接的限制的协议。
客户端将向互联网上的 STUN 服务器发送请求,服务器将回复客户端的公网地址以及客户端是否可在路由器的 NAT 后面访问。
TURN
某些使用 NAT 的路由器采用了一种称为"对称 NAT"的限制。这意味着路由器只接受来自您之前连接过的对等节点的连接。
通过 NAT 中继穿越(TURN,Traversal Using Relays around NAT)旨在通过与 TURN 服务器建立连接并通过该服务器中继所有信息来绕过对称 NAT 限制。您可以创建与 TURN 服务器的连接,并告知所有对等节点将数据包发送到该服务器,服务器会将其转发给您。这显然会带来一些开销,因此只有在没有其他替代方案时才使用。
构建 STUN/TURN 服务器
创建 Delphi STUN/TURN 服务器的过程非常简单,只需创建一个 TsgcTURNServer 并配置以下属性:
- Port:默认为 3478,这是 STUN/TURN 协议的通用端口。
- STUNOptions:在此配置 STUN 选项。通常 STUN 请求不使用身份验证,因此可以保留默认值。
- TURNOptions:在此配置 TURN 选项。通常 TURN 服务器需要长期凭据(因为 TURN 使用中继地址在 NAT 后面的对等节点之间交换数据,这需要大量资源)。在此配置中继地址的 IP 地址,在 TURNOptions.Allocation.RelayIP 中设置。
处理事件 OnSTUNRequestAuthorization,在 TURN 客户端发送请求时设置密码。
最后将 Active 属性设置为 True 以启动服务器。
以下示例配置中,STUN 请求无需授权,而 TURN 请求需要长期凭据:
oTURN := TsgcTURNServer.Create(nil);
oTURN.Port := 3478;
oTURN.STUNOptions.Authentication.Enabled := False;
oTURN.TURNOptions.Authentication.Enabled := True;
oTURN.TURNOptions.Authentication.LongTermCredentials.Enabled := True;
oTURN.TURNOptions.Authentication.LongTermCredentials.Realm := 'sgcWebSockets';
oTURN.TURNOptions.Authentication.LongTermCredentials.StaleNonce := 600;
oTURN.Active := True;
procedure OnSTUNRequestAuthorization(Sender: TObject; const aRequest: TsgcSTUN_Message; const aUsername, aRealm: string; var Password: string);
begin
if (aUsername = 'my-user') and (aRealm = 'sgcWebSockets') then
Password := 'my-password';
end;
构建 STUN/TURN 客户端
创建 Delphi STUN/TURN 客户端与创建服务器一样简单。只需创建一个 TsgcTURNClient 并配置以下属性:
- Host:STUN/TURN 服务器运行所在的 DNS 名称或 IP 地址。
- Port:端口,默认为 3478。
- STUNOptions:在此配置 STUN 选项。通常 STUN 请求不使用身份验证,因此可以保留默认值。
- TURNOptions:在此配置 TURN 选项。通常 TURN 服务器需要长期凭据(因为 TURN 使用中继地址在 NAT 后面的对等节点之间交换数据,这需要大量资源)。
分配 IP 地址
TURN 协议允许使用中继 IP 地址在 NAT 后面的对等节点之间交换数据。
要在 TURN 服务器上创建新的中继 IP 地址,客户端必须首先调用方法 Allocate,该方法向 TURN 服务器发送请求以创建新的中继 IP 地址。如果 TURN 服务器可以创建新的中继 IP 地址,客户端将收到成功响应。客户端可以在分配生命周期内与其他对等节点进行通信。
oTURN := TsgcTURNClient.Create(nil);
oTURN.Host := 'turn.sgcwebsockets.com';
oTURN.Port := 3478;
oTURN.Allocate();
procedure OnTURNAllocate(Sender: TObject; const aSocket: TsgcSocketConnection; const
aMessage: TsgcSTUN_Message; const aAllocation: TsgcTURN_ResponseAllocation);
begin
DoLog('Relayed IP: ' + aAllocation.RelayedIP + '. Relayed Port: ' + IntToStr(aAllocation.RelayedPort));
end;
procedure OnSTUNResponseError(Sender: TObject; const aMessage: TsgcSTUN_Message;
const aError: TsgcSTUN_ResponseError);
begin
DoLog('Error: ' + IntToStr(aError.Code) + ' ' + aError.Reason);
end;
可以使用方法 Refresh 更新生命周期以避免过期。Lifetime 是以秒为单位的过期时间。如果值为零,则分配将被删除。
oTURN.Refresh(600);
