Browse Source

code init

221V 6 days ago
commit
c36bb87de9
3 changed files with 224 additions and 0 deletions
  1. 66 0
      README.md
  2. 56 0
      socktest.erl
  3. 102 0
      socktest.zig

+ 66 - 0
README.md

@@ -0,0 +1,66 @@
+# erlang + Zig with Unix domain socket
+
+```
+erl 25
+zig 0.13.0
+(l)ubuntu 22.04 lts
+
+
+// in terminal 1
+$ zig build-exe socktest.zig -O ReleaseFast --library c -femit-bin=socktest
+
+$ rm -f /tmp/socktest.sock
+
+$ ./socktest
+Zig server is listening /tmp/socktest.sock
+
+
+// in terminal 2
+
+$ ls -la /tmp/socktest.sock
+srwxrwxr-x 1 e e 0 сер  7 20:38 /tmp/socktest.sock=
+
+$ lsof /tmp/socktest.sock
+COMMAND      PID USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
+socktest 1929355    e    3u  unix 0x00000000e2e74e16      0t0 14115541 /tmp/socktest.sock type=STREAM
+
+$ erl
+1> c(socktest).
+{ok,socktest}
+2> socktest:send("hello").
+<<"Hello World (by Zig) !">>
+3> socktest:send("factorial 5").
+<<"factorial(5) = 120">>
+4> socktest:send("factorial 20").
+<<"factorial(20) = 2432902008176640000">>
+5> socktest:send("factorial 34").
+<<"factorial(34) = 295232799039604140847618609643520000000">>
+6> socktest:send("factorial 35").
+<<"error: input too large, max is 34">>
+7> socktest:send("factorial -5").
+<<"error: invalid number">>
+8> socktest:send("factorial xyz").
+<<"error: invalid number">>
+9> q().
+ok
+
+
+// in terminal 1 we can see
+./socktest
+Zig server is listening /tmp/socktest.sock
+client connected
+receive msg: hello
+client connected
+receive msg: factorial 5
+client connected
+receive msg: factorial 20
+client connected
+receive msg: factorial 34
+client connected
+receive msg: factorial 35
+client connected
+receive msg: factorial -5
+client connected
+receive msg: factorial xyz
+```
+

+ 56 - 0
socktest.erl

@@ -0,0 +1,56 @@
+-module(socktest).
+
+-export([
+  connect/0,
+  send/1
+]).
+
+
+connect() ->
+  SocketPath = "/tmp/socktest.sock",
+  
+  Options = [
+    binary,         %% receive binary
+    {packet, raw},  %% raw data - without prefix - for TCP only
+    {active, false} %% sync
+  ],
+  
+  Conn = gen_tcp:connect({local, SocketPath}, 0, Options), %% Unix domain socket
+  
+  case Conn of
+    {ok, Socket} ->
+      %%io:format("connected to Unix socket ~s~n", [SocketPath]),
+      Socket;
+    {error, Reason} ->
+      io:format("connect err: ~p~n", [Reason]),
+      error
+  end.
+
+
+send(Message) when is_list(Message) orelse is_binary(Message) ->
+  Msg = iolist_to_binary(Message),
+  Socket = connect(),
+  send(Socket, Msg).
+
+send(error, _) -> error;
+send(Socket, Msg) ->
+  case gen_tcp:send(Socket, Msg) of
+    ok ->
+      
+      case gen_tcp:recv(Socket, 0) of
+        {ok, Reply} ->
+          %%io:format("received: ~s~n", [Reply]),
+          gen_tcp:close(Socket),
+          Reply;
+        {error, Reason} ->
+          %%io:format("receive err: ~p~n", [Reason]),
+          gen_tcp:close(Socket),
+          {error, Reason}
+      end;
+    
+    E = {error, Reason} ->
+      io:format("send err: ~p~n", [Reason]),
+      gen_tcp:close(Socket),
+      E
+  end.
+

+ 102 - 0
socktest.zig

@@ -0,0 +1,102 @@
+
+
+const std = @import("std");
+
+const c = @cImport({
+  @cInclude("sys/socket.h");
+  @cInclude("sys/un.h");
+  @cInclude("unistd.h");
+});
+
+const SOCKET_PATH = "/tmp/socktest.sock";
+
+
+pub fn main() !void{
+  std.fs.deleteFileAbsolute(SOCKET_PATH) catch {}; // delete old if exists
+  
+  const sock = c.socket(c.AF_UNIX, c.SOCK_STREAM, 0); // create new TCP sock, Unix domain (TCP-like)
+  if(sock == -1){
+    std.debug.print("failed to create socket\n", .{});
+    return error.SocketCreateFailed;
+  }
+  defer _ = c.close(sock);
+  
+  var addr: c.struct_sockaddr_un = undefined;
+  addr.sun_family = c.AF_UNIX;
+  
+  if(SOCKET_PATH.len >= addr.sun_path.len){
+    return error.PathTooLong;
+  }
+  @memcpy(addr.sun_path[0..SOCKET_PATH.len], SOCKET_PATH);
+  addr.sun_path[SOCKET_PATH.len] = 0;
+  
+  if(c.bind(sock, @ptrCast(&addr), @sizeOf(c.struct_sockaddr_un)) == -1){
+    std.debug.print("failed to bind socket\n", .{});
+    return error.SocketBindFailed;
+  }
+  
+  if(c.listen(sock, 1) == -1){
+    std.debug.print("failed to listen socket\n", .{});
+    return error.SocketListenFailed;
+  }
+  
+  std.debug.print("Zig server is listening {s}\n", .{SOCKET_PATH});
+  
+  while(true){
+    const client_fd = c.accept(sock, null, null);
+    if(client_fd == -1){
+      std.debug.print("accept error\n", .{});
+      continue;
+    }
+    
+    std.debug.print("client connected\n", .{});
+    
+    const file = std.fs.File{ .handle = @intCast(client_fd) };
+    const in_stream = file.reader();
+    const out_stream = file.writer();
+    
+    var buffer: [256]u8 = undefined;
+    const n_bytes = in_stream.read(&buffer) catch |err|{ // read msg
+      std.debug.print("read msg err: {any}\n", .{err});
+      _ = c.close(client_fd);
+      continue;
+    };
+    
+    const msg = buffer[0..n_bytes];
+    std.debug.print("receive msg: {s}\n", .{msg});
+    
+    
+    // process msg
+    var reply: [128]u8 = undefined;
+    var fbs = std.io.fixedBufferStream(&reply);
+    var writer = fbs.writer();
+    
+    if(std.mem.eql(u8, msg, "hello")){
+      try writer.print("Hello World (by Zig) !", .{});
+    
+    }else if(std.mem.startsWith(u8, msg, "factorial ")){
+      const num_str = msg["factorial ".len..];
+      if(std.fmt.parseInt(usize, num_str, 10)) |num|{
+        
+        if(num > 34){ // max n for max factorial for u128
+          try writer.print("error: input too large, max is 34", .{});
+        }else{
+          var fact: u128 = 1;
+          var i = num;
+          while(i > 1) : (i -= 1){ fact *= i; }
+          try writer.print("factorial({d}) = {d}", .{ num, fact });
+        }
+      
+      }else |_|{
+        try writer.print("error: invalid number", .{});
+      }
+    
+    }else{
+      try writer.print("unknown command", .{});
+    }
+    
+    _ = try out_stream.write(fbs.getWritten()); // send responce
+    _ = c.close(client_fd);
+  }
+}
+