Network.cs (5070B)
1 using System; 2 using System.Collections.Generic; 3 using System.Net; 4 using System.Net.Sockets; 5 6 // for MemoryMarshall, to efficiently read and write buffers 7 using System.Runtime.InteropServices; 8 9 using NetworkingDemo.Packets; 10 11 namespace NetworkingDemo { 12 internal enum ConnectionResult { 13 ReceivePackets, 14 CloseConnection, 15 } 16 17 internal delegate ConnectionResult PacketHandler(Memory<byte> buffer, Connection conn); 18 19 internal class Connection { 20 private readonly NetworkStream network; 21 22 private const int PacketHeaderLength = sizeof(PacketType) + sizeof(int); 23 private byte[] recvBuffer; 24 private int recvBufferOffset; 25 26 private readonly Dictionary<PacketType, PacketHandler> handlers; 27 28 internal Connection(NetworkStream network) { 29 this.network = network; 30 31 handlers = new Dictionary<PacketType, PacketHandler>(); 32 } 33 34 internal Connection(NetworkStream network, Dictionary<PacketType, PacketHandler> handlers) { 35 this.network = network; 36 37 this.handlers = handlers; 38 } 39 40 internal void On(PacketType type, PacketHandler handler) { 41 handlers[type] = handler; 42 } 43 44 internal void Send<T>(T packet) where T : IPacket { 45 byte[] buffer = new byte[packet.MaxSerializedLength()]; 46 int length = packet.SerializeInto(buffer); 47 48 int raw_type = IPAddress.HostToNetworkOrder((int) packet.Type()); 49 int raw_length = IPAddress.HostToNetworkOrder(length); 50 51 Span<byte> header = stackalloc byte[PacketHeaderLength]; 52 MemoryMarshal.Write(header.Slice(0, sizeof(int)), ref raw_type); 53 MemoryMarshal.Write(header.Slice(sizeof(int), sizeof(int)), ref raw_length); 54 55 network.Write(header); 56 57 network.Write(buffer, 0, length); 58 } 59 60 internal void BeginRecv() { 61 recvBuffer = new byte[PacketHeaderLength]; 62 recvBufferOffset = 0; 63 64 network.BeginRead(recvBuffer, 0, PacketHeaderLength, OnRecvHeader, null); 65 } 66 67 private void OnRecvHeader(IAsyncResult result) { 68 int bytes_read = network.EndRead(result); 69 if (bytes_read <= 0) { 70 Console.WriteLine("Error! Zero bytes read when receiving header!"); 71 return; 72 } 73 74 recvBufferOffset += bytes_read; 75 if (recvBufferOffset < recvBuffer.Length) { 76 network.BeginRead(recvBuffer, recvBufferOffset, 77 recvBuffer.Length - recvBufferOffset, 78 OnRecvHeader, null); 79 return; 80 } 81 82 Span<byte> header = recvBuffer; 83 84 int raw_type = MemoryMarshal.Read<int>(header.Slice(0, sizeof(int))); 85 int raw_length = MemoryMarshal.Read<int>(header.Slice(sizeof(int), sizeof(int))); 86 87 PacketType type = (PacketType) IPAddress.NetworkToHostOrder(raw_type); 88 int length = IPAddress.NetworkToHostOrder(raw_length); 89 90 recvBuffer = new byte[length]; 91 recvBufferOffset = 0; 92 93 network.BeginRead(recvBuffer, 0, length, OnRecvBody, type); 94 } 95 96 private void OnRecvBody(IAsyncResult result) { 97 int bytes_read = network.EndRead(result); 98 if (bytes_read <= 0) { 99 Console.WriteLine("Error! Zero bytes read when receiving body!"); 100 return; 101 } 102 103 recvBufferOffset += bytes_read; 104 if (recvBufferOffset < recvBuffer.Length) { 105 network.BeginRead(recvBuffer, recvBufferOffset, 106 recvBuffer.Length - recvBufferOffset, 107 OnRecvBody, result.AsyncState); 108 return; 109 } 110 111 PacketType type = (PacketType) result.AsyncState!; 112 ConnectionResult handlerResult = handlers[type](recvBuffer, this); 113 114 switch (handlerResult) { 115 case ConnectionResult.ReceivePackets: 116 BeginRecv(); 117 break; 118 119 case ConnectionResult.CloseConnection: 120 network.Close(); 121 break; 122 } 123 } 124 } 125 126 internal class Server { 127 private readonly TcpListener listener; 128 129 private const int MaxConcurrentClients = 16; 130 131 private int nextClientId; 132 private readonly Dictionary<int, Connection> clients; 133 134 private readonly Dictionary<PacketType, PacketHandler> handlers; 135 136 internal Server(IPAddress addr, int port) { 137 listener = new TcpListener(addr, port); 138 139 nextClientId = 0; 140 clients = new Dictionary<int, Connection>(); 141 142 handlers = new Dictionary<PacketType, PacketHandler>(); 143 } 144 145 internal void On(PacketType type, PacketHandler handler) { 146 handlers[type] = handler; 147 } 148 149 internal void Start() { 150 listener.Start(); 151 152 listener.BeginAcceptTcpClient(OnAcceptClient, null); 153 } 154 155 private void OnAcceptClient(IAsyncResult result) { 156 TcpClient peer = listener.EndAcceptTcpClient(result); 157 listener.BeginAcceptTcpClient(OnAcceptClient, null); 158 159 if (clients.Count == MaxConcurrentClients) { 160 peer.Close(); 161 peer.Dispose(); 162 return; 163 } 164 165 int clientId = nextClientId++; 166 clients[clientId] = new Connection(peer.GetStream(), handlers); 167 Handle(clients[clientId]); 168 } 169 170 private void Handle(Connection connection) { 171 // send the welcome packet 172 connection.Send(new WelcomePacket("Hello!")); 173 174 // start waiting on responses 175 connection.BeginRecv(); 176 } 177 } 178 179 internal class Client { 180 private readonly TcpClient connection; 181 182 internal Client() { 183 connection = new TcpClient(); 184 } 185 186 internal Connection ConnectTo(IPAddress addr, int port) { 187 connection.Connect(addr, port); 188 return new Connection(connection.GetStream()); 189 } 190 } 191 }