如何使用Haxe SSL套接字?

时间:2019-06-02 20:19:30

标签: haxe hashlink

我正在尝试设置一个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生成的密钥/证书文件。

我正确使用了插座吗?

1 个答案:

答案 0 :(得分:7)

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

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

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

第三,我在Haxe std库中遇到了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...

一切都按预期进行!