Skip to content

Commit

Permalink
update globals to server
Browse files Browse the repository at this point in the history
  • Loading branch information
Time-Coder committed Jan 28, 2024
0 parents commit ab97c79
Show file tree
Hide file tree
Showing 13 changed files with 509 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
29 changes: 29 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.7"
# You can also specify other tool versions:
# nodejs: "16"
# rust: "1.55"
# golang: "1.17"

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: doc/source/conf.py

# If using Sphinx, optionally build your docs in additional formats such as PDF
# formats:
# - pdf

# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: doc/requirements.txt
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
35 changes: 35 additions & 0 deletions make.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@ECHO OFF

pushd %~dp0

REM Command file for Sphinx documentation

if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build

if "%1" == "" goto help

%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)

%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end

:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%

:end
popd
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
sphinx_markdown_tables
sphinx_rtd_theme
recommonmark
setuptools
135 changes: 135 additions & 0 deletions source/Examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Examples

In this section, we will write 2 excition examples with **Connector** to see how it make complex things easy.

## Chat room

If you want to do make a chat room program, **Connector** can help a lot. Run following code on server computer:
```python
# server side
from Connector import *

def on_connect(client):
print(client.address, "is in.")

def on_disconnect(address):
print(address, "is out.")

server = Server("192.168.199.210", 1900) # Please input your server computer's real ip
server.set_connect_callback(on_connect)
server.set_disconnect_callback(on_disconnect)
while True:
message = server.get()
for client in server.clients:
if client.address != message["address"]:
client.send(message)
```

Run following code on each client computer:
```python
# client side
from Connector import *
from inputer import * # you need call 'pip install inputer'
import threading

client = Client()
client.connect("192.168.199.210", 1900) # Please input server computer's real ip
inputer = Inputer()

def recving(client, inputer):
while True:
try:
message = client.recv()
except BaseException:
break

inputer.print_before(message["address"], ": ", message["content"], sep="")

recving_thread = threading.Thread(target=recving, args=(client, inputer), daemon=True)
recving_thread.start()

while True:
content = inputer.input("Myself: ")
if content == "exit":
break
client.server.put({"address": client.address, "content": content})

client.close()
```

Then you can get this effect:
![chat room effect](https://github.com/Time-Coder/Connector/blob/master/doc/source/chat_room.gif)

## Time consuming integral

If you want to get the integral value of a function on an inteval, in a simple way, you can use following code:
```python
import math
def integral(func, inteval):
result = 0
n = 1000
L = inteval[1] - inteval[0]
dx = L / n
for i in range(n):
x = inteval[0] + i/n*L
result += dx * func(x)
return result

def f(x):
return math.sin(x)

if __name__ == '__main__':
print(integral(f, [0, math.pi]))
```

In above code, `f` is a simple function. But if `f` is a very time consuming function, the time consuming will be multiplied by 1000 for total integral. Naturally, we want multiple computers to calculate function value at the same time. Then we can use **Connector** to get multiple computer work together and do task scheduling easily. Just use following code:
```python
# server side
import math
from Connector import *

def integral(func, inteval):
result = 0
n = 1000
L = inteval[1] - inteval[0]
dx = L / n

server = Server("192.168.199.210", 1900)
server["func"] = func
for i in range(n):
x = inteval[0] + i/n*L
server.queues["task"].put(x)

while n > 0:
result += dx * server.queues["result"].get()
n -= 1

server.close()

return result

def f(x):
return math.sin(x)

if __name__ == '__main__':
print(integral(f, [0, math.pi]))
```

```python
# client side
from Connector import *

client = Client()
client.connect("192.168.199.210", 1900)

f = client.server["func"]
while True:
try:
x = client.server.queues["task"].get()
value = f(x)
client.server.queues["result"].put(value)
except BaseException:
break
```

If you just copy this example and test, you will find that use distributed computing way is even slower then before. Because in this example, network communication time delay is larger then calculating `sin(x)`. Only to use distributed computing way when your function is complicated enough.
16 changes: 16 additions & 0 deletions source/File-Transfer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# File Transfer

**Connector** can do file exchange in a very easy way. But can only exchange files between server and client. Client and client cannot exchange file directly, but they can do it through server. You can use following functions to get/put files/folders between client and server(where `client` on client side means `client` object and on server side means ***client peer***).

* `client.get_file(src_file_path, dest_file_path = None, block = True)`: Get file from remote computer.
* `src_file_path`: Remote file path you need to get from remote computer.
* `dest_file_path`: Local file path you need to put the file at. If it's `None`, it will put file at current working directory.
* `block`: If it's `True`, it will block the process until file transfer finished. Otherwise this method will immediately return a `Future` object which you can call `done` method on it to check if transfer is finished. To see more usage of a `Future` object, please refer to [User Functions Reference]()
* `client.put_file(src_file_path, dest_file_path = None, block = True)`: Put local file to remote computer.
* `src_file_path`: Local file path you need to put to remote computer.
* `dest_file_path`: Remote computer file path which you need to put file at. If it's `None`, it will put file at remote script working directory.
* `block`: If it's `True`, it will block the process until file transfer finished. Otherwise this method will immediately return a `Future` object which you can call `done` method on it to check if transfer is finished. To see more usage of a `Future` object, please refer to [User Functions Reference]()
* `client.get_folder(src_folder_path, dest_folder_path = None, block = True)`: Get folder from remote computer. The usage is just like `get_file`.
* `client.put_folder(src_folder_path, dest_folder_path = None, block = True)`: Put local folder to remote computer. The usage is just like `put_file`.
* `server.put_file_to_all(src_file_path, dest_file_path)`: Put server computer's file `src_file_path` to all connected clients as path `dest_file_path`.
* `server.put_folder_to_all(src_folder_path, dest_folder_path)`: Put server computer's folder `src_folder_path` to all connected clients as path `dest_folder_path`.
34 changes: 34 additions & 0 deletions source/Getting-Start.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Getting Start

## Installation

Just use pip command to install **Connector** to your local Python environment:
```batch
pip install tcp-connector
```
PyPI not support name Connector, so on PyPI it's named as **tcp-connector**. But in Python environment, you should import `Connector` instead of `tcp-connector`. After installed, try following code in Python. If this statement won't throw exception, that means your installation is succeeded.
```python
>>> from Connector import *
```

## Get connected

**Connector** works in Client/Server mode. So if you want to communicate between some computers, you should choose one computers work as a server, other computers work as a clients and should connect with server. So you should use following steps to get all computers connected:

0. You have some computers connected in the one Local Area Network.
1. Choose one computer works as server. Other computers work as clients.
2. On server computer, run code `server = Server(ip, port_number)`. If `port_number` gives nothing, it will use a random avaiable port. If both `ip` and `port_number` give nothing, it will use first avaiable network interface card's ip.
3. On server computer, you can use `print(server.address)` to get address of server. Such that it print `('192.168.199.210', 57680)`. Note it on your notebook.
4. On each client computer, run following code to connect with server:
```python
client = Client()
client.connect('192.168.199.210', 57680) # use the address you note on notebook
```
5. On server computer, you can get each ***client peer*** by this way:
```python
server.clients[0] # to get first connected client
server.clients[('192.168.105.23', 8273)] # to get client whose address is ('192.168.105.23', 8273)
```
***client peer*** means a communication handle on server side that connected with one client.

After that, you got `server` object on server computer and `client` object on each client computer. Let's use **Connector** to communicate with each other!
28 changes: 28 additions & 0 deletions source/RPC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Remote Procedure Call

**Connector** can do RPC in a easy way. It can call functions, call system commands and run scripts on remote computer. In following description, `client` means `client` object on client side, means ***client peer*** on server side.

* `client.eval(statement, block = True)`: Let remote computer call python `eval` and get return value.
* `statement`: The string of Python code.
* `block`: If `block` is `True`, it will block process until remote `eval` returns. Otherwise it will immediately return a `Future` object which you can call `result()` on it to get real return value.
* `client.exec(code, block = True)`: Let remote computer call python `exec` and get return value. Usage just like `client.eval`.
* `client.execfile(local_script_path, block=True)`: Let remote computer run a local python script.
* `local_script_path`: Script file path on local computer.
* `block`: If `block` is `True`, it will block process until remote computer run script finished. Otherwise, it will immediately return a `Future` object which you can call `result()` on to wait it finished.
* `client.exec_remote_file(remote_script_path, block=True)`: Let remote computer run a remote python script. The usage is just like `client.execfile`.
* `client.system(cmd, quiet=False, remote_quiet=False, once_all=False, block=True)`: Let remote computer run system command.
* `cmd`: System command string need to be call.
* `quiet`: If `quiet` is `True`, local side won't print anything of standard output and standard error.
* `remote_quiet`: If it's `True`, remote side won't print anything of standard output and standard error.
* `once_all`: Local and remote side won't print anything during system call processing and will print message after system finished.
* `block`: If `block` is `True`, it will block process until system call finished and return system call's return value. Otherwise, it will immediately return a `Future` object which you can call `result()` on it to get system call's return value.
* `client.call(function, args=(), kwargs={}, block=True)`: Let remote computer call a local function.
* `function`: A local callable python object, for example `print` is OK.
* `args`: Function's positional arguments need to be passed.
* `kwargs`: Function's key words arguments need to be passed.
* `block`: If `block` is `True`, it will block process until remote computer call this function finished and return this function calling return value. Otherwise, it will immediately return a `Future` object which you can call `result()` on it to get function calling return value.
* `client.call_remote(function_name, args=(), kwargs={}, block=True)`: Let remote computer call a remote function.
* `function_name`: A remote function name string, for example `"print"` is OK.
* `args`: Function's positional arguments need to be passed.
* `kwargs`: Function's key words arguments need to be passed.
* `block`: If `block` is `True`, it will block process until remote computer call this function finished and return this function calling return value. Otherwise, it will immediately return a `Future` object which you can call `result()` on it to get function calling return value.

0 comments on commit ab97c79

Please sign in to comment.