Browse Source

unix domain socket - udp-like

221V 6 days ago
parent
commit
b0d3a94eaf
5 changed files with 241 additions and 6 deletions
  1. 65 1
      README.md
  2. 2 2
      socktest.erl
  3. 3 3
      socktest.zig
  4. 66 0
      socktest2.erl
  5. 105 0
      socktest2.zig

+ 65 - 1
README.md

@@ -6,10 +6,17 @@ zig 0.13.0
 (l)ubuntu 22.04 lts
 
 
+socktest  -- TCP-like Unix domain socket example
+socktest2 -- UDP-like Unix domain socket example
+```
+
+
+### socktest  -- TCP-like Unix domain socket example
+```
 // in terminal 1
 $ zig build-exe socktest.zig -O ReleaseFast --library c -femit-bin=socktest
 
-$ rm -f /tmp/socktest.sock
+//$ rm -f /tmp/socktest.sock
 
 $ ./socktest
 Zig server is listening /tmp/socktest.sock
@@ -64,3 +71,60 @@ client connected
 receive msg: factorial xyz
 ```
 
+
+### socktest2 -- UDP-like Unix domain socket example
+```
+// in terminal 1
+$ zig build-exe socktest2.zig -O ReleaseFast --library c -femit-bin=socktest2
+
+//$ rm -f /tmp/socktest2.sock
+
+$ ./socktest2
+Zig server is listening /tmp/socktest2.sock
+
+
+// in terminal 2
+
+$ ls -la /tmp/socktest2.sock
+srwxrwxr-x 1 e e 0 сер  8 12:45 /tmp/socktest2.sock=
+
+$ lsof /tmp/socktest2.sock
+COMMAND       PID USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
+socktest2 2063359    e    3u  unix 0x000000002af1affe      0t0 14986276 /tmp/socktest2.sock type=DGRAM
+
+$ erl
+1> c(socktest2).
+{ok,socktest2}
+2> socktest2:send("hello").
+<<"Hello World (by Zig) !">>
+3> socktest2:send("factorial 5").
+<<"factorial(5) = 120">>
+4> socktest2:send("factorial 20").
+<<"factorial(20) = 2432902008176640000">>
+5> socktest2:send("factorial 34").
+<<"factorial(34) = 295232799039604140847618609643520000000">>
+6> socktest2:send("factorial 35").
+<<"error: input too large, max is 34">>
+7> socktest2:send("factorial -5").
+<<"error: invalid number">>
+8> socktest2:send("factorial xyz").
+<<"error: invalid number">>
+9> socktest2:send("бла").
+<<"unknown command">>
+10> q().
+ok
+
+
+// in terminal 1 we can see
+./socktest2
+Zig UDP server is listening /tmp/socktest2.sock
+receive msg: hello
+receive msg: factorial 5
+receive msg: factorial 20
+receive msg: factorial 34
+receive msg: factorial 35
+receive msg: factorial -5
+receive msg: factorial xyz
+receive msg: бла
+```
+

+ 2 - 2
socktest.erl

@@ -15,11 +15,11 @@ connect() ->
     {active, false} %% sync
   ],
   
-  Conn = gen_tcp:connect({local, SocketPath}, 0, Options), %% Unix domain socket
+  Conn = gen_tcp:connect({local, SocketPath}, 0, Options), %% Unix domain socket TCP-like
   
   case Conn of
     {ok, Socket} ->
-      %%io:format("connected to Unix socket ~s~n", [SocketPath]),
+      %%io:format("connected to Unix socket (TCP) ~s~n", [SocketPath]),
       Socket;
     {error, Reason} ->
       io:format("connect err: ~p~n", [Reason]),

+ 3 - 3
socktest.zig

@@ -25,7 +25,7 @@ pub fn main() !void{
   addr.sun_family = c.AF_UNIX;
   
   if(SOCKET_PATH.len >= addr.sun_path.len){
-    return error.PathTooLong;
+    return error.SocketPathTooLong;
   }
   @memcpy(addr.sun_path[0..SOCKET_PATH.len], SOCKET_PATH);
   addr.sun_path[SOCKET_PATH.len] = 0;
@@ -67,8 +67,8 @@ pub fn main() !void{
     
     
     // process msg
-    var reply: [128]u8 = undefined;
-    var fbs = std.io.fixedBufferStream(&reply);
+    var reply_buffer: [128]u8 = undefined;
+    var fbs = std.io.fixedBufferStream(&reply_buffer);
     var writer = fbs.writer();
     
     if(std.mem.eql(u8, msg, "hello")){

+ 66 - 0
socktest2.erl

@@ -0,0 +1,66 @@
+-module(socktest2).
+
+-export([
+  connect/0,
+  send/1
+]).
+
+
+-define(SOCKET_PATH, "/tmp/socktest2.sock").
+-define(SOCKET_CLIENT_PATH, "/tmp/sockclienttest2.sock").
+
+
+connect() ->
+  catch file:delete(?SOCKET_CLIENT_PATH), %% delete old client sock if exists
+  %case socket:open(local, dgram, udp) of %% Unix domain socket UDP-like
+  case socket:open(local, dgram, 0) of %% Unix domain socket UDP-like
+    {ok, Socket} ->
+      %%io:format("connected to Unix socket (UDP) ~s~n", [SocketPath]),
+      connect(Socket);
+    {error, Reason} ->
+      io:format("connect err: ~p~n", [Reason]),
+      error
+  end.
+
+connect(Socket) ->
+  case socket:bind(Socket, #{family => local, path => ?SOCKET_CLIENT_PATH}) of
+    ok -> Socket;
+    {error, Reason} ->
+      io:format("connect bind err: ~p~n", [Reason]),
+      error
+  end.
+
+
+send(Message) when is_list(Message) orelse is_binary(Message) ->
+  Socket = connect(),
+  send(Socket, Message).
+
+send(error, _) -> error;
+send(Socket, Message) ->
+  Msg = unicode:characters_to_binary(Message, utf8),
+  case socket:sendto(Socket, Msg, #{family => local, path => ?SOCKET_PATH}) of
+    ok ->
+      
+      case socket:recv(Socket, 0, 5000) of %% 5 sec timeout
+        {ok, Reply} ->
+          %%io:format("received: ~s~n", [Reply]),
+          socket:close(Socket),
+          Reply;
+        
+        E = {error, timeout} ->
+          io:format("Receive timeout~n", []),
+          socket:close(Socket),
+          E;
+        
+        E = {error, Reason} ->
+          io:format("receive err: ~p~n", [Reason]),
+          socket:close(Socket),
+          E
+      end;
+    
+    E = {error, Reason} ->
+      io:format("send err: ~p~n", [Reason]),
+      gen_udp:close(Socket),
+      E
+  end.
+

+ 105 - 0
socktest2.zig

@@ -0,0 +1,105 @@
+
+
+const std = @import("std");
+
+const c = @cImport({
+  @cInclude("sys/socket.h");
+  @cInclude("sys/un.h");
+  @cInclude("unistd.h");
+});
+
+const SOCKET_PATH = "/tmp/socktest2.sock";
+
+
+pub fn main() !void{
+  std.fs.deleteFileAbsolute(SOCKET_PATH) catch {}; // delete old if exists
+  
+  const sock = c.socket(c.AF_UNIX, c.SOCK_DGRAM, 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.SocketPathTooLong;
+  }
+  @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;
+  }
+  
+  std.debug.print("Zig UDP server is listening {s}\n", .{SOCKET_PATH});
+  
+  
+  var buffer: [256]u8 = undefined;
+  var sender_addr: c.struct_sockaddr_un = undefined;
+  var addr_len: c.socklen_t = @sizeOf(c.struct_sockaddr_un);
+  
+  while(true){
+    const n_bytes = c.recvfrom(
+      sock,
+      &buffer,
+      buffer.len,
+      0,
+      @ptrCast(&sender_addr),
+      &addr_len
+    );
+    
+    if(n_bytes <= 0){
+      std.debug.print("receive error\n", .{});
+      continue;
+    }
+    
+    const msg = buffer[0..@intCast(n_bytes)];
+    std.debug.print("receive msg: {s}\n", .{msg});
+    
+    
+    // process msg
+    var reply_buffer: [128]u8 = undefined;
+    var fbs = std.io.fixedBufferStream(&reply_buffer);
+    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", .{});
+    }
+    
+    const reply = fbs.getWritten();
+    
+    _ = c.sendto( // send responce
+      sock,
+      reply.ptr,
+      reply.len,
+      0,
+      @ptrCast(&sender_addr),
+      addr_len
+    );
+  }
+}
+