{"id":15971156,"url":"https://github.com/glurp/minitcp","last_synced_at":"2025-06-19T22:39:09.737Z","repository":{"id":19712323,"uuid":"22967757","full_name":"glurp/minitcp","owner":"glurp","description":"Helper for programming client, server TCP/UDP in a few lines of code.","archived":false,"fork":false,"pushed_at":"2016-05-18T14:29:46.000Z","size":81,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-05-26T13:19:34.312Z","etag":null,"topics":["dsl","ruby","tcp","udp"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/glurp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.txt","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-08-14T20:33:38.000Z","updated_at":"2019-01-07T11:52:30.000Z","dependencies_parsed_at":"2022-08-24T10:40:08.708Z","dependency_job_id":null,"html_url":"https://github.com/glurp/minitcp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/glurp/minitcp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glurp%2Fminitcp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glurp%2Fminitcp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glurp%2Fminitcp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glurp%2Fminitcp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/glurp","download_url":"https://codeload.github.com/glurp/minitcp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/glurp%2Fminitcp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260844888,"owners_count":23071655,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dsl","ruby","tcp","udp"],"created_at":"2024-10-07T20:08:08.290Z","updated_at":"2025-06-19T22:39:04.723Z","avatar_url":"https://github.com/glurp.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Minitcp\n===\n\nPresentation\n==\n\nA little tool for doing some TCP/UDP sockets communications.\n\nThis tool have no pretention : it is not EventMachine ! : \n\n* no mainloop, \n* many threads are created/deleted.\n* it is useful for send some data one-shot, test a ascii communication in few minutes...\n* should be equivalent to netcat+bash ...\n\n\nA  TCP client :\n\n```ruby\nMClient.run_one_shot(\"localhost\",2200) do |socket|\n   socket.on_any_receive { |data| p \"client recieved #{data.inspect}\"}\n   3.times { |j| socket.puts \"Hello  #{j}...\" ; sleep(1) }\nend.join\n```\n\nAn echo server :\n\n```ruby\nsrv=MServer.service(2200,\"0.0.0.0\",22) do |socket|\n  socket.on_any_receive { |data|  socket.print(data) }\n  socket.on_timer(2000) do\n    socket.close\n  end\n  socket.wait_end\nend\n```\n\nA UDP server :\n\n```ruby\n\tUDPAgent.on_datagramme(\"127.0.0.2\",SRV_PORT ) { |data,from,p| \n\t  puts \"Agent: received #{data} from #{from}:#{p}\" \n\t  data \u0026\u0026 data.size\u003e3 ? \"OK-#{data}.\" : nil\n\t}\n```\n\nA UDP sender :\n\n```ruby\n\tUDPAgent.on_timer(1000, \n\t\tport: 2232,\n\t\ton_timer: proc do\n\t\t  data=Time.now.to_i.to_s\n\t\t  puts \"\\n\\n\\non timer send \u003c#{data}\u003e\"\n\t\t  {mess: data,host: \"127.0.0.2\",port: SRV_PORT}\n\t\tend,\n\t\ton_receive: proc { |data,from,sock| \n\t\t  puts \"Client: received #{data} from #{from}\"  \n\t\t  UDPAgent.send_datagram_on_socket(sock,from.last,from[1],'ack')\n\t\t}\n\t)\n```\n\nA Message agent :\nMessage Client:\n```ruby\nMClientAgent.run(\"localhost\",2222) do |chan|\n  chan.send_message({date: Time.now.to_f}) \n  chan.on_message do |mess| \n\tputs \"cli: receive: #{mess.inspect}\" \n\tchan.close\n\tnil\n  end\n  chan.on_timeout(10_000) { p \"timeout\" ; chan.close rescue nil }\n  chan.wait_end\nend\n```\n\nMessage Server:\n```ruby\nMServerAgent.run(2222,\"localhost\",22) do |chan|\n    chan.on_message { |mess|  p mess ; \"ok\" }\n    chan.wait_end\nend\n```\n\nDocs: http://rubydoc.info/gems/minitcp\n\n\nTCP\n===\n\nClient\n---\n\n```\nMClient.run_one_shot(host,port) do |socket| ... end\nMClient.run_continious(host,port,time_inter_connection) do |socket| ... end\n```\n\nClient socket is extended with all specifiques commandes (```SocketReactive```, see Sockets).\n\nServer\n---\n\n```ruby\nsrv=MServer.service(port,\"0.0.0.0\",max_client) do |socket| ... end\n```\nMserver run a GServer. \nConnected sockets are extended with same module as in client.\n\nSockets\n---\n\nHandlers are disponibles for any sockets : client or server. \nAll handler's bloc run in distinct thread : so any handler-bloc can wait anything.\nif socket is closed, handler/thread are cleanly (?) stoped.\n\n* socket.**on_any_receive() {|data| ...}**          : on receive some data, any size\n* socket.**on_n_receive(sizemax=1) {|data| ...}**   : receives n byte(s) only\n* socket.**on_receive_sep(\";\") {|field | ... }**    : reveive data until string separator\n* socket.**on_timer(value_ms) { ... }**             : each time do something, if socket is open\n* socket.**after(duration) { ... }**    : do something after n millisecondes, if socket is open\n\nSome primitives are here for help (no thread):\n\n* **received_timeout(sizemax,timeout)** : wait for n bytes, with timeout, (blocking caller),\n* **wait_end()**                        : wait, (blocking caller) until socket is close. this\n  work only if something has closed the socket.this is possible unicly by receiving 0 bte on a\n* **receive_n_bytes** / **on_receive_sep** : bocking version of handler,\n* **connected?** : test if a close have been done,  \n* **data_readed()** : some receives (receive_sep...) can read more data form sockets,\n this data are used by ```n_revieve/any_receive/receive_sep```, but they can be read/reseted \n whith data_readed getseter.\n\nThis primitives are declared in SocketReactive module.\n\nUDP\n===\n\n2 type of agents : \n\n* **datagramme** (server) : \n   receive data from anywhere, can reply to sender : UDPAgent.on_datagramme\n* **Timer**  (client) : \n   can emit to everybody, periodicly. Can receive response from them : UDPAgent.on_timer\n   ```send_datagram_on_socket``` can be used for emit spontaneously to a server\n\n2 primitives :\n\n* **send_datagram(host,port,message)** : \n   create a socket, send mesage and close socket (can't receive a reply)\n* **send_datagram_on_socket(socket,host,port,message)** : \n  use an existant socket for send a message to ip:port\n  the existant socket should be one created by a current UDPAgent timer or server\n\nMessages\n===\nServeur and client ```MServerAgent``` and ```MClientAgent``` define a 'agent ' which\ncan communicate by messages.\n\nMessages are ```ruby-data.inspect```, max size is 1 MByte.\n* MClientAgent.run(host,port) : connect to a MServerAgent, for communicate by message\n* MServerAgent.run(port,host,max-connections) : message server\n\nin Agent, socket are extended with this capability :\n* socket.send_message(data)\n* socket.on_message { |data|  .... ; ret_data}  will send ret_data to sender if not nil\n\nSee test.rb for an example\n\nTODO\n==\n\n* Serial line\n* more socket primitive\n* messages Agent : use Marchal instead of inspect/eval ....\n\nTests case\n==\nA TCP proxy, debug tool (see samples/proxy.rb) :\n\n```ruby\nMServer.service(2200,\"0.0.0.0\",22) do |scli|\n  px 2, \"======== client Connected ========\"\n  srv=MClient.run_one_shot(\"ip\",2200) do |ssrv|\n     px 1, \"======== server Connected ========\"\n     ssrv.on_any_receive { |data| px 1,data; scli.print data }\n     scli.on_any_receive { |data| px 2,data; ssrv.print data}\n     ssrv.wait_end\n     scli.close rescue nil\n  end\n  scli.wait_end\n  srv.stop rescue nil\nend   \ndef px(sens,data)\n  data.each_line {|line| puts \"#{(sens==1 ? \"\u003e \" : \"\u003c \")}#{line.chomp}\"\nend\n\n```\n\n\n\n\nTest serial **protocole-like** : header/body =\u003e ack/timeout:\n\n* client send \u003clength\u003e\u003cdata\u003e , wait one char acquit or timeout\n* serveur receive heade( size: 4 chars) , then data with size, send ack\n\n\n```ruby\n   \nsrv=MServer.service(2200,\"0.0.0.0\",22) do |socket|\n  socket.on_n_receive(4) { |data| \n     size=data.to_i\n     data=socket.recv(size)\n     puts \"  Server recieved buffer : #{data.inspect}\"\n     if rand(100)\u003e50\n        socket.print(\"o\") \n     else \n        puts \"  !!!non-ack by serv\"\n     end\n  }\n  socket.on_timer(40*1000) { puts \" serv close after 40 seconds\"; socket.close }\n  socket.wait_end\nend   \n\nMClient.run_one_shot(\"localhost\",2200) do |socket|\n   10.times { |j| \n\t   size=rand(1..10)\n\t   puts \"Sending #{size} data...\"\n\t   data='*'*size\n\t   socket.print \"%04d\" % size\n\t   socket.print data \n\t   p socket.received_timeout(1,100)  ? \"ack ok\" : \"!!! timeout ack\"\n   }\n   p \"end client\"\nend.join\n\n\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglurp%2Fminitcp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglurp%2Fminitcp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglurp%2Fminitcp/lists"}