Shrapnel is a scalable, high-performance cooperative threading library for Python.

Overview

This Python library was evolved at IronPort Systems and has been provided as open source by Cisco Systems under an MIT license.

Intro

Shrapnel is a library for high-performance concurrency. It uses coroutines to implement user threads on top of either kqueue (FreeBSD, OS X) or /dev/epoll (linux), and is written mostly in Pyrex/Cython, supporting both 32-bit and 64-bit platforms. It is the culmination of about 8 years of work at IronPort Systems, a provider of high-speed mail appliances. It was open-sourced by Cisco Systems in late 2011.

Status

Apr 18, 2013: I've recently merged in a long chain of branches for several important features:

  • Support for pure-cython servers (branch 'pxdfix')
  • Full DNS resolver implementation (branch 'dns-cache')
  • Updated postgres support (branch 'postgres')
  • Included OpenSSL support

Features

  • Lightweight threads, event-driven scheduler.
  • Underneath: non-blocking operations on descriptors, like sockets and pipes.
  • On top, synchronous API for straight-line, simple code.
  • Highly scalable - tens or hundreds of thousands of connections/threads.
  • Thread synchronization primitives, like mutexes, semaphores, etc...
  • with_timeout(): wrap any funcall with a timeout.
  • Wait on kqueue events like file/directory changes, signals, processes, etc... [kqueue only]
  • DNS resolver and cache
  • HTTP server and client (plus WebSocket, RFC6455 & hixie-76)
  • Support for TLS via tlslite and openssl (plus NPN for both)
  • other protocols/codecs: ldap, asn1, ftp, mysql, postgres, AMQP.
  • MIT License.

Advantages

Compared to other concurrency packages available for Python, Shrapnel gives you:

  • Speed and Efficiency: the entire scheduler, poller, socket layer, synchronization objects, etc... are written in Cython, with an emphasis on performance and low memory usage.
  • Stock Python: Shrapnel works with out-of-the-box CPython [2.X]. No special variants of Python are needed, it will even work with your OS's OEM python installation. So you can use all the external libraries/modules you've come to rely on.
  • No Callbacks: no need to cuisinart your application into a thousand callbacks. No need to decompose every action into a state machine. Write simple, performant code now without having to send your programmers to class.
  • Drop to Cython for speed: all the capabilities of the system are available from Cython, so you can e.g. write a server entirely in Cython for speed. You can interface with external libraries, and do thread switches from Cython or C. It's even possible to have external C code call back into shrapnel. This makes it easy to prototype your application in Python, and then push only the hot spots into Cython.
  • Timeouts: Shrapnel provides a general timeout mechanism that can be used to wrap any function call with a timeout.
  • Profiler: Thread-aware profiler generates HTML reports.

Tutorial

See http://ironport.github.com/shrapnel/tutorial.html

API Documentation

See http://ironport.github.com/shrapnel/

Comments
  • Segmentation fault while running example with coro

    Segmentation fault while running example with coro

    I have tried to execute that example: https://github.com/ironport/shrapnel/blob/master/coro/http/demo/jsonrpc_server.py and got error:

    $ python env/http_server.py FIX ber.py to use coro.asn1.ber Segmentation fault

    Then I tried run in gdb:

    (gdb) run Starting program: /str/development/projects/open-source/shrapnel/env/bin/python env/http_server.py warning: Could not load shared library symbols for linux-vdso.so.1. Do you need "set solib-search-path" or "set sysroot"? [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". FIX ber.py to use coro.asn1.ber

    Program received signal SIGSEGV, Segmentation fault. 0x00007ffff675e875 in _wrap1 () from /str/development/projects/open-source/shrapnel/env/lib/python2.7/site-packages/coro-1.0.3_000-py2.7-linux-x86_64.egg/coro/_coro.so (gdb) c Continuing.

    Program terminated with signal SIGSEGV, Segmentation fault. The program no longer exists. (gdb)

    opened by atykhonov 7
  • Darwin/OSX 10.6.8 with default gcc builds swap.c:wrap0 incorrectly.

    Darwin/OSX 10.6.8 with default gcc builds swap.c:wrap0 incorrectly.

    My hack that checks for llvm doesn't appear to work with pre-Lion gcc. That gcc reports itself as:

    gcc --version i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)

    But requires the 16-byte offset, not the 8-byte offset. Perhaps it's not llvm that distinguishes the different offset, but the platform?

    This leads to the immediate segfault. Anyone have a pre-Lion machine to test this on?

    opened by samrushing 7
  • Modified sock.send() to support memoryview objects.

    Modified sock.send() to support memoryview objects.

    This is to fix socket._fileobject.write() method. It was failing because _fileobject.flush() that is eventually called after the _fileobject.write() passes data to sock.sendall() as a memoryview object.

    opened by sgalaburda 5
  • sslip module needed.

    sslip module needed.

    Hello Guys,

    I found that sslip module (wrapper for the OpenSSL library) is missing in the shrapnel code tree. Because of this fact the coroutine/coro_ssl.py module is broken and I cannot use SSL connections with shrapnel. If the module was forgotten when shrapnel went open source please add it. If it was decided not to post the sslip here for some reason please let me know.

    Thanks.

    opened by sgalaburda 5
  • Remove aplib dependency

    Remove aplib dependency

    The dependency on aplib causes a lot of complexity with installation. It would be best to remove the dependency, and the drawback of copying some stuff into shrapnel.

    opened by ehuss 4
  • Fixed ticks per second detection for Linux.

    Fixed ticks per second detection for Linux.

    The ticks per second value was hard-coded in tsc_time.pyx module. This caused the time in shrapnel-based program to notably diverge from the system time. I implemented detection of actual ticks per second based on CPU frequency from /proc/cpuinfo.

    opened by sgalaburda 3
  • stub_resolver.install doesn't install until reload(coro)

    stub_resolver.install doesn't install until reload(coro)

    I tried using stub_resolver yesterday and I noticed that when you do stub_resolver.install(), the_resolver doesn't change until reload(coro).

    This is a python behavior but I am wondering if we can workaround it in a better way.

    from coro.dns import stub_resolver stub_resolver.install(['8.8.8.8']) import coro coro.the_resolver <coro._coro.dummy_resolver object at 0x8015590b0> reload(coro) coro.the_resolver <coro.dns.stub_resolver.stub_resolver instance at 0x801a1ed40>

    opened by halayli 3
  • s2n implementation.

    s2n implementation.

    Note: this code will not work correctly until https://github.com/awslabs/s2n/issues/165 is addressed. If you would like to test it now, use samrushing/s2n to get the 3-line patch.

    Only the most basic of testing has been done. Using s2n/test/{serve.py,openssl_client.py} I can open multiple client connections and funnel about 8MB/s through two clients simultaneously. No client testing from s2n has been tried yet.

    opened by samrushing 2
  • asn1: BER/DER codec does not support multi-byte tags.

    asn1: BER/DER codec does not support multi-byte tags.

    Single-byte tags are limited to the range 0-30. When used in a protocol implementation with the APPLICATION flag set, this limits the number of different packet types that can be flagged in the protocol. While 31 types should be enough for most protocols, it precludes the ability to use a 'private' range of tags (e.g. tags > 100 indicate private protocol extensions).

    Unfortunately the assumption that tag & flags fit into a single byte was pretty heavily built in to the codec, so fixing it required sweeping changes. I've placed this work into the multibyte-tag branch.

    But I'd actually prefer to not use that code. I have a different code base that uses a nifty trick for encoding that will give much higher performance (by avoiding lots of copying). This different code base also has extensive auto-generated tests.

    opened by samrushing 2
  • coro build fails on Ubuntu 14.04

    coro build fails on Ubuntu 14.04

    Python 2.7 Cython 0.20.1

    $ python setup.py build
    

    Error compiling Cython file: ... cdef int scope_id cdef addrinfo * ai if len(address) == 4: # as per python return value from getaddrinfo() and arg to connect() ip, port, flowinfo, scope_id = address sin6.sin6_flowinfo = htonl (flowinfo) ^ coro/socket.pyx:830:16: Object of type 'sockaddr_in6' has no attribute 'sin6_flowinfo'

    and similar errors for 'sin6_scope_id' eventually terminating in:

    building 'coro._coro' extension creating build/temp.linux-x86_64-2.7 creating build/temp.linux-x86_64-2.7/coro x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/home/venzen/sourcecode/shrapnel/. -I/home/venzen/sourcecode/shrapnel/include -I/usr/include/python2.7 -c coro/_coro.c -o build/temp.linux-x86_64-2.7/coro/_coro.o -Wno-unused-function coro/_coro.c:1:2: error: #error Do not use this file, it is the result of a failed Cython compilation. #error Do not use this file, it is the result of a failed Cython compilation. ^ error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

    _Using Python 3.4 results in errors with building package distribute egg

    opened by venzen 2
  • Make coro.TimeoutError subclass Exception

    Make coro.TimeoutError subclass Exception

    The _coro.coro.TimeoutError class inherits from _coro.coro.Interrupted, which, in turn, inherits from BaseException. Simple Exception is not subclassed. Therefore, timeouts cannot be trapped so:

        try:
            coro.with_timeout(timeout, method)
        except Exception, unused:
            handle_exception()
    

    Instead, one must use:

        try:
            coro.with_timeout(timeout, method)
        except (Exception, TimeoutError), unused:
            handle_exception()
    

    According to documentation, BaseException is not to be directly inherited by user-defined classes. Likewise, BaseException is used in terminal situations which are not meant to be (normally) intercepted by the user. For example, GeneratorExit is a subclass of BaseException, while StopIteration subclasses Exception. Users are expected to handle StopIteration, while GeneratorExit is not often observed in userland.

    Generally, I think timeout situation should not be considered as a terminal condition. If there is a possibility to impose an explicit timeout on a some operation, I think it should be possible to trap and handle the related exception on the same level, as anticipated, and not terminal, situation.

    opened by ghost 2
  • Getting Build Error with Python 3.7

    Getting Build Error with Python 3.7

    Hello, I am getting Build Error while compiling with Python3.7. openssl Extension was compiling properly with Python3.5 at least. currently i uninstall Python3.5 and install Python3.7, not sure if any thing i got miss .kindly help me to find the issue

    Error: ^ coro/ssl/openssl.c: In function ‘__Pyx_GetException’: coro/ssl/openssl.c:25622:22: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_type’ tmp_type = tstate->exc_type; ^ coro/ssl/openssl.c:25623:23: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_value’ tmp_value = tstate->exc_value; ^ coro/ssl/openssl.c:25624:20: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_traceback’ tmp_tb = tstate->exc_traceback; ^ coro/ssl/openssl.c:25625:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_type’ tstate->exc_type = local_type; ^ coro/ssl/openssl.c:25626:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_value’ tstate->exc_value = local_value; ^ coro/ssl/openssl.c:25627:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_traceback’ tstate->exc_traceback = local_tb; ^ coro/ssl/openssl.c: In function ‘__Pyx_ExceptionSwap’: coro/ssl/openssl.c:25649:22: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_type’ tmp_type = tstate->exc_type; ^ coro/ssl/openssl.c:25650:23: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_value’ tmp_value = tstate->exc_value; ^ coro/ssl/openssl.c:25651:20: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_traceback’ tmp_tb = tstate->exc_traceback; ^ coro/ssl/openssl.c:25652:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_type’ tstate->exc_type = *type; ^ coro/ssl/openssl.c:25653:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_value’ tstate->exc_value = *value; ^ coro/ssl/openssl.c:25654:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_traceback’ tstate->exc_traceback = *tb; ^ coro/ssl/openssl.c: In function ‘__Pyx_ExceptionSave’: coro/ssl/openssl.c:25667:19: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_type’ *type = tstate->exc_type; ^ coro/ssl/openssl.c:25668:20: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_value’ *value = tstate->exc_value; ^ coro/ssl/openssl.c:25669:17: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_traceback’ *tb = tstate->exc_traceback; ^ coro/ssl/openssl.c: In function ‘__Pyx_ExceptionReset’: coro/ssl/openssl.c:25681:22: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_type’ tmp_type = tstate->exc_type; ^ coro/ssl/openssl.c:25682:23: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_value’ tmp_value = tstate->exc_value; ^ coro/ssl/openssl.c:25683:20: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_traceback’ tmp_tb = tstate->exc_traceback; ^ coro/ssl/openssl.c:25684:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_type’ tstate->exc_type = type; ^ coro/ssl/openssl.c:25685:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_value’ tstate->exc_value = value; ^ coro/ssl/openssl.c:25686:11: error: ‘PyThreadState {aka struct _ts}’ has no member named ‘exc_traceback’ tstate->exc_traceback = tb; ^ error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

    opened by vijaysharma136-git 3
  • Build fails on Ubuntu, MacOS

    Build fails on Ubuntu, MacOS

    It seems to have problem when building with cython 0.29.10 (currently installed by pip3). "pip3 install coro" failed as well as build from source by "python3 setup.py build. Platform: Ubuntu and MacOS (10.14.5) Python: 3.7.3

    opened by iapyeh 5
  • Build fails on fedora

    Build fails on fedora

    Error message is

    Error compiling Cython file:
    ------------------------------------------------------------
    ...
        """Raise an OSError exception by errno.
    
        :Parameters:
            - `error_number`: The errno value to raise.
        """
        map_exception(OSError(error_number, libc.strerror(error_number)))
                                               ^
    ------------------------------------------------------------
    
    coro/oserrors.pyx:54:44: cimported module has no attribute 'strerror'
    building 'coro.oserrors' extension
    gcc -pthread -fno-strict-aliasing -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -DNDEBUG -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/usr/include/python2.7 -c coro/oserrors.c -o build/temp.linux-x86_64-2.7/coro/oserrors.o
    coro/oserrors.c:1:2: error: #error Do not use this file, it is the result of a failed Cython compilation.
     #error Do not use this file, it is the result of a failed Cython compilation.
      ^~~~~
    error: command 'gcc' failed with exit status 1
    
    opened by darakian 3
  • /test/test_readv.py fails to run on Ubuntu and FreeBSD

    /test/test_readv.py fails to run on Ubuntu and FreeBSD

    chatty@ubuntu:~shrapnel/test$ python test_readv.py E

    ERROR: test_readv (main.Test) Test readv.

    Traceback (most recent call last): File "test_readv.py", line 113, in test_readv '01234567')) File "test_readv.py", line 77, in testit blocks = coro.with_timeout(5, s.readv, block_receives) File "coro/_coro.pyx", line 1111, in coro._coro.sched.with_timeout (coro/_coro.c:16399) TypeError: Argument 'size_list' has incorrect type (expected list, got tuple)


    Ran 1 test in 0.001s

    FAILED (errors=1) 7: Tue Dec 6 22:26:19 2016 thread 7 (<bound method TestSession.run of <main.TestSession instance at 0x7f1f252603f8>>): error '('coro/oserrors.pyx coro.oserrors.map_exception (coro/oserrors.c:874)|44', "<class 'coro.oserrors.EPIPE'>", '[Errno 32] Broken pipe', '[coro/_coro.pyx coro._coro._wrap1 (coro/_coro.c:12115)|772] [test_readv.py run|63] [coro/socket.pyx coro._coro.sock.send (coro/_coro.c:27626)|282] [coro/socket.pyx coro._coro.sock.send (coro/_coro.c:27500)|314] [coro/pyrex_helpers.pyx coro._coro.raise_oserror (coro/_coro.c:3879)|32] [coro/oserrors.pyx coro.oserrors.map_exception (coro/oserrors.c:874)|44]')' chatty@ubuntu:~shrapnel/test$

    opened by coder03 1
  • Failed to detect CPU frequency if run container (LXC) with fixed CPUs.

    Failed to detect CPU frequency if run container (LXC) with fixed CPUs.

    Ran into this issue using coro inside of an lxc container when cpu resources of the container were fixed to something other than the first few cores.

    When you assign the cpus to the container - they report back sequentially starting at , 0,1,2,3,4...depending on the number of cores assigned to the container ( in the /proc/cpuinfo file.)

    However when you get the current_cpu_number = sched_getcpu(), this returns the actual cpu on which the program is running. They don't match up, so cpu-found remains false - and the error below is produced.

    see tsc_time.pyx, lines 254-280

    import coro File "/root/traffic_gen/py27/local/lib/python2.7/site-packages/coro/init.py", line 25, in from coro._coro import * File "coro/clocks/tsc_time.pxd", line 25, in init coro._coro (coro/_coro.c:58161) File "coro/clocks/tsc_time.pyx", line 308, in init coro.clocks.tsc_time (coro/clocks/tsc_time.c:15190) File "coro/clocks/tsc_time.pyx", line 272, in coro.clocks.tsc_time.get_ticks_per_sec (coro/clocks/tsc_time.c:2119) RuntimeError: failed to detect CPU frequency

    opened by roadk1ll 1
Owner
null
PerfSpect is a system performance characterization tool based on linux perf targeting Intel microarchitectures

PerfSpect PerfSpect is a system performance characterization tool based on linux perf targeting Intel microarchitectures. The tool has two parts perf

Intel Corporation 139 Dec 30, 2022
guapow is an on-demand and auto performance optimizer for Linux applications.

guapow is an on-demand and auto performance optimizer for Linux applications. This project's name is an abbreviation for Guarana powder (Guaraná is a fruit from the Amazon rainforest with a highly caffeinated seed).

Vinícius Moreira 19 Nov 18, 2022
Pyccel stands for Python extension language using accelerators.

Pyccel stands for Python extension language using accelerators.

Pyccel 242 Jan 2, 2023
Sampling profiler for Python programs

py-spy: Sampling profiler for Python programs py-spy is a sampling profiler for Python programs. It lets you visualize what your Python program is spe

Ben Frederickson 9.5k Jan 1, 2023
Pearpy - a Python package for writing multithreaded code and parallelizing tasks across CPU threads.

Pearpy The Python package for (pear)allelizing your tasks across multiple CPU threads. Installation The latest version of Pearpy can be installed with

MLH Fellowship 5 Nov 1, 2021
A fast, scalable, high performance Gradient Boosting on Decision Trees library, used for ranking, classification, regression and other machine learning tasks for Python, R, Java, C++. Supports computation on CPU and GPU.

Website | Documentation | Tutorials | Installation | Release Notes CatBoost is a machine learning method based on gradient boosting over decision tree

CatBoost 5.7k Feb 12, 2021
A fast, scalable, high performance Gradient Boosting on Decision Trees library, used for ranking, classification, regression and other machine learning tasks for Python, R, Java, C++. Supports computation on CPU and GPU.

Website | Documentation | Tutorials | Installation | Release Notes CatBoost is a machine learning method based on gradient boosting over decision tree

CatBoost 6.9k Jan 5, 2023
An open source framework that provides a simple, universal API for building distributed applications. Ray is packaged with RLlib, a scalable reinforcement learning library, and Tune, a scalable hyperparameter tuning library.

Ray provides a simple, universal API for building distributed applications. Ray is packaged with the following libraries for accelerating machine lear

null 23.2k Dec 30, 2022
An open source framework that provides a simple, universal API for building distributed applications. Ray is packaged with RLlib, a scalable reinforcement learning library, and Tune, a scalable hyperparameter tuning library.

Ray provides a simple, universal API for building distributed applications. Ray is packaged with the following libraries for accelerating machine lear

null 23.3k Dec 31, 2022
High performance, easy-to-use, and scalable machine learning (ML) package, including linear model (LR), factorization machines (FM), and field-aware factorization machines (FFM) for Python and CLI interface.

What is xLearn? xLearn is a high performance, easy-to-use, and scalable machine learning package that contains linear model (LR), factorization machin

Chao Ma 3k Jan 3, 2023
High performance, easy-to-use, and scalable machine learning (ML) package, including linear model (LR), factorization machines (FM), and field-aware factorization machines (FFM) for Python and CLI interface.

What is xLearn? xLearn is a high performance, easy-to-use, and scalable machine learning package that contains linear model (LR), factorization machin

Chao Ma 2.8k Feb 12, 2021
High performance, easy-to-use, and scalable machine learning (ML) package, including linear model (LR), factorization machines (FM), and field-aware factorization machines (FFM) for Python and CLI interface.

What is xLearn? xLearn is a high performance, easy-to-use, and scalable machine learning package that contains linear model (LR), factorization machin

Chao Ma 3k Jan 8, 2023
Docker image with Uvicorn managed by Gunicorn for high-performance FastAPI web applications in Python 3.6 and above with performance auto-tuning. Optionally with Alpine Linux.

Supported tags and respective Dockerfile links python3.8, latest (Dockerfile) python3.7, (Dockerfile) python3.6 (Dockerfile) python3.8-slim (Dockerfil

Sebastián Ramírez 2.1k Dec 31, 2022
Python 3 tool for finding unclaimed groups on Roblox. Supports multi-threading, multi-processing and HTTP proxies.

roblox-group-scanner Python 3 tool for finding unclaimed groups on Roblox. Supports multi-threading, multi-processing and HTTP proxies. Usage usage: s

h0nda 43 May 11, 2022
python+PySimpleGUI+pyserial+threading

GUI_pyserial python+PySimpleGUI+pyserial+threading 功能 1.利用PySimpleGUI制作图形用户界面 2.利用threading实现多线程调用pyserial处理串口通信 模块版本 PySimpleGUI 4.46.0 pyserial 3.5

null 2 Dec 27, 2022
a script to bulk check usernames on multiple site. includes proxy & threading support.

linked-bulk-checker bulk checks username availability on multiple sites info people have been selling these so i just made one to release dm my discor

krul 9 Sep 20, 2021
Django CacheMiddleware has a multi-threading issue with pylibmc

django-pylibmc-bug Django CacheMiddleware has a multi-threading issue with pylibmc. CacheMiddleware shares a thread-unsafe cache object with many thre

Iuri de Silvio 1 Oct 19, 2022
Made with pygame. Multiplayer game using socket module and threading.

Rock Paper Scissor made with python-pygame. Poorly made, as a beginner in programming. Multiplayer with server code and client code provided.

AllenJo 1 Dec 29, 2021
Cedric Owens 16 Sep 27, 2022
A simple chat room using socket and threading for handle multiple connections.

• Socket Chat Room was a little project for socket study. It works with a server handling the incoming connections from the clients. Clients send encoded messages while waiting for others clients messages simultaneously. And the server receive all the messages and delivers to the other clients.

Guilherme de Oliveira 2 Mar 3, 2022