diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/TestVelGameServer/Assets/NetworkGUI.cs b/TestVelGameServer/Assets/NetworkGUI.cs index d224c34..2523455 100644 --- a/TestVelGameServer/Assets/NetworkGUI.cs +++ b/TestVelGameServer/Assets/NetworkGUI.cs @@ -40,6 +40,10 @@ public class NetworkGUI : MonoBehaviour string s = m.type + ":" + m.sender +":" + m.text; messageBuffer.Add(s); messages.text = ""; + + + + if(messageBuffer.Count > 10) { messageBuffer.RemoveAt(0); diff --git a/TestVelGameServer/Assets/NetworkManager.cs b/TestVelGameServer/Assets/NetworkManager.cs index 1a82b91..09647a5 100644 --- a/TestVelGameServer/Assets/NetworkManager.cs +++ b/TestVelGameServer/Assets/NetworkManager.cs @@ -13,8 +13,12 @@ public class NetworkManager : MonoBehaviour #region private members private TcpClient socketConnection; private Thread clientReceiveThread; - public int userid; + public int userid = -1; + public string room; int messagesReceived = 0; + public GameObject playerPrefab; + public Dictionary players = new Dictionary(); + #endregion // Use this for initialization public class Message @@ -27,6 +31,7 @@ public class NetworkManager : MonoBehaviour void Start() { ConnectToTcpServer(); + } private void addMessage(Message m) @@ -42,6 +47,59 @@ public class NetworkManager : MonoBehaviour lock(receivedMessages) { foreach(Message m in receivedMessages) { + if(m.type == 0) //when you join the server + { + this.userid = m.sender; + Debug.Log("joined server"); + } + + if (m.type == 2) + { + //if this message is for me, that means I joined a new room... + if (this.userid == m.sender) + { + foreach (KeyValuePair kvp in players) + { + Destroy(kvp.Value.gameObject); + } + players.Clear(); //we clear the list, but will recreate as we get messages from people in our room + + if (m.text != "") + { + NetworkPlayer player = GameObject.Instantiate(playerPrefab).GetComponent(); + player.userid = m.sender; + players.Add(userid, player); + player.room = m.text; + player.manager = this; + } + } + else //not for me, a player is joining or leaving + { + NetworkPlayer me = players[userid]; + + if (me.room != m.text) + { + //we got a left message, kill it + Destroy(players[m.sender].gameObject); + players.Remove(m.sender); + } + else + { + //we got a join mesage, create it + NetworkPlayer player = GameObject.Instantiate(playerPrefab).GetComponent(); + player.room = m.text; + player.userid = m.sender; + player.manager = this; + players.Add(m.sender, player); + } + } + } + if(m.type == 3) + { + + players[m.sender]?.handleMessage(m); + + } messageReceived(m); } receivedMessages.Clear(); @@ -88,9 +146,9 @@ public class NetworkManager : MonoBehaviour { if(sections.Length > 1) { - int user_id = int.Parse(sections[1]); + m.type = type; - m.sender = user_id; + m.sender = int.Parse(sections[1]); m.text = ""; addMessage(m); } @@ -109,9 +167,8 @@ public class NetworkManager : MonoBehaviour m.type = 2; int user_id = int.Parse(sections[1]); m.sender = user_id; - - string room_name = sections[2]; - m.text = room_name; + string new_room = sections[2]; + m.text = new_room; addMessage(m); } diff --git a/TestVelGameServer/Assets/NetworkPlayer.cs b/TestVelGameServer/Assets/NetworkPlayer.cs new file mode 100644 index 0000000..316f404 --- /dev/null +++ b/TestVelGameServer/Assets/NetworkPlayer.cs @@ -0,0 +1,71 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class NetworkPlayer : MonoBehaviour +{ + public int userid; + public string username; + public string room; + public NetworkManager manager; + Vector3 networkPosition; + + // Start is called before the first frame update + void Start() + { + if (manager.userid == userid) //this is me, I send updates + { + StartCoroutine(syncTransformCoroutine()); + } + } + + // Update is called once per frame + void Update() + { + if (userid != manager.userid) //not me, I move to wherever I should + { + transform.position = Vector3.Lerp(transform.position, networkPosition, .1f); + } + else + { + float h = Input.GetAxis("Horizontal"); + float v = Input.GetAxis("Vertical"); + Vector3 delta = h * Vector3.right + v * Vector3.up; + transform.position = transform.position + delta * Time.deltaTime; + } + } + + IEnumerator syncTransformCoroutine() + { + while (true) + { + manager.sendTo(0, "1," + transform.position.x + "," + transform.position.y + "," + transform.position.z + ";"); + yield return new WaitForSeconds(.1f); + } + } + public void handleMessage(NetworkManager.Message m) + { + //we need to parse the message + + //types of messages + string[] messages = m.text.Split(';'); //messages are split by ; + for(int i = 0; i < messages.Length; i++) + { + //individual message parameters separated by comma + string[] sections = messages[i].Split(','); + + switch (sections[0]) + { + case "1": //update transform of self + { + float x = float.Parse(sections[1]); + float y = float.Parse(sections[2]); + float z = float.Parse(sections[3]); + networkPosition = new Vector3(x, y, z); + break; + } + } + } + + } +} diff --git a/TestVelGameServer/Assets/NetworkPlayer.cs.meta b/TestVelGameServer/Assets/NetworkPlayer.cs.meta new file mode 100644 index 0000000..f2b23a1 --- /dev/null +++ b/TestVelGameServer/Assets/NetworkPlayer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d8d3b6de660834e3e898725928251405 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestVelGameServer/Assets/PlayerPrefab.prefab b/TestVelGameServer/Assets/PlayerPrefab.prefab new file mode 100644 index 0000000..241b76f --- /dev/null +++ b/TestVelGameServer/Assets/PlayerPrefab.prefab @@ -0,0 +1,114 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &6139051692386484099 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3076416102083120807} + - component: {fileID: 8527011532923434593} + - component: {fileID: 6854617867369839} + - component: {fileID: 5845716565458182149} + - component: {fileID: 5713671764962751473} + m_Layer: 0 + m_Name: PlayerPrefab + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3076416102083120807 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6139051692386484099} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &8527011532923434593 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6139051692386484099} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6854617867369839 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6139051692386484099} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!65 &5845716565458182149 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6139051692386484099} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &5713671764962751473 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6139051692386484099} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d8d3b6de660834e3e898725928251405, type: 3} + m_Name: + m_EditorClassIdentifier: + userid: 0 + username: + room: + manager: {fileID: 0} diff --git a/TestVelGameServer/Assets/PlayerPrefab.prefab.meta b/TestVelGameServer/Assets/PlayerPrefab.prefab.meta new file mode 100644 index 0000000..7899882 --- /dev/null +++ b/TestVelGameServer/Assets/PlayerPrefab.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d4158ab9c4a204cdbba28d3273fc1fb3 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestVelGameServer/Assets/Scenes/SampleScene.unity b/TestVelGameServer/Assets/Scenes/SampleScene.unity index 5d2cf95..ffca624 100644 --- a/TestVelGameServer/Assets/Scenes/SampleScene.unity +++ b/TestVelGameServer/Assets/Scenes/SampleScene.unity @@ -405,6 +405,7 @@ MonoBehaviour: sendInput: {fileID: 945446555} roomInput: {fileID: 711524768} messages: {fileID: 1894247854} + messageBuffer: [] --- !u!1 &440509381 GameObject: m_ObjectHideFlags: 0 @@ -1090,8 +1091,10 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: host: localhost - port: 80 + port: 3290 userid: 0 + room: + playerPrefab: {fileID: 6139051692386484099, guid: d4158ab9c4a204cdbba28d3273fc1fb3, type: 3} connected: 0 --- !u!1 &1235343400 GameObject: diff --git a/TestVelGameServer/ProjectSettings/ProjectSettings.asset b/TestVelGameServer/ProjectSettings/ProjectSettings.asset index 1d97062..4e57304 100644 --- a/TestVelGameServer/ProjectSettings/ProjectSettings.asset +++ b/TestVelGameServer/ProjectSettings/ProjectSettings.asset @@ -42,8 +42,8 @@ PlayerSettings: m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} m_HolographicTrackingLossScreen: {fileID: 0} - defaultScreenWidth: 1024 - defaultScreenHeight: 768 + defaultScreenWidth: 600 + defaultScreenHeight: 400 defaultScreenWidthWeb: 960 defaultScreenHeightWeb: 600 m_StereoRenderingPath: 0 @@ -68,7 +68,7 @@ PlayerSettings: androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 androidBlitType: 0 - defaultIsNativeResolution: 1 + defaultIsNativeResolution: 0 macRetinaSupport: 1 runInBackground: 1 captureSingleScreen: 0 @@ -82,7 +82,7 @@ PlayerSettings: bakeCollisionMeshes: 0 forceSingleInstance: 0 useFlipModelSwapchain: 1 - resizableWindow: 0 + resizableWindow: 1 useMacAppStoreValidation: 0 macAppStoreCategory: public.app-category.games gpuSkinning: 1 @@ -93,7 +93,7 @@ PlayerSettings: xboxEnableFitness: 0 visibleInBackground: 1 allowFullscreenSwitch: 1 - fullscreenMode: 1 + fullscreenMode: 3 xboxSpeechDB: 0 xboxEnableHeadOrientation: 0 xboxEnableGuest: 0 @@ -145,7 +145,8 @@ PlayerSettings: resolutionScalingMode: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.1 - applicationIdentifier: {} + applicationIdentifier: + Standalone: com.DefaultCompany.TestVelGameServer buildNumber: Standalone: 0 iPhone: 0 diff --git a/threaded_binary_server.py b/threaded_binary_server.py new file mode 100644 index 0000000..c4b640b --- /dev/null +++ b/threaded_binary_server.py @@ -0,0 +1,213 @@ + +import socket +from _thread import * +import threading +import types + +rooms = {} #will be a list of room objects. #this must be locked when when adding or removing rooms +rooms_lock = threading.Lock() +client_dict = {} #will be a list of client objects. #this must be locked when adding or removing clients +client_lock = threading.Lock() + +HOST = "" +PORT = 80 + + +def send_synced_room_message(roomName, message, exclude_client=None): #guaranteed to be received by all clients in order, mostly use for handling ownership + rooms_lock.acquire() + if roomName in rooms: + room_lock = rooms[roomName].room_lock + clients = rooms[roomName].clients + else: + rooms_lock.release() + return + rooms_lock.release() + room_lock.acquire() + for c in clients: + if (exclude_client != None) and (c.id == exclude_client.id): continue + send_client_message(c,message) + room_lock.release() + +def send_room_message(roomName, message, exclude_client=None): #not guaranteed to be received by all clients in order + rooms_lock.acquire() + if roomName in rooms: + clients = rooms[roomName].clients + else: + rooms_lock.release() + return + rooms_lock.release() + for c in clients: + if (exclude_client != None) and (c.id == exclude_client.id): continue + send_client_message(c,message) + +def send_client_message(client, message): + client.message_lock.acquire() + client.message_queue.append(message) + client.message_lock.release() + client.message_ready.set() + +def decode_message(client,message): + global rooms + global rooms_lock + decodedMessage = message.split(":") + if len(decodedMessage) < 1: print("Invalid message received"); return + messageType = decodedMessage[0] + + if not client.logged_in and messageType == '0' and len(decodedMessage) == 3: + client.username = decodedMessage[1] + #probaby check password too against a database of some sort where we store lots of good stuff + client.logged_in = True + send_client_message(client,f"0:{client.id}:\n") + + elif client.logged_in: + if messageType == '1': + rooms_lock.acquire() + response = "1:" + ",".join([room.name+"-"+str(len(room.clients)) for room in rooms.values()]) + "\n" + rooms_lock.release() + send_client_message(client,response) + if messageType == '2' and len(decodedMessage) > 1: + + #join or create a room + + roomName = decodedMessage[1] + if client.room == roomName: #don't join the same room + pass + elif (roomName == '-1') and client.room != '': #can't leave a room if you aren't in one + #leave the room + rooms_lock.acquire() + try: + + rooms[client.room].clients.remove(client) + if(len(rooms[client.room].clients) == 0): + del rooms[client.room] + except Exception as e: + print("not in room") + rooms_lock.release() + send_room_message(client.room, f"2:{client.id}:\n") + send_client_message(client,f"2:{client.id}:\n") + client.room = '' + else: #join or create the room + rooms_lock.acquire() + if roomName in rooms: + #join the room + rooms[roomName].clients.append(client) + else: + #create the room and join + rooms[roomName] = types.SimpleNamespace(name=roomName,clients=[client],room_lock=threading.Lock()) + rooms_lock.release() + + if (client.room != '') and (client.room != roomName): #client left the previous room + send_room_message(client.room, f"2:{client.id}:{roomName}:\n") + + client.room = roomName #client joins the new room + #send a message to the clients new room that they joined! + send_room_message(roomName, f"2:{client.id}:{client.room}\n") + + + + + if messageType == '3' and len(decodedMessage) > 2: + subMessageType = decodedMessage[1] + if subMessageType == '0': + #send a message to everyone in the room (not synced) + send_room_message(client.room,f"3:{client.id}:{decodedMessage[2]}\n",client) + elif subMessageType == '1': + send_room_message(client.room,f"3:{client.id}:{decodedMessage[2]}\n") + elif subMessageType == '2': + #send a message to everyone in the room (not synced) + send_synced_room_message(client.room,f"3:{client.id}:{decodedMessage[2]}\n",client) + elif subMessageType == '3': + send_synced_room_message(client.room,f"3:{client.id}:{decodedMessage[2]}\n") + +def client_read_thread(conn, addr, client): + global rooms + global rooms_lock + global client_dict + global client_lock + buffer = bytearray() + state = 0 + buffer_size = 0 + #valid messages are at least 2 bytes (size) + + while client.alive: + try: + recv_data = conn.recv(1024) + except Exception as e: + client.alive = False + client.message_ready.set() + continue + if not recv_data: + client.alive = False + client.message_ready.set() #in case it's waiting for a message + else: + buffer.extend(recv_data) #read + if (state == 0) and (len(buffer) > 2): + buffer_size = int.from_bytes(buffer[0:2], byteorder='big') + state = 1 + if (state == 1) and (len(buffer) >= buffer_size): + #we have a complete packet, process it + message = buffer[2:buffer_size] + decode_message(client,message) + buffer = buffer[buffer_size:] + state = 0 + + while not client.write_thread_dead: + client.message_ready.set() + pass + #now we can kill the client, removing the client from the rooms + client_lock.acquire() + rooms_lock.acquire() + if client.room != '': + rooms[client.room].clients.remove(client) + if(len(rooms[client.room].clients) == 0): + del rooms[client.room] + del client_dict[client.id] #remove the client from the list of clients... + rooms_lock.release() + client_lock.release() + send_room_message(client.room, f"2:{client.id}:\n") + print("client destroyed") +def client_write_thread(conn, addr, client): + while client.alive: + + client.message_lock.acquire() + for message in client.message_queue: + try: + conn.sendall(message.encode('utf-8')) + except: + break #if the client is dead the read thread will catch it + client.message_queue = [] + client.message_lock.release() + client.message_ready.wait() + client.message_ready.clear() + client.write_thread_dead = True + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + sock.bind((HOST, PORT)) + sock.listen() + next_client_id = 0 + while True: + + c, addr = sock.accept() #blocks until a connection is made + client = types.SimpleNamespace(id=next_client_id, + alive=True, + message_queue=[], + message_lock=threading.Lock(), + inb='', #read buffer + message_ready=threading.Event(), + logged_in=False, + username='', + room='', + write_thread_dead=False + ) + client_lock.acquire() + client_dict[next_client_id] = client + client_lock.release() + + next_client_id += 1 + + start_new_thread(client_read_thread, (c, addr, client)) + start_new_thread(client_write_thread, (c, addr, client)) + + diff --git a/threaded_server.py b/threaded_server.py index 3d6e4b7..1f229b4 100644 --- a/threaded_server.py +++ b/threaded_server.py @@ -10,7 +10,7 @@ client_dict = {} #will be a list of client objects. #this must be locked when a client_lock = threading.Lock() HOST = "" -PORT = 80 +PORT = 3290 def send_synced_room_message(roomName, message, exclude_client=None): #guaranteed to be received by all clients in order, mostly use for handling ownership @@ -86,14 +86,18 @@ def decode_message(client,message): send_room_message(client.room, f"2:{client.id}:\n") send_client_message(client,f"2:{client.id}:\n") client.room = '' - else: #join or create the room + elif roomName != '': #join or create the room rooms_lock.acquire() + if roomName in rooms: #join the room rooms[roomName].clients.append(client) + else: #create the room and join rooms[roomName] = types.SimpleNamespace(name=roomName,clients=[client],room_lock=threading.Lock()) + + rooms_lock.release() if (client.room != '') and (client.room != roomName): #client left the previous room @@ -102,6 +106,12 @@ def decode_message(client,message): client.room = roomName #client joins the new room #send a message to the clients new room that they joined! send_room_message(roomName, f"2:{client.id}:{client.room}\n") + + for c in rooms[roomName].clients: + if c.id != client.id: + send_client_message(client,f"2:{c.id}:{client.room}\n") + + @@ -182,6 +192,7 @@ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: while True: c, addr = sock.accept() #blocks until a connection is made + c.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) client = types.SimpleNamespace(id=next_client_id, alive=True, message_queue=[], diff --git a/threaded_test_client.py b/threaded_test_client.py index a862422..d31b587 100644 --- a/threaded_test_client.py +++ b/threaded_test_client.py @@ -3,8 +3,9 @@ import time from _thread import * import threading -server_addr = ('127.0.0.1', 80) +server_addr = ('localhost', 3290) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.connect_ex(server_addr) @@ -25,7 +26,7 @@ def writeThread(sock): while True: sock.sendall(f'3:0:{"thasdl;fjasd;lfjasl;dfjal;skdjlask;dflasd;jkjfjkjfsfjfjakfjafjdfjakjflfjadjf;jfakdjfdjfakdjfsdj;ldjf;laskdflsdjfasdkjfkdjflskdjfskdjflkfjlkdjfskdjfkjfskdjf;kfjs;kfjadkfjas;ldfalsdkfsdkfjasdkjfasdkfjlkdjfkdjflkdjf;djfadkfjaldkfjalkfja;kfja;kfjadkfjadkfja;sdkfa;dkfj;dfkjaslkfjas;dkfs;dkfjsldfjasdfjaldfjaldkfj;lkj"}\n'.encode('utf-8')) i = i+1 - time.sleep(0.1) + time.sleep(0.01) diff --git a/threaded_test_client_binary.py b/threaded_test_client_binary.py new file mode 100644 index 0000000..a862422 --- /dev/null +++ b/threaded_test_client_binary.py @@ -0,0 +1,41 @@ +import socket +import time +from _thread import * +import threading + +server_addr = ('127.0.0.1', 80) +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.connect_ex(server_addr) + + + + +def readThread(sock): + + while True: + data = sock.recv(1024) + print(data.decode('utf-8')); + +def writeThread(sock): + i = 0 + sock.sendall('0::\n'.encode('utf-8')) + sock.sendall('2:myroom\n'.encode('utf-8')) + + + while True: + sock.sendall(f'3:0:{"thasdl;fjasd;lfjasl;dfjal;skdjlask;dflasd;jkjfjkjfsfjfjakfjafjdfjakjflfjadjf;jfakdjfdjfakdjfsdj;ldjf;laskdflsdjfasdkjfkdjflskdjfskdjflkfjlkdjfskdjfkjfskdjf;kfjs;kfjadkfjas;ldfalsdkfsdkfjasdkjfasdkfjlkdjfkdjflkdjf;djfadkfjaldkfjalkfja;kfja;kfjadkfjadkfja;sdkfa;dkfj;dfkjaslkfjas;dkfs;dkfjsldfjasdfjaldfjaldkfj;lkj"}\n'.encode('utf-8')) + i = i+1 + time.sleep(0.1) + + + +start_new_thread(readThread,(sock,)) +start_new_thread(writeThread,(sock,)) + +while True: + time.sleep(1) + +sock.close() + + +