Jump to content

Node.JS WebSocket Client Socket


Recommended Posts

This is me sharing again.

The NodeJS WebSocket server socket is implemented in SMS. But I didn't find an implementation of the WebSocket client socket.

Sometimes an SMS NodeJS server needs to connect to another server. So I kinda hacked this together using the server socket as a guide.

unit UPBNCommonNJWebSocket;

interface

uses 
  System.Types,
  System.Types.Convert,
  System.Time,
  System.Streams,
  System.Reader,
  System.Writer,
  System.Device.Storage,
  System.Objects,
  SmartNJ.System,
  SmartNJ.Streams,
  SmartNJ.Device.Storage,
  SmartNJ.Application,
  NodeJS.Core,
  NodeJS.WebSocket,
  SmartNJ.Server.WebSocket;

type
  // Forward declarations
  TNJWebSocket   = class;

  TNJWebSocketOpenEvent     = procedure (Sender: TNJWebSocket);
  TNJWebSocketCloseEvent    = procedure (Sender: TNJWebSocket; Code: integer; const Reason: string);
  TNJWebSocketErrorEvent    = procedure (Sender: TNJWebSocket; Error: TJSErrorObject);
  TNJWebSocketMessageEvent  = procedure (Sender: TNJWebSocket; Message: TNJWebsocketMessage);

  TNJWebSocket = class(TW3ErrorObject)
  private
    FSocket:    JWsSocket;
  public
    property WSSocket: JWsSocket read FSocket;
    function    SocketState: JWsReadyState;
    function    Connected: boolean;
    function    URL: string;
    function    Protocol: string;

    procedure   Connect(URL: string; Protocols: array of string); overload;
    procedure   Connect(URL: string); overload;

    procedure   Disconnect; overload;

    procedure Send(const Data: variant); overload;
    procedure Send(const Text: string); overload;
    procedure Send(const Data: TStream); overload;

    property    TagData: variant;

    constructor Create; override;
    destructor  Destroy; override;
    procedure Ping;
  published
    property OnOpen: TNJWebSocketOpenEvent;
    property OnClosed: TNJWebSocketCloseEvent;
    property OnMessage: TNJWebSocketMessageEvent;
    property OnError: TNJWebSocketErrorEvent;
  end;

implementation

constructor TNJWebSocket.Create;
begin
  inherited Create;
  // We dont want to throw exceptions whenever SetLastError() is called
  ErrorOptions.ThrowExceptions := false;
end;

destructor TNJWebSocket.Destroy;
begin
  FSocket := nil;
  inherited;
end;

procedure TNJWebSocket.Ping;
begin
  if FSocket <> nil then
  asm
    (@FSocket).ping(function() {});
  end;
end;

function TNJWebSocket.Protocol: string;
begin
  if FSocket <> nil then
    Result := FSocket.protocol;
end;

function  TNJWebSocket.URL:String;
begin
  if FSocket <> nil then
    Result:=FSocket.url;
end;

function TNJWebSocket.SocketState: JWsReadyState;
begin
  if FSocket <> nil then
    Result := JWsReadyState( integer( FSocket.readyState) )
  else
    Result := rsClosed;
end;

function TNJWebSocket.Connected: boolean;
begin
  Result := SocketState = rsOpen;
end;

procedure TNJWebSocket.Connect(URL: string);
begin
  Connect(Url, []);
end;

procedure TNJWebSocket.Connect(URL: string; Protocols: Array of string);
begin
  ClearLastError();

  (* disconnect socket if already connected *)
  if connected then
    disconnect();

  (* Allocate new socket *)
  var WebSocket = WebSocketAPI;
  asm
    (@self.FSocket) = new (@WebSocket)(@URL, @Protocols);
  end;

  // initialize standard socket events
  FSocket.on("open",
    procedure
    begin
      if assigned(OnOpen) then
        OnOpen(self);
    end);
  FSocket.on("error", procedure (error: variant)
  begin
    SetLastError("internal websocket error");
    if assigned(OnError) then
      OnError(self, TJSErrorObject(error));
  end);
  FSocket.on("message",
    procedure (message: variant)
    var
      ResData:  TNJWebsocketMessage;
    begin
      if message.IsUint8Array then
      begin
        ResData.wiType := mtBinary;
        ResData.wiBuffer := JBuffer(message);
      end else
      begin
        ResData.wiType := mtText;
        ResData.wiText := message;
      end;
      if assigned(OnMessage) then
        OnMessage(self, ResData);
    end);
  Variant(FSocket).on("close",
    procedure (code: integer; reason: string)
    begin
      if assigned(OnClosed) then
        OnClosed(self, code, reason);
    end);
end;

procedure TNJWebSocket.Disconnect;
begin
  ClearLastError();
  if Connected then
  begin
    try
      FSocket.close();
    finally
      FSocket := nil;
    end;
  end;
end;

procedure TNJWebSocket.Send(const Data: variant);
begin
  FSocket.send(data);
end;

procedure TNJWebSocket.Send(const Text: string);
begin
  FSocket.send(Text);
end;

procedure TNJWebSocket.Send(const Data: TStream);
begin
  if Data <> nil then
  begin
    if Data.position < Data.Size then
    begin
      // Get bytes from stream
      var Bytes := Data.Read(Data.Size - Data.Position);

      // Convert to typed-array
      var TypedArray := TDataType.BytesToTypedArray(bytes);

      // Send as binary
      FSocket.Send(TypedArray);
    end;
  end;
end;

end.

 

It might be a bit simplified, and it doesn't really follow the SMS component name convention, but it does what I need. And I figured I would share.

-David

 

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...