Giter Club home page Giter Club logo

middleware's Introduction

FFI & NTNU Informatics Bachelor project collaboration 2023 - Network communication middleware

IT2901 Informatics Project II - NTNU & FFI: Network communication middleware

System Architecture

System architecture diagram

As shown in the diagram, the middleware exposes an API which enables sending and receiving. The API is split between endpoints for reliable and unreliable communication. Despite what is shown in the diagram, the API is only byte stream based when using reliable communication, while the unreliable part of the API is message based. With unreliable, the messages are divided into an ordered list of ”data packets” (fragments) to fit with the Maximum Transmission Unit (MTU) of the network (shown as ”Split” and ”Merge”). To reassemble the fragmented packets at the receiving end, a header is prepended with a unique identifier when sending, and packets are reordered using this information when receiving (shown as ”Add header, Number” and ”Remove header, Reorder”). When reliable communication is used, the byte stream is sent and received using TCP, using the configured TCP options in the config file. For both the reliable and unreliable configurations it is also possible to set the timeout for blocking operations, as well as the TOS value, through the API.


API documentation

Our middleware aims to be equivalent to the Python socket API in the functions it implements, as listed below.

After importing the API, we begin our journey by initializing either a MiddlewareReliable or a MiddlewareUnreliable object. Both objects have a constructor that accepts an MTU. This MTU is by default set to the value loaded from the middleware config file, but can be overridden per socket by this argument.

MiddlewareReliable.connect(address)

Connect to a remote socket at address. (The format of address depends on the address family — see above.)

If the connection is interrupted by a signal, the method waits until the connection completes, or raises a TimeoutError on timeout, if the signal handler doesn’t raise an exception and the socket is blocking or has a timeout. For non-blocking sockets, the method raises an InterruptedError exception if the connection is interrupted by a signal (or the exception raised by the signal handler).

Raises an auditing event socket.connect with arguments self, address.

 

MiddlewareReliable.send(bytes)

Send data to the socket. The socket must be connected to a remote socket. The optional flags argument has the same meaning as for recv() above. Returns the number of bytes sent. Applications are responsible for checking that all data has been sent; if only some of the data was transmitted, the application needs to attempt delivery of the remaining data. For further information on this topic, consult the Socket Programming HOWTO.

 

MiddlewareReliable.sendall(data)

Send data to the connected remote socket. This differs from MiddlewareReliable.send in that it blocks until the entire payload (data) is sent or the operation is timed out. Nothing is returned, and an exception is raised if the operation timed out (TimeoutError) or the operation would block on a non-blocking socket (BlockingIOError).

 

MiddlewareUnreliable.sendto(data, address)

Send data to a remote socket with the specified address. An exception is raised on timeout (TimeoutError) or if the operation would block on a non-blocking socket (BlockingIOError).

 

MiddlewareReliable.listen([backlog])

Enable a server to accept connections. If backlog is specified, it must be at least 0 (if it is lower, it is set to 0); it specifies the number of unaccepted connections that the system will allow before refusing new connections. If not specified, a default reasonable value is chosen.

 

MiddlewareReliable.bind(address)

MiddlewareUnreliable.bind(address)

Bind the socket to address. The socket must not already be bound. (The format of the address is a tuple: (IP, port))

 

MiddlewareReliable.accept()

Accept a connection. The socket must be bound to an address and must be listening for connections. The return value is a pair (conn, address) where conn is a new socket object usable to send and receive data on the connection, and address is the address bound to the socket on the other end of the connection.

 

MiddlewareReliable.recv(buffer_size)

Receives data from an accepted connection. The amount of data returned is less than or equal to the provided buffer_size. An exception is raised on timeout (TimeoutError) or if the operation would block on a non-blocking socket (BlockingIOError).

 

MiddlewareUnreliable.recvfrom()

Received data and sender address for a received MiddlewareDatagram. An exception is raised on timeout (TimeoutError) or if the operation would block on a non-blocking socket (BlockingIOError).

 

MiddlewareReliable.settimeout(timeout_s)

MiddlewareUnreliable.settimeout(timeout_s)

Sets the timeout for blocking operations to timeout_s seconds. A value of None makes all blocking operations block, a value of 0 makes all blocking operations non-blocking and raise an exception (BlockingIOError) if it would block. A value > 0 will result in an exception (TimeoutError) when a blocking operation takes more than timeout_s seconds to complete.

 

MiddlewareReliable.gettimeout()

MiddlewareUnreliable.gettimeout()

Gets the currently set timeout value for blocking operations.

 

MiddlewareReliable.set_tos()

MiddlewareUnreliable.set_tos()

Sets the TOS value to be sent with outbound packets. When tcp_ecn is enabled (see system_config.ini) TOS values will be rounded down to the nearest multiple of 4.

 

MiddlewareReliable.get_tos()

MiddlewareUnreliable.get_tos()

Gets the currently set TOS value to be sent with outbound packets.

 

MiddlewareReliable.get_mtu()

MiddlewareUnreliable.get_mtu()

Gets the currently set MTU. The socket will never send an IP packet larger than this size.

 

MiddlewareReliable.get_mss()

Gets the currently set MSS (Maximum Segment Size). This is equal to MTU - TCP/IP headers, and is the maximal payload carried by a single TCP segment.

 

MiddlewareUnreliable.get_mss()

Gets the currently set MSS (Maximum Segment Size). This is equal to the current MTU - total header size. No UDP datagram will be sent with a larger payload than this size. This is applies to each individual fragment sent as a UDP datagram, and is different to MiddlewareUnreliable.get_max_payload_size(), which applies to a MiddlewareUnreliable datagram (which is composed of fragments, each fragment is sent as a separate UDP datagram).

 

MiddlewareUnreliable.get_max_payload_size()

Gets the maximum payload a single call to MiddlewareUnreliable.sendto() can handle. This is based on the minimum possible MTU (64 bytes) and the number of fragment index bits (currently 11), which yields a maximum payload size of 63488 Bytes. Connections with a larger MTU could effectively exceed this, for safety, all payloads for all MTU sizes are required to be less than this size.

Example usage

To get started, here is a code snippet to show how to interact with the API and set up basic communication using: Make sure the import path to middleware.middlewareAPI is correct depending on wherer your project is.

Unreliable:

    from middleware.middlewareAPI import *

    mwSend = MiddlewareUnreliable()
    mwReceive = MiddlewareUnreliable()
    mwReceive.bind(("", 5005))
    mwSend.sendto(b"Hello", ("localhost", 5005))
    dataReceived = mwReceive.recvfrom()[0]

    print(dataReceived)

    mwSend.close()
    mwReceive.close()

Reliable: Reliable communication requires at least two threads to function if testing on a single machine, since one will need to connect while the other one accepts.

    from middleware.middlewareAPI import *
    import threading

    def waitForPacket(mwSocket):
        conn, addr = mwSocket.accept()
        assert conn.recv(1024) == b"Hello there"
        conn.send(b"General Kenobi")
        conn.close()

    mwReceive = MiddlewareReliable()
    mwSend = MiddlewareReliable()

    mwReceive.bind(("", 5001))
    mwReceive.listen()
    thread = threading.Thread(target=waitForPacket, args=(mwReceive,))
    thread.start()

    mwSend.connect(("localhost", 5001))
    mwSend.send(b"Hello there")

    received = mwSend.recv(1024)
    print(received)

    thread.join()
    mwReceive.close()
    mwSend.close()

Configuration options

Configuration is split into two categories: middleware configuration and system configuration. Each category has an INI file associated with it in the root directory of the repository.

System/TCP Configuration

System configuration must be applied manually by calling the script "configure_system.py" with elevated privileges. This configures the TCP/IP stack according to system_config.ini, and must be done once when installing the middleware, and after changing any values in the system_config.ini file. Each option is documented in the INI file. This script might not work on all systems. If this is the case, the following commands can be used (with elevated privilages) instead of running the script:

These commands will allow sockets to use tcp vegas as their congestion algorithm (the congestion algorithm that the middleware will use is chosen in the middleware config, see below), if both the script, and these commands, do not work then the congestion control can only be set to either reno or cubic.

# This will load tcp vegas as a kernel module, making it possible to use it as congestion control
modprobe tcp_vegas

# This will force tcp vegas to be loaded and possible for sockets to use. This is done by setting the
# default congestion control to vegas, which is then reset to reno afterwards
sysctl -w net.ipv4.tcp_congestion_control=vegas
sysctl -w net.ipv4.tcp_congestion_control=reno

# This will allow sockets to use reno cubic and vegas as congestion algorithms
sysctl -w net.ipv4.tcp_available_congestion_control="reno cubic vegas"
sysctl -w net.ipv4.tcp_allowed_congestion_control="reno cubic vegas"

Each option in the system_config.ini file can be set manually by calling

# NAME is the name used in system_config.ini, VALUE is the value wanted to be set. Strings with spaces can be used by surrounding the string with quotes (")
sysctl -w net.ipv4.NAME=VALUE

Middleware Configuration

Middleware configuration is applied automatically when the middleware is loaded as a python module. As opposed to system configuration, the middleware will try to load the config from any file named "middleware_config.ini" in the current working directory of the process that imported the middleware module, with the middleware_config.ini in the root directory as fallback. Each option available is documented in the middleware_config.ini file in the root directory. Note that all variables must be set by a middleware config INI file, and there may only be one section called "middleware_configuration". It is advised to copy to middleware_config.ini file in the root directory and modify this, intead of writing it from scratch.

Currently, the following options are available in the config file:

  • mtu: default MTU assigned to each socket
  • fragment_timeout: amount of time to wait after receiving new fragments before discarding the whole datagram
  • congestion_algorithm: the congestion algorithm to use for MiddlewareReliable sockets, must be one of the allowed algorithms set in system configuration
  • echo_config_path: if true the middleware will print the path of which middleware config file it loaded during initialization

A number of preconfigured configuration files are provided, which can be used instead of creating custom configurations from scratch.

Network configuration Transfer rate Latency Packet loss Configuration file
CNR 10kbps 100ms 5% cnr_config.ini
NATO narrow-band waveform 16kbps 500 ms 0% nato_narrowband_satcom_config.ini
SATCOM 250kbps 550ms 0% nato_narrowband_satcom_config.ini
Tactical broadband 2Mbps 100ms 1% fast_config.ini
Low-band 5G 100Mbps 20ms 0% fast_config.ini

These configuration files can be found in the root folder of the project.

Test service

A test service for tuning the middleware was developed. A detailed readme documenting it is provided here

Using the middleware in custom applications

A barebones example of how to use poetry is shown in example-service/. This can be used as a starting point to create applications on top of the middleware. Details for how to do this are explained in detail in the example-service README.

middleware's People

Contributors

kurumiiw avatar mollersthrane avatar olavfla avatar soimn avatar thorbjoernl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

middleware's Issues

Increase deployability

Increase ease of deployment

Summary

Relates to #16. The Middleware should be deployable easily, by non-developers. Documentation should be at a level where someone outside the dev-team can use the middleware. Deploying the middleware and getting a test service up should be relatively quick, not requiring too many commands.

Perhaps some shell scripts for deploying each service (which does poetry set up etc.)

Tasks

What tasks should be solved, for this issue to be complete?

  • Implement whatever

Tests

What test cases should be implemented for this issue to be completed?

  • Test whatever

Notes

Anything else?

Fragmentation

Fragmentation of large packets

Summary

When trying to send data that's larger than the configured MTU, the middleware should fragment the packet and send multiple packets through the socket. This is not something the socket should do, but rather the middleware.

Tasks

  • MTU
  • Fragmenting the data (chopping it up)
  • Assembling the data (putting it back together)
  • Creating multiple packets
  • Inserting headers? (If using UDP, insert proprietary header)
  • Buffering of packets waiting to be sent

Tests

  • Using configured MTU
  • Correctly fragments data
  • Correctly assembles data
  • Headers inserted in each packet
  • All buffered packets sent

Notes

It could be interesting to think about how this should be represented in the header of the packet.

Pytest random failure when running in Github actions, even though they work locally.

Pytest random failure when running in Github actions, even though they work locally.

Summary

It appears that pytest randomly fails when running on Github Actions sometimes. This occurs even if the tests run fine locally.

If this happens you will see a Timeout error in the pytest output of the failing task.

A workaround for now is to simply rerun the test until it succeeds.

Tasks

  • Implement whatever

Tests

What test cases should be implemented for this issue to be completed?

  • Test whatever

Notes

Anything else?

Verify that setting maxseg controls maximum packet size

TCP maxseg might not control packet size

Summary

In testing related to the Emil issue, we discovered that the middleware seemed to disregard configured MTU when using reliable connection. This might be bacause of Thorbjørns whacky OS (not ubuntu), but worth investigating.

Tasks

Tests

What test cases should be implemented for this issue to be completed?

  • Test Emil service and monitor with Wireshark with Ubuntu

Notes

It is also possible to set interface MTU with "sudo ifconfig <interface> mtu <mtu>", but this will restrict MTU for all traffic across the interface, not just MW connections. This is also capped to a minimum of 1280 if ipv6 is enabled.

Reliable communication

Reliable communication

Summary

The middleware needs to be able to use reliable communication, like TCP, for services that need it.

Tasks

  • Retransmission
  • ACKs
  • Buffering and receive / send windows
  • Detect packet loss

Tests

  • Transmission of data and ACKs
  • Data gets properly designated as ACKed
  • Retransmission in poor networks according to configuration

Notes

Expand on this description

Testing with netem

Testing with netem

Summary

During this sprint we need to start testing the API and service using netem (and possible Scapy?)

Tasks

  • Set up netem and send normal data
  • Add more tasks when we know more.

Tests

This is a "test" in and of itself, so it doesn't make sense to test the test, so to speak.

Notes

Should test all different kinds of situations that one might encounter:

  • complete disconnects and buffering for it to send at "a later date"
  • frequent disconnects
  • very slow bandwidths
  • high latencies
  • more?

I have a rack server that I can use for testing, going to look into this. - Ola J.

API documentation in README

Documentation of the API in the README file

Summary

Short summary of the issue.

Tasks

  • Architecture and how the middleware works
  • Technical description of the fragmentation header / proprietary information
  • Description of endpoints and what they do
  • Example code snippet
  • Configuration options and what they do / how it interacts with the underlying system.

Fix hardcoded config.ini path

Fix hardcoded config.ini path

Summary

Currently we load config.ini from a hardcoded path ("middleware/configuration/config.in"). This only works when running the program from the ./middleware/ folder, and otherwise it will error out the ass.

We should support a commandline parameter to set the configuration file path eg. "--config config.ini", and fall back to "config.ini" in the current working dir, if not provided. This relates to #26 #31.

Tasks

What tasks should be solved, for this issue to be complete?

  • See above.

Tests

What test cases should be implemented for this issue to be completed?

  • Test whatever

Notes

Anything else?

Improved fragmenting

Improved fragmenting

Summary

Including the entire final fragment number with each packet is inefficient, and increases overhead while limiting the number of fragments we can include in a sequence. It should instead be possible to indicate the final fragment in a sequence by setting a single bit flag instead (as is done in IP fragmentation).

Secondly, the current reassembly code is somewhat fragile, as it requires a list of fragments presorted by identification to successfully reassemble a packet. It would be nice if the fragmenting module could handle processing fragments, and any other part of the program just needs to send any mix of fragments to this module (even if incomplete), and receive finished packets back whenever they are ready.

Tasks

  • Implement the new header format.
  • Improved reassembly.
    • FragmentSequence class - For storing partially reassembled packets.
    • Reassembly of mixed packets / fragments and mixed identification.
    • Configurable timeout of old fragments.
    • Separate FragmentSequence() instance for each sender.
      • Sender identified by IP+port.
    • Collect statistics about discarded fragments, and provide interface to query them (e.g. percentage of packets lost due to missing fragment during reassembly).
  • Integrate with changes from #6
  • Refactoring
    • Be consistent with naming. Packet id and identification is both used interchangably currently. Borrow the "identification" label from IP fragmentation and use it everywhere.
    • Move the public interface for fragmentation and reassembly to its own class: Fragmenter class.

Tests

  • Test cases for various fragmentation and reassembly scenarios, using the new interface.
    • Simple ordered fragmentation/reassembly.
    • Unordered fragmentation/reassembly.
    • Mixed packet fragmentation/reassembly.
    • Mixture of many packets/fragments reassembly in multiple calls to Fragmenter.process_packets(): Should represent a realistic scenario of real world use.

Notes

New header format:

image

Questions

  • Do we want configurable timeout of packages on a per destination application basis?
  • Memory usage, buffering a lot of partial packets will eat memory. Have to experiment with timeout.

Config initialization

Config initialization

Summary

The config system must be initialized before use. "load_config_from_file" must therefore be called at middleware startup both during actual use and testing. Currently this is not done at middleware startup, and during testing it is only done when the config system is tested.

Tasks

  • Decide on when the config should be loaded and actually load it
  • Decide on what config should be used during testing and ensure the corrent one is loaded before tests (except config tests) are run
  • Ensure no config is loaded before the config tests are run

Tests

  • Tests actually work when the config is both loaded and used

First product release

First product release

Summary

First product release should be created on Github, and made available.

Tasks

What tasks should be solved, for this issue to be complete?

  • Implement whatever

Tests

What test cases should be implemented for this issue to be completed?

  • Test whatever

Notes

Anything else?

API for services

API outline

Summary

To be able to use the middleware and test it, there needs to be a basic outline of an API that services can use.

Tasks

  • Sending endpoint
  • Receiving endpoint / wait call?
  • Decide on configuration endpoints and meta-information about the network to be communicated with the service

Tests

  • Sending small data, like text
  • Sending large data, image?
  • Receive text / image data

Notes

This should be expanded upon later, but for sprint 3 we need to have something basic for sending and receiving.

Test service

Test service

Summary

Configurable test service for sending a lot of random data according to some configuration (eg. lots of small packets/a few big packets / reliable/unreliable / burst/regular interval. For received data, send back a hash of the data to source to allow for verification. Collect statistics about the provided service, and report it.

Should allow for systematic testing of many network patterns, and realistic sending patterns.

Tasks

  • Controller
    • Load test configuration from config file and configure worker(s).
      • Simple port discovery when connecting to workers.
      • Configuration should support setting a distribution of packet size to send, what type of connection to use (reliable/unreliable) and whether traffic should be spaced evenly or in bursts.
    • Run tests and collect statistics.
    • Reporting of test results
      • RTT, packet lost, retransmission overhead, amount of data received/sent on either end, etc.
      • Console
      • HTML report (??)
  • Worker
    • Receive and set configuration from Controller
    • Echo data as well as measured statistics to Controller.

Tests

  • Verify that the correct data is being sent and received
  • Verify correct statistics

Notes

Anything else?

Use service to send and receive data

Use service to send and receive data

Summary

Instead of using some testing code or built-in testing, we should use an actual service to send and receive data to start using the API early.

Tasks

  • Build easy, low effort service to use for sending and receiving e.g. text

Tests

  • API endpoint connection works as intended
  • Takes user input
  • Gives expected output from network

Notes

None.

TOS field not set properly

TOS field not set properly

Summary

When calling setsockopt(IPPROTO_IP, IP_TOS, tos), getsockopt(IPPROTO_IP, IP_TOS) does not reflect this value on tcp sockets.

Tasks

What tasks should be solved, for this issue to be complete?

  • Set TOS field on tcp sockets

Tests

What test cases should be implemented for this issue to be completed?

  • Ensure TOS is set correctly and that both get_tos and outbound packets reflect this value

Multicast support

Multicast support for the middleware

Summary

It would be nice for the middleware to have support for multicast over networks that might not support it natively.

Tasks

  • Creation, configuration and leaving of multicast groups
  • Support for multicast (duh)

Tests

  • Test that all clients in the group get the data

Notes

Not to my knowledge.

Pakker kommer ikke frem med reliable sending av video

Har testet mellomvaren med en video tjeneste, men jeg har litt problemer med å sende når jeg bruker MiddlewareReliable. Det virker som at noen av pakkene ikke kommer frem til den tjenesten som skal motta, som gjør at videoen ikke blir helt jevn. Dette er testet mellom to maskiner på samme nettverk. Testing med MiddlewareUnreliable har fungert uten problemer.

Jeg har lagt ut koden til video tjenesten på Github som dere skal ha tilgang til (den er public). Det er bare å bruke denne til testing og gjøre endringer dersom dere trenger det. For å hente inn mellomvaren brukte jeg poetry som beskrevet i example-service. Det er bare å si fra hvis det er noen problemer med oppsettet.

https://github.com/EPA1/simple_video_service

Documentation

Documentation

Summary

Make sure that all documentation in the product is up to date. This includes docstrings, readmes, etc.

Tasks

  • Readme for middleware.
    • Add a paragraph about the preconfigured configs, perhaps just table 9 from the report.
  • Readme for example service.
  • Docstrings.
  • Readme for test-service.
  • Licence (MIT).
  • Reference the documentation in the report.

Set TOS in packet

Set TOS in packet

Summary

The middleware must be able to set the Type-of-Service (TOS) field of the IP header.

Tasks

  • Implement a way to set TOS of a packet

Tests

  • Check if TOS is set (Wireshark)

Notes

Configuring the TOS field from a service comes later, in sprint 4.

Bug in fragmenting packet when size<MTU

Bug in fragmenting packet when size<MTU

Summary

Fragment.fragment fails when attempting to fragment a packet that doesn't get fragmented because its size is smaller than the MTU. In this case intended behaviour is that the original packet is returned.

Tasks

  • Fix bug where fragment() does not return original packet when packet size<MTU.

Tests

  • Add testcase to verify that fragmenting a packet with size<MTU returns the original packet as intended.

Notes

Anything else?

Send and receive data using TCP

Send and receive data using TCP

Summary

Implement a program that can send and receive TCP packets using the decided python package.

Tasks

  • Decide on which python socket (scapy/sockets) library to use.
  • Send some data.
  • Receive data.

Tests

  • TBD

TCP tuning

TCP tuning

Requires #27

Summary

For release 2, FFI wanted a tuned TCP to begin testing on their NATO radio emulator. This needs to be done in order to start satisfying their needs.

Tasks

  • Test different TCP settings

Tests

Nothing; this is research based and should only result in findings about TCP and network environment variables. For testing of the demo suite, see #27.

Defragmentation bug

Defragmentation bug

Summary

Our current custom header makes it impossible to defragment the packets on the receiving end, as we do not know where one packet ends and another begins in the bytestream. To fix this the header must include a length field so we can identify where one packet ends and another begins.

Tasks

  • Expand the header with a 2byte length field. Must also be included in the Packet header, not just fragment header.
  • Adapt the middlewareApi to make use the length field for defragmentation.

Tests

  • Test that it works correctly.
  • Either rewrite existing tests, or add new ones that check that checks that the entire fragmentation process works, that is "data->packet->fragments->bytestream->fragments->packet->data. Current tests, do not check the middle part.

Notes

New header format.
image

Docker image setup

Before we begin working with any code, it's probably a good idea to set up a Docker image, or some sort of easy deployment method, as per the request of FFI.

Failing tests due to default configuration as of merging #49

Failing tests due to default configuration as of merging #49

Summary

Pipeline fails due to missing Vegas congestion algorithm on Github's Ubuntu workers (see comment in #49).

Tasks

  • Fix the problem by (a) changing the default configuration to use a congestion algorithm available on the worker or (b) use a test-specific custom configuration.

Tests

What test cases should be implemented for this issue to be completed?

  • Test whatever

Notes

Anything else?

Architecture diagram

We need a good, high-level diagram of the architecture of the project. Will be useful for the report as well. NEEDS to be done by prelim. deadline!

Network information configuration

Network information configuration and service info

Summary

Through file config and/or API, the middleware needs to be able to set different parameteres about how it behaves and what it should know about the network as a whole.

Tasks

Configuration for:

  • MTU
  • Retransmission delay

Config for expected:

  • Congestion
  • Bandwidth
  • Packet loss
  • Jitter?

The service must configure:

  • TOS-value

Tests

  • Configuration getting applied as intended

Notes

Expand tasks when we know more.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.