【问题标题】:How to use Haxe SSL Socket?如何使用 Haxe SSL 套接字?
【发布时间】:2019-06-02 20:19:30
【问题描述】:

我正在尝试设置一个 https 服务器,但我找不到任何关于如何使用 Haxe sys.ssl.Socket 的示例,并且文档并没有完全说明我的意思。

到目前为止,我得到了以下引发“访问冲突”的代码。

var _aSocketDistant = new List<Socket>();

var _oSocketMaster =  new SocketSSL();
_oSocketMaster.setCA(Certificate.loadFile('ssl/homeplanet.pem'));
_oSocketMaster.setCertificate( 
    Certificate.loadFile('ssl/homeplanet.pem'), 
    Key.loadFile('ssl/homeplanet.key', false, 'homeplanet') 
);
_oSocketMaster.setHostname('localhost');

_oSocketMaster.bind( new Host( 'localhost' ), 8000);
_oSocketMaster.setBlocking( false );
_oSocketMaster.listen( 9999 );

while(true) {

    // Accepting socket
    var oSocketDistant = _oSocketMaster.accept();
    if ( oSocketDistant != null ) {
        trace( 'opening : ' + oSocketDistant.peer() );
        oSocketDistant.setBlocking( false );
        _aSocketDistant.add( oSocketDistant );
    }

    // Trying to read from each socket 
    for ( oSocketDistant in _aSocketDistant ) {
        try {
            Sys.print( oSocketDistant.input.readString(1) );
        } catch ( e :Dynamic ) {
            if ( e != Error.Blocked )
                throw e;
        }
    }
}

这给了我以下结果:

Uncaught exception: Access violation
Called from sys.ssl.Socket.accept(D:\HaxeToolkit4\haxe\std/hl/_std/sys/ssl/Socket.hx:203)
Called from $Main.main(Main.hx:39)
Called from fun$517(?:1)

使用this tutorial 生成的密钥/证书文件。

我是否正确使用了套接字?

【问题讨论】:

    标签: haxe hashlink


    【解决方案1】:

    好的,我决定自己测试 Haxe SSL 套接字。由于有很多警告,这可能无法解决您的问题,但也许一些花絮会有所帮助。并且从“有用的东西”开始总比没有好!

    首先,我在 Linux 上。我发现套接字(和线程)在 Windows 上的行为和问题与在 Linux / OSX 上的不同。

    其次,我首先尝试使用有效的主机名。我从未在localhost 上使用过 SSL,并且想删除所有未知数。所以我碰巧有一个我正在使用的有效证书/密钥。在下面的代码中,它被称为foo.example.com。您可以通过 letsencrypt.org 获得您拥有的域的免费证书。

    第三,我在 Haxe 标准库中遇到了an issue。为了解决这个问题,我只是更改了haxe/std/cpp/_std/sys/ssl/Key.hx 中的第 17 行:

    var str = data.toString(); // cpp.Lib.stringReference(data);
    

    第四,我不知道“访问冲突”问题。这可能是特定于 Windows 的。我猜可能是权限或防火墙问题,但谷歌搜索“Windows 套接字访问冲突”,我看到很多随机讨论。

    最后,我不确定带有 while 循环的非阻塞套接字是个好主意。也许可以那样做......但我一直有更好的阻塞套接字和线程的运气(同样,线程在 'nix 上的表现可能比 Windows 更好。)

    注意:如果您确实使用非阻塞套接字,有时您必须同时捕获/忽略haxe.io.Error.Blockedhaxe.io.Error.Custom(Blocked)。一种刺激,但是嗯。使用这个:

      try {
        Sys.print( oSocketDistant.input.readString(1) );
      } catch ( e:haxe.io.Error ) {
        switch e {
          case haxe.io.Error.Blocked: // no problem
          case haxe.io.Error.Custom(c) if (c==haxe.io.Error.Blocked): // no problem
          default: throw e;
        }
      } catch ( e:haxe.io.Eof ) {
        trace('Got Eof');
      }
    

    使用带有阻塞套接字的线程更有效。这样,线程只是休眠,直到套接字将其唤醒。这就是你想要的,所以 CPU 不会在 while 循环中旋转,无休止地检查未阻塞的套接字。

    所以我稍微修改了您的代码。我的示例使用主线程接受连接,然后将套接字传递给读取器线程。阅读器线程打印收到的任何内容(如您的示例),然后在Eof 上退出。

    import sys.net.Host;
    import sys.net.Socket;
    import sys.ssl.Socket as SocketSSL;
    import sys.ssl.Certificate;
    import sys.ssl.Key;
    
    import cpp.vm.Mutex;
    import cpp.vm.Thread;
    
    class Main
    {
      static var _mutex:Mutex = new Mutex();
    
      public static function main()
      {
        var _oSocketMaster =  new SocketSSL();
        var cert = Certificate.loadFile('my_chain.pem');
        _oSocketMaster.setCA(cert);
        _oSocketMaster.setCertificate(cert, 
                                      Key.loadFile('my_key.key'));
        _oSocketMaster.setHostname('foo.example.com');
    
        // e.g. for an application like an HTTPs server, the client
        // doesn't need to provide a certificate. Otherwise we get:
        // Error: SSL - No client certification received from the client, but required by the authentication mode
        _oSocketMaster.verifyCert = false;
    
        // Binding 0.0.0.0 means, listen on "any / all IP addresses on this host"
        _oSocketMaster.bind( new Host( '0.0.0.0' ), 8000);
        _oSocketMaster.listen( 9999 );
    
        while(true) {
    
          // Accepting socket
          trace('waiting to accept...');
          var oSocketDistant:SocketSSL = _oSocketMaster.accept();
          if ( oSocketDistant != null ) {
            trace( 'got connection from : ' + oSocketDistant.peer() );
            oSocketDistant.handshake(); // This may not be necessary, if !verifyCert
    
            // Spawn a reader thread for this connection:
            var thrd = Thread.create(reader);
            trace('sending socket...');
            thrd.sendMessage(oSocketDistant);
            trace('ok...');
          }
    
        }
      }
    
      static function reader()
      {
        var oSocketDistant:sys.net.Socket = cast Thread.readMessage(true);
        trace('new reader thread...');
    
        while(true) {
    
          try {
            Sys.print( oSocketDistant.input.readString(1) );
          } catch ( e:haxe.io.Eof ) {
            trace('Eof, reader thread exiting...');
            return;
          } catch ( e:Dynamic ) {
            trace('Uncaught: ${ e }'); // throw e;
          }
        }
      }
    }
    

    那么,让我们看看它的实际效果吧!

    我在一个终端编译并启动上面的服务器:

    > haxe -main Main -debug -cpp out && ./out/Main-debug
    ...compiling info removed...
    Main.hx:37: waiting to accept...
    

    然后我从另一个终端连接一个客户端,该客户端是一个用于测试 ssl 连接的命令行实用程序:

    > openssl s_client -connect foo.example.com:8000
    ...lots of info about the cert...
    SSL handshake has read 3374 bytes and written 370 bytes
    Verification: OK
    ---
    

    它挂在那里,等待你输入。在服务器端我们看到:

    Main.hx:38: got connection from : { host => Host, port => 57394 }
    Main.hx:43: sending socket...
    Main.hx:45: ok...
    Main.hx:35: waiting to accept...
    Main.hx:54: new reader thread...
    

    我们可以在不同的终端中打开多个客户端,每个客户端都有自己的阅读器线程。将消息输入客户端会显示在服务器端,因此阅读器线程正在工作。

    在客户端,CTRL+C退出,在服务端我们看到:

    Main.hx:61: Eof, reader thread exiting...
    

    一切都按预期工作!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-10
      • 1970-01-01
      • 2011-09-19
      • 2016-10-19
      • 2013-09-18
      • 2021-12-18
      • 1970-01-01
      相关资源
      最近更新 更多