├── .gitignore ├── 01.a.HttpClient ├── 01.HttpClient.csproj ├── Downloader.cs └── Properties │ └── AssemblyInfo.cs ├── 02.HttpListener ├── 02.HttpListener.csproj ├── HttpServer.cs └── Properties │ └── AssemblyInfo.cs ├── 03.b.TcpChatServer ├── 03.b.TcpChatServer.csproj ├── Properties │ └── AssemblyInfo.cs └── TcpChatServer.cs ├── 03.c.TcpChatMessenger ├── 03.c.TcpChatMessenger.csproj ├── Properties │ └── AssemblyInfo.cs └── TcpChatMessenger.cs ├── 03.d.TcpChatViewer ├── 03.d.TcpChatViewer.csproj ├── Properties │ └── AssemblyInfo.cs └── TcpChatViewer.cs ├── 04.b.TcpGamesServer ├── 04.b.TcpGamesServer.csproj ├── GuessMyNumberGame.cs ├── IGame.cs ├── Packet.cs ├── Properties │ └── AssemblyInfo.cs ├── TcpGamesServer.cs └── packages.config ├── 04.e.TcpGamesClient ├── 04.e.TcpGamesClient.csproj ├── Properties │ └── AssemblyInfo.cs ├── TCPGamesClient.cs └── packages.config ├── 05.Compression ├── 05.Compression.csproj ├── CompressionExample.cs ├── Properties │ └── AssemblyInfo.cs ├── film.mp4 └── image.bmp ├── 06.a.UdpFileSender ├── 06.d.UdpFileSender.csproj ├── Block.cs ├── Files │ └── short_message.txt ├── NetworkMessage.cs ├── Packet.cs ├── Properties │ └── AssemblyInfo.cs └── UdpFileSender.cs ├── 06.b.UdpFileReceiver ├── 06.e.UdpFileReceiver.csproj ├── Properties │ └── AssemblyInfo.cs └── UdpFileReceiver.cs ├── 07.e.UdpPongServer ├── 07.e.UdpPongServer.csproj ├── Arena.cs ├── Ball.cs ├── GameGeometry.cs ├── NetworkMessage.cs ├── Packet.cs ├── Paddle.cs ├── PlayerInfo.cs ├── PongServer.cs ├── Properties │ └── AssemblyInfo.cs ├── ThreadSafe.cs └── packages.config ├── 07.g.UdpPongClient ├── 07.g.UdpPongClient.csproj ├── Content │ ├── ball-hit.bfxrsound │ ├── ball-hit.wav │ ├── ball.png │ ├── establishing-connection-msg.png │ ├── game-over-msg.png │ ├── paddle.png │ ├── score.bfxrsound │ ├── score.wav │ └── waiting-for-game-start-msg.png ├── PongClient.cs └── packages.config ├── 08.Ping ├── 08.Ping.csproj ├── PingExample.cs └── Properties │ └── AssemblyInfo.cs ├── 09.b.BerkelySocketCCode ├── 09.b.BerkelySocketCCode.csproj ├── Makefile ├── client.c └── server.c ├── 09.c.TcpSocketClientExample ├── 09.c.TcpSocketClientExample.csproj ├── Properties │ └── AssemblyInfo.cs └── TcpSocketClientExample.cs ├── 09.c.TcpSocketServerExample ├── 09.c.TcpSocketServerExample.csproj ├── Properties │ └── AssemblyInfo.cs └── TcpSocketServerExample.cs ├── CSharpNetworking.sln ├── CSharpNetworking.userprefs ├── DnsExample ├── DnsExample.cs ├── DnsExample.csproj └── Properties │ └── AssemblyInfo.cs ├── IPAddressExample ├── IPAddressExample.cs ├── IPAddressExample.csproj └── Properties │ └── AssemblyInfo.cs ├── IPEndPointExample ├── IPEndPointExample.cs ├── IPEndPointExample.csproj └── Properties │ └── AssemblyInfo.cs ├── README.md └── html ├── 01.HttpClient.html ├── 02.HttpListener.html ├── 03.a.tcp-chat.html ├── 03.b.tcp-chat-server.html ├── 03.c.tcp-chat-messenger.html ├── 03.d.tcp-chat-viewer.html ├── 03.e.tcp-chat-recap.html ├── 04.a.tcp-games.html ├── 04.b.tcp-games-application-protocol.html ├── 04.c.tcp-games-async-multithreaded-server.html ├── 04.d.tcp-games-guess-my-number.html ├── 04.e.tcp-games-client.html ├── 04.f.tcp-games-recap.html ├── 05.compression.html ├── 06.a.udp-file-transfer.html ├── 06.b.udp-file-transfer-protocol-design.html ├── 06.c.udp-file-transfer-common-files.html ├── 06.d.udp-file-transfer-sender.html ├── 06.e.udp-file-transfer-receiver.html ├── 06.f.udp-file-transfer-recap.html ├── 07.a.udp-pong.html ├── 07.b.udp-pong-protocol-design.html ├── 07.c.udp-pong-game-mechanics.html ├── 07.d.udp-pong-common-files.html ├── 07.e.udp-pong-server.html ├── 07.f.udp-pong-arena.html ├── 07.g.udp-pong-client.html ├── 07.h.udp-pong-recap.html ├── 08.ping.html ├── 09.a.socket-class-intro.html ├── 09.b.socket-class-intro-original-c-code.html ├── 09.c.socket-class-intro-csharp-port.html ├── 09.d.socket-class-intro-recap.html ├── conclusion.html ├── dns.html ├── ipaddress.html ├── ipendpoint.html └── preface.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Download this file using PowerShell v3 under Windows with the following comand 2 | # Invoke-WebRequest https://gist.githubusercontent.com/kmorcinek/2710267/raw/ -OutFile .gitignore 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # NuGet Packages 19 | *.nupkg 20 | # The packages folder can be ignored because of Package Restore 21 | **/packages/* 22 | # except build/, which is used as an MSBuild target. 23 | !**/packages/build/ 24 | # Uncomment if necessary however generally it will be regenerated when needed 25 | #!**/packages/repositories.config 26 | 27 | # MSTest test Results 28 | [Tt]est[Rr]esult*/ 29 | [Bb]uild[Ll]og.* 30 | 31 | *_i.c 32 | *_p.c 33 | *.ilk 34 | *.meta 35 | *.obj 36 | *.pch 37 | *.pdb 38 | *.pgc 39 | *.pgd 40 | *.rsp 41 | *.sbr 42 | *.tlb 43 | *.tli 44 | *.tlh 45 | *.tmp 46 | *.tmp_proj 47 | *.log 48 | *.vspscc 49 | *.vssscc 50 | .builds 51 | *.pidb 52 | *.log 53 | *.scc 54 | 55 | # OS generated files # 56 | .DS_Store* 57 | Icon? 58 | 59 | # Visual C++ cache files 60 | ipch/ 61 | *.aps 62 | *.ncb 63 | *.opensdf 64 | *.sdf 65 | *.cachefile 66 | 67 | # Visual Studio profiler 68 | *.psess 69 | *.vsp 70 | *.vspx 71 | 72 | # Guidance Automation Toolkit 73 | *.gpState 74 | 75 | # ReSharper is a .NET coding add-in 76 | _ReSharper*/ 77 | *.[Rr]e[Ss]harper 78 | 79 | # TeamCity is a build add-in 80 | _TeamCity* 81 | 82 | # DotCover is a Code Coverage Tool 83 | *.dotCover 84 | 85 | # NCrunch 86 | *.ncrunch* 87 | .*crunch*.local.xml 88 | 89 | # Installshield output folder 90 | [Ee]xpress/ 91 | 92 | # DocProject is a documentation generator add-in 93 | DocProject/buildhelp/ 94 | DocProject/Help/*.HxT 95 | DocProject/Help/*.HxC 96 | DocProject/Help/*.hhc 97 | DocProject/Help/*.hhk 98 | DocProject/Help/*.hhp 99 | DocProject/Help/Html2 100 | DocProject/Help/html 101 | 102 | # Click-Once directory 103 | publish/ 104 | 105 | # Publish Web Output 106 | *.Publish.xml 107 | 108 | # Windows Azure Build Output 109 | csx 110 | *.build.csdef 111 | 112 | # Windows Store app package directory 113 | AppPackages/ 114 | 115 | # Others 116 | *.Cache 117 | ClientBin/ 118 | [Ss]tyle[Cc]op.* 119 | ~$* 120 | *~ 121 | *.dbmdl 122 | *.[Pp]ublish.xml 123 | *.pfx 124 | *.publishsettings 125 | modulesbin/ 126 | tempbin/ 127 | 128 | # EPiServer Site file (VPP) 129 | AppData/ 130 | 131 | # RIA/Silverlight projects 132 | Generated_Code/ 133 | 134 | # Backup & report files from converting an old project file to a newer 135 | # Visual Studio version. Backup files are not needed, because we have git ;-) 136 | _UpgradeReport_Files/ 137 | Backup*/ 138 | UpgradeLog*.XML 139 | UpgradeLog*.htm 140 | 141 | # vim 142 | *.txt~ 143 | *.swp 144 | *.swo 145 | 146 | # svn 147 | .svn 148 | 149 | # Remainings from resolvings conflicts in Source Control 150 | *.orig 151 | 152 | # SQL Server files 153 | **/App_Data/*.mdf 154 | **/App_Data/*.ldf 155 | **/App_Data/*.sdf 156 | 157 | 158 | #LightSwitch generated files 159 | GeneratedArtifacts/ 160 | _Pvt_Extensions/ 161 | ModelManifest.xml 162 | 163 | # ========================= 164 | # Windows detritus 165 | # ========================= 166 | 167 | # Windows image file caches 168 | Thumbs.db 169 | ehthumbs.db 170 | 171 | # Folder config file 172 | Desktop.ini 173 | 174 | # Recycle Bin used on file shares 175 | $RECYCLE.BIN/ 176 | 177 | # Mac desktop service store files 178 | .DS_Store 179 | 180 | # SASS Compiler cache 181 | .sass-cache 182 | 183 | # Visual Studio 2014 CTP 184 | **/*.sln.ide 185 | 186 | # Visual Studio temp something 187 | .vs/ 188 | 189 | ##### 190 | # End of core ignore list, below put you custom 'per project' settings (patterns or path) 191 | ##### -------------------------------------------------------------------------------- /01.a.HttpClient/01.HttpClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {FEA4727D-7D53-453F-ADBF-43527B8D137A} 7 | Exe 8 | a.HttpClient 9 | 01.a.HttpClient 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /01.a.HttpClient/Downloader.cs: -------------------------------------------------------------------------------- 1 | // Filename: Downloader.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.IO; 7 | using System.Text; 8 | using System.Net.Http; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace HttpClientExample 13 | { 14 | class Downloader 15 | { 16 | // Where to download from, and where to save it to 17 | public static string urlToDownload = "http://localhost:8000"; 18 | public static string filename = "index.html"; 19 | 20 | public static async Task DownloadWebPage() 21 | { 22 | Console.WriteLine("Starting download..."); 23 | 24 | // Setup the HttpClient 25 | using (HttpClient httpClient = new HttpClient()) 26 | { 27 | // Get the webpage asynchronously 28 | HttpResponseMessage resp = await httpClient.GetAsync(urlToDownload); 29 | 30 | // If we get a 200 response, then save it 31 | if (resp.IsSuccessStatusCode) 32 | { 33 | Console.WriteLine("Got it..."); 34 | 35 | // Get the data 36 | byte[] data = await resp.Content.ReadAsByteArrayAsync(); 37 | 38 | // Save it to a file 39 | FileStream fStream = File.Create(filename); 40 | await fStream.WriteAsync(data, 0, data.Length); 41 | fStream.Close(); 42 | 43 | Console.WriteLine("Done!"); 44 | } 45 | } 46 | } 47 | 48 | public static void Main (string[] args) 49 | { 50 | Task dlTask = DownloadWebPage(); 51 | 52 | Console.WriteLine("Holding for at least 5 seconds..."); 53 | Thread.Sleep(TimeSpan.FromSeconds(5)); 54 | 55 | dlTask.GetAwaiter().GetResult(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /01.a.HttpClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle ("01.a.HttpClient")] 8 | [assembly: AssemblyDescription ("")] 9 | [assembly: AssemblyConfiguration ("")] 10 | [assembly: AssemblyCompany ("")] 11 | [assembly: AssemblyProduct ("")] 12 | [assembly: AssemblyCopyright ("ben")] 13 | [assembly: AssemblyTrademark ("")] 14 | [assembly: AssemblyCulture ("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion ("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /02.HttpListener/02.HttpListener.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {0D539553-B757-42E9-A9AC-8B9E46F199A0} 7 | Exe 8 | HttpListener 9 | 02.HttpListener 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /02.HttpListener/HttpServer.cs: -------------------------------------------------------------------------------- 1 | // Filename: HttpServer.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.IO; 7 | using System.Text; 8 | using System.Net; 9 | using System.Threading.Tasks; 10 | 11 | namespace HttpListenerExample 12 | { 13 | class HttpServer 14 | { 15 | public static HttpListener listener; 16 | public static string url = "http://localhost:8000/"; 17 | public static int pageViews = 0; 18 | public static int requestCount = 0; 19 | public static string pageData = 20 | "" + 21 | "" + 22 | " " + 23 | " HttpListener Example" + 24 | " " + 25 | " " + 26 | "

Page Views: {0}

" + 27 | "
" + 28 | " " + 29 | "
" + 30 | " " + 31 | ""; 32 | 33 | 34 | public static async Task HandleIncomingConnections() 35 | { 36 | bool runServer = true; 37 | 38 | // While a user hasn't visited the `shutdown` url, keep on handling requests 39 | while (runServer) 40 | { 41 | // Will wait here until we hear from a connection 42 | HttpListenerContext ctx = await listener.GetContextAsync(); 43 | 44 | // Peel out the requests and response objects 45 | HttpListenerRequest req = ctx.Request; 46 | HttpListenerResponse resp = ctx.Response; 47 | 48 | // Print out some info about the request 49 | Console.WriteLine("Request #: {0}", ++requestCount); 50 | Console.WriteLine(req.Url.ToString()); 51 | Console.WriteLine(req.HttpMethod); 52 | Console.WriteLine(req.UserHostName); 53 | Console.WriteLine(req.UserAgent); 54 | Console.WriteLine(); 55 | 56 | // If `shutdown` url requested w/ POST, then shutdown the server after serving the page 57 | if ((req.HttpMethod == "POST") && (req.Url.AbsolutePath == "/shutdown")) 58 | { 59 | Console.WriteLine("Shutdown requested"); 60 | runServer = false; 61 | } 62 | 63 | // Make sure we don't increment the page views counter if `favicon.ico` is requested 64 | if (req.Url.AbsolutePath != "/favicon.ico") 65 | pageViews += 1; 66 | 67 | // Write the response info 68 | string disableSubmit = !runServer ? "disabled" : ""; 69 | byte[] data = Encoding.UTF8.GetBytes(String.Format(pageData, pageViews, disableSubmit)); 70 | resp.ContentType = "text/html"; 71 | resp.ContentEncoding = Encoding.UTF8; 72 | resp.ContentLength64 = data.LongLength; 73 | 74 | // Write out to the response stream (asynchronously), then close it 75 | await resp.OutputStream.WriteAsync(data, 0, data.Length); 76 | resp.Close(); 77 | } 78 | } 79 | 80 | 81 | public static void Main(string[] args) 82 | { 83 | // Create a Http server and start listening for incoming connections 84 | listener = new HttpListener(); 85 | listener.Prefixes.Add(url); 86 | listener.Start(); 87 | Console.WriteLine("Listening for connections on {0}", url); 88 | 89 | // Handle requests 90 | Task listenTask = HandleIncomingConnections(); 91 | listenTask.GetAwaiter().GetResult(); 92 | 93 | // Close the listener 94 | listener.Close(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /02.HttpListener/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("02.HttpListener")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /03.b.TcpChatServer/03.b.TcpChatServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {D70B1782-FFDD-4A42-9597-DA856D42C105} 7 | Exe 8 | b.TcpChatServer 9 | 03.b.TcpChatServer 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /03.b.TcpChatServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("03.b.TcpChatServer")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /03.c.TcpChatMessenger/03.c.TcpChatMessenger.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {1E048B96-6C04-45DB-BD77-13B7021782FB} 7 | Exe 8 | c.TcpChatMessenger 9 | 03.c.TcpChatMessenger 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /03.c.TcpChatMessenger/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("03.c.TcpChatMessenger")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /03.c.TcpChatMessenger/TcpChatMessenger.cs: -------------------------------------------------------------------------------- 1 | // Filename: TcpChatMessenger.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.Text; 7 | using System.Net; 8 | using System.Net.Sockets; 9 | using System.Threading; 10 | 11 | namespace TcpChatMessenger 12 | { 13 | class TcpChatMessenger 14 | { 15 | // Connection objects 16 | public readonly string ServerAddress; 17 | public readonly int Port; 18 | private TcpClient _client; 19 | public bool Running { get; private set; } 20 | 21 | // Buffer & messaging 22 | public readonly int BufferSize = 2 * 1024; // 2KB 23 | private NetworkStream _msgStream = null; 24 | 25 | // Personal data 26 | public readonly string Name; 27 | 28 | public TcpChatMessenger(string serverAddress, int port, string name) 29 | { 30 | // Create a non-connected TcpClient 31 | _client = new TcpClient(); // Other constructors will start a connection 32 | _client.SendBufferSize = BufferSize; 33 | _client.ReceiveBufferSize = BufferSize; 34 | Running = false; 35 | 36 | // Set the other things 37 | ServerAddress = serverAddress; 38 | Port = port; 39 | Name = name; 40 | } 41 | 42 | public void Connect() 43 | { 44 | // Try to connect 45 | _client.Connect(ServerAddress, Port); // Will resolve DNS for us; blocks 46 | EndPoint endPoint = _client.Client.RemoteEndPoint; 47 | 48 | // Make sure we're connected 49 | if (_client.Connected) 50 | { 51 | // Got in! 52 | Console.WriteLine("Connected to the server at {0}.", endPoint); 53 | 54 | // Tell them that we're a messenger 55 | _msgStream = _client.GetStream(); 56 | byte[] msgBuffer = Encoding.UTF8.GetBytes(String.Format("name:{0}", Name)); 57 | _msgStream.Write(msgBuffer, 0, msgBuffer.Length); // Blocks 58 | 59 | // If we're still connected after sending our name, that means the server accepts us 60 | if (!_isDisconnected(_client)) 61 | Running = true; 62 | else 63 | { 64 | // Name was probably taken... 65 | _cleanupNetworkResources(); 66 | Console.WriteLine("The server rejected us; \"{0}\" is probably in use.", Name); 67 | } 68 | } 69 | else 70 | { 71 | _cleanupNetworkResources(); 72 | Console.WriteLine("Wasn't able to connect to the server at {0}.", endPoint); 73 | } 74 | } 75 | 76 | public void SendMessages() 77 | { 78 | bool wasRunning = Running; 79 | 80 | while (Running) 81 | { 82 | // Poll for user input 83 | Console.Write("{0}> ", Name); 84 | string msg = Console.ReadLine(); 85 | 86 | // Quit or send a message 87 | if ((msg.ToLower() == "quit") || (msg.ToLower() == "exit")) 88 | { 89 | // User wants to quit 90 | Console.WriteLine("Disconnecting..."); 91 | Running = false; 92 | } 93 | else if (msg != string.Empty) 94 | { 95 | // Send the message 96 | byte[] msgBuffer = Encoding.UTF8.GetBytes(msg); 97 | _msgStream.Write(msgBuffer, 0, msgBuffer.Length); // Blocks 98 | } 99 | 100 | // Use less CPU 101 | Thread.Sleep(10); 102 | 103 | // Check the server didn't disconnect us 104 | if (_isDisconnected(_client)) 105 | { 106 | Running = false; 107 | Console.WriteLine("Server has disconnected from us.\n:["); 108 | } 109 | } 110 | 111 | _cleanupNetworkResources(); 112 | if (wasRunning) 113 | Console.WriteLine("Disconnected."); 114 | } 115 | 116 | // Cleans any leftover network resources 117 | private void _cleanupNetworkResources() 118 | { 119 | _msgStream?.Close(); 120 | _msgStream = null; 121 | _client.Close(); 122 | } 123 | 124 | // Checks if a socket has disconnected 125 | // Adapted from -- http://stackoverflow.com/questions/722240/instantly-detect-client-disconnection-from-server-socket 126 | private static bool _isDisconnected(TcpClient client) 127 | { 128 | try 129 | { 130 | Socket s = client.Client; 131 | return s.Poll(10 * 1000, SelectMode.SelectRead) && (s.Available == 0); 132 | } 133 | catch(SocketException se) 134 | { 135 | // We got a socket error, assume it's disconnected 136 | return true; 137 | } 138 | } 139 | 140 | 141 | 142 | 143 | 144 | public static void Main(string[] args) 145 | { 146 | // Get a name 147 | Console.Write("Enter a name to use: "); 148 | string name = Console.ReadLine(); 149 | 150 | // Setup the Messenger 151 | string host = "localhost";//args[0].Trim(); 152 | int port = 6000;//int.Parse(args[1].Trim()); 153 | TcpChatMessenger messenger = new TcpChatMessenger(host, port, name); 154 | 155 | // connect and send messages 156 | messenger.Connect(); 157 | messenger.SendMessages(); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /03.d.TcpChatViewer/03.d.TcpChatViewer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {73E07A5D-9830-4EC0-BEAB-951870DD1159} 7 | Exe 8 | c.TcpChatViewer 9 | 03.d.TcpChatViewer 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /03.d.TcpChatViewer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("03.c.TcpChatViewer")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /04.b.TcpGamesServer/04.b.TcpGamesServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {30BDEC8D-83EC-4D3D-B12F-CA15BC34BFC2} 7 | Exe 8 | TcpGames 9 | 04.b.TcpGamesServer 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /04.b.TcpGamesServer/IGame.cs: -------------------------------------------------------------------------------- 1 | // Filename: IGame.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System.Net.Sockets; 6 | 7 | namespace TcpGames 8 | { 9 | interface IGame 10 | { 11 | #region Properties 12 | // Name of the game 13 | string Name { get; } 14 | 15 | // How many players are needed to start 16 | int RequiredPlayers { get; } 17 | #endregion // Properties 18 | 19 | #region Functions 20 | // Adds a player to the game (should be before it starts) 21 | bool AddPlayer(TcpClient player); 22 | 23 | // Tells the server to disconnect a player 24 | void DisconnectClient(TcpClient client); 25 | 26 | // The main game loop 27 | void Run(); 28 | #endregion // Functions 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /04.b.TcpGamesServer/Packet.cs: -------------------------------------------------------------------------------- 1 | // Filename: Packet.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using Newtonsoft.Json; 6 | 7 | namespace TcpGames 8 | { 9 | public class Packet 10 | { 11 | [JsonProperty("command")] 12 | public string Command { get; set; } 13 | 14 | [JsonProperty("message")] 15 | public string Message { get; set; } 16 | 17 | // Makes a packet 18 | public Packet(string command="", string message="") 19 | { 20 | Command = command; 21 | Message = message; 22 | } 23 | 24 | public override string ToString() 25 | { 26 | return string.Format( 27 | "[Packet:\n" + 28 | " Command=`{0}`\n" + 29 | " Message=`{1}`]", 30 | Command, Message); 31 | } 32 | 33 | // Serialize to Json 34 | public string ToJson() 35 | { 36 | return JsonConvert.SerializeObject(this); 37 | } 38 | 39 | // Deserialize 40 | public static Packet FromJson(string jsonData) 41 | { 42 | return JsonConvert.DeserializeObject(jsonData); 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /04.b.TcpGamesServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("04.b.TcpGamesServer")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /04.b.TcpGamesServer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /04.e.TcpGamesClient/04.e.TcpGamesClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {76423EEC-14BB-44B7-8B7D-30C43F1F539C} 7 | Exe 8 | TcpGames 9 | 04.e.TcpGamesClient 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll 36 | 37 | 38 | 39 | 40 | 41 | 42 | Packet.cs 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /04.e.TcpGamesClient/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("04.e.TcpGamesClient")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /04.e.TcpGamesClient/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /05.Compression/05.Compression.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {C5C9ED58-7910-44E8-991A-BB9AB6F1E4E5} 7 | Exe 8 | Compression 9 | 05.Compression 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | PreserveNewest 43 | 44 | 45 | PreserveNewest 46 | 47 | 48 | -------------------------------------------------------------------------------- /05.Compression/CompressionExample.cs: -------------------------------------------------------------------------------- 1 | // Filename: CompressionExample.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.IO.Compression; 9 | 10 | namespace CompressionExample 11 | { 12 | class CompressionExample 13 | { 14 | // Tiny helper to get a number in MegaBytes 15 | public static float ComputeSizeInMB(long size) 16 | { 17 | return (float)size / 1024f / 1024f; 18 | } 19 | 20 | public static void Main(string[] args) 21 | { 22 | // Our testing file 23 | string fileToCompress = "film.mp4"; 24 | byte[] uncompressedBytes = File.ReadAllBytes(fileToCompress); 25 | 26 | // Benchmarking 27 | Stopwatch timer = new Stopwatch(); 28 | 29 | // Display some information 30 | long uncompressedFileSize = uncompressedBytes.LongLength; 31 | Console.WriteLine("{0} uncompressed is {1:0.0000} MB large.", 32 | fileToCompress, 33 | ComputeSizeInMB(uncompressedFileSize)); 34 | 35 | // Compress it using Deflate (optimal) 36 | using (MemoryStream compressedStream = new MemoryStream()) 37 | { 38 | // init 39 | DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionLevel.Optimal, true); 40 | 41 | // Run the compression 42 | timer.Start(); 43 | deflateStream.Write(uncompressedBytes, 0, uncompressedBytes.Length); 44 | deflateStream.Close(); 45 | timer.Stop(); 46 | 47 | // Print some info 48 | long compressedFileSize = compressedStream.Length; 49 | Console.WriteLine("Compressed using DeflateStream (Optimal): {0:0.0000} MB [{1:0.00}%] in {2}ms", 50 | ComputeSizeInMB(compressedFileSize), 51 | 100f * (float)compressedFileSize / (float)uncompressedFileSize, 52 | timer.ElapsedMilliseconds); 53 | 54 | // cleanup 55 | timer.Reset(); 56 | } 57 | 58 | // Compress it using Deflate (fast) 59 | using (MemoryStream compressedStream = new MemoryStream()) 60 | { 61 | // init 62 | DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionLevel.Fastest, true); 63 | 64 | // Run the compression 65 | timer.Start(); 66 | deflateStream.Write(uncompressedBytes, 0, uncompressedBytes.Length); 67 | deflateStream.Close(); 68 | timer.Stop(); 69 | 70 | // Print some info 71 | long compressedFileSize = compressedStream.Length; 72 | Console.WriteLine("Compressed using DeflateStream (Fast): {0:0.0000} MB [{1:0.00}%] in {2}ms", 73 | ComputeSizeInMB(compressedFileSize), 74 | 100f * (float)compressedFileSize / (float)uncompressedFileSize, 75 | timer.ElapsedMilliseconds); 76 | 77 | // cleanup 78 | timer.Reset(); 79 | } 80 | 81 | // Compress it using GZip (save it) 82 | string savedArchive = fileToCompress + ".gz"; 83 | using (MemoryStream compressedStream = new MemoryStream()) 84 | { 85 | // init 86 | GZipStream gzipStream = new GZipStream(compressedStream, CompressionMode.Compress, true); 87 | 88 | // Run the compression 89 | timer.Start(); 90 | gzipStream.Write(uncompressedBytes, 0, uncompressedBytes.Length); 91 | gzipStream.Close(); 92 | timer.Stop(); 93 | 94 | // Print some info 95 | long compressedFileSize = compressedStream.Length; 96 | Console.WriteLine("Compressed using GZipStream: {0:0.0000} MB [{1:0.00}%] in {2}ms", 97 | ComputeSizeInMB(compressedFileSize), 98 | 100f * (float)compressedFileSize / (float)uncompressedFileSize, 99 | timer.ElapsedMilliseconds); 100 | 101 | // Save it 102 | using (FileStream saveStream = new FileStream(savedArchive, FileMode.Create)) 103 | { 104 | compressedStream.Position = 0; 105 | compressedStream.CopyTo(saveStream); 106 | } 107 | 108 | // cleanup 109 | timer.Reset(); 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /05.Compression/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("05.Compression")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /05.Compression/film.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/05.Compression/film.mp4 -------------------------------------------------------------------------------- /05.Compression/image.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/05.Compression/image.bmp -------------------------------------------------------------------------------- /06.a.UdpFileSender/06.d.UdpFileSender.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {E28D759D-2DE2-408D-89C3-70F4D00101C8} 7 | Exe 8 | UdpFileTransfer 9 | 06.a.UdpFileSender 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | PreserveNewest 49 | 50 | 51 | -------------------------------------------------------------------------------- /06.a.UdpFileSender/Block.cs: -------------------------------------------------------------------------------- 1 | // Filename: Block.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.Text; 7 | using System.Linq; 8 | 9 | namespace UdpFileTransfer 10 | { 11 | // These are the chunks of data that will be sent across the network 12 | public class Block 13 | { 14 | public UInt32 Number { get; set; } 15 | public byte[] Data { get; set; } = new byte[0]; 16 | 17 | #region Constructors 18 | // Creates a new block of data w/ the supplied number 19 | public Block(UInt32 number=0) 20 | { 21 | Number = number; 22 | } 23 | 24 | // Creates a Block from a byte array 25 | public Block (byte[] bytes) 26 | { 27 | // First four bytes are the number 28 | Number = BitConverter.ToUInt32(bytes, 0); 29 | 30 | // Data starts at byte 4 31 | Data = bytes.Skip(4).ToArray(); 32 | } 33 | #endregion // Constructors 34 | 35 | public override string ToString() 36 | { 37 | // Take some of the first few bits of data and turn that into a string 38 | String dataStr; 39 | if (Data.Length > 8) 40 | dataStr = Encoding.ASCII.GetString(Data, 0, 8) + "..."; 41 | else 42 | dataStr = Encoding.ASCII.GetString(Data, 0, Data.Length); 43 | 44 | return string.Format( 45 | "[Block:\n" + 46 | " Number={0},\n" + 47 | " Size={1},\n" + 48 | " Data=`{2}`]", 49 | Number, Data.Length, dataStr); 50 | } 51 | 52 | // Returns the data in the block as a byte array 53 | public byte[] GetBytes() 54 | { 55 | // Convert meta-data 56 | byte[] numberBytes = BitConverter.GetBytes(Number); 57 | 58 | // Join all the data into one bigger array 59 | byte[] bytes = new byte[numberBytes.Length + Data.Length]; 60 | numberBytes.CopyTo(bytes, 0); 61 | Data.CopyTo(bytes, 4); 62 | 63 | return bytes; 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /06.a.UdpFileSender/Files/short_message.txt: -------------------------------------------------------------------------------- 1 | Did I hear you right, did I hear you sayin' 2 | That you're gonna make a copy of a game without payin'? 3 | -------------------------------------------------------------------------------- /06.a.UdpFileSender/NetworkMessage.cs: -------------------------------------------------------------------------------- 1 | // Filename: NetworkMessage.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System.Net; 6 | 7 | namespace UdpFileTransfer 8 | { 9 | // This is a Simple datastructure that is used in packet queues 10 | public class NetworkMessage 11 | { 12 | public IPEndPoint Sender { get; set; } 13 | public Packet Packet { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /06.a.UdpFileSender/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("06.a.UdpFileSender")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /06.b.UdpFileReceiver/06.e.UdpFileReceiver.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {14B9BA4C-7F03-454D-A0E2-F7380328C99C} 7 | Exe 8 | UdpFileTransfer 9 | 06.b.UdpFileReceiver 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | Block.cs 40 | 41 | 42 | NetworkMessage.cs 43 | 44 | 45 | Packet.cs 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /06.b.UdpFileReceiver/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("06.b.UdpFileReceiver")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/07.e.UdpPongServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {5FD22E01-419A-4995-8679-72787430B3D9} 7 | Exe 8 | UdpPong 9 | 07.e.UdpPongServer 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | ..\packages\MonoGame.Framework.DesktopGL.3.5.1.1679\lib\net40\MonoGame.Framework.dll 36 | 37 | 38 | ..\packages\MonoGame.Framework.DesktopGL.3.5.1.1679\lib\net40\NVorbis.dll 39 | 40 | 41 | ..\packages\MonoGame.Framework.DesktopGL.3.5.1.1679\lib\net40\OpenTK.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/Ball.cs: -------------------------------------------------------------------------------- 1 | // Filename: Ball.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | 5 | using System; 6 | using Microsoft.Xna.Framework; 7 | using Microsoft.Xna.Framework.Graphics; 8 | using Microsoft.Xna.Framework.Content; 9 | 10 | namespace PongGame 11 | { 12 | // The ball that's bounced around 13 | public class Ball 14 | { 15 | // Statics 16 | public static Vector2 InitialSpeed = new Vector2(60f, 60f); 17 | 18 | // Private data members 19 | private Texture2D _sprite; 20 | private Random _random = new Random(); // Random Number Generator 21 | 22 | // Public data members 23 | public Vector2 Position = new Vector2(); 24 | public Vector2 Speed; 25 | public int LeftmostX { get; private set; } // Bounds 26 | public int RightmostX { get; private set; } 27 | public int TopmostY { get; private set; } 28 | public int BottommostY { get; private set; } 29 | 30 | // What gets hit 31 | public Rectangle CollisionArea 32 | { 33 | get { return new Rectangle(Position.ToPoint(), GameGeometry.BallSize); } 34 | } 35 | 36 | public void LoadContent(ContentManager content) 37 | { 38 | _sprite = content.Load("ball.png"); 39 | } 40 | 41 | // this is used to reset the postion of the ball to the center of the board 42 | public void Initialize() 43 | { 44 | // Center the ball 45 | Rectangle playAreaRect = new Rectangle(new Point(0, 0), GameGeometry.PlayArea); 46 | Position = playAreaRect.Center.ToVector2(); 47 | Position = Vector2.Subtract(Position, GameGeometry.BallSize.ToVector2() / 2f); 48 | 49 | // Set the velocity 50 | Speed = InitialSpeed; 51 | 52 | // Randomize direction 53 | if (_random.Next() % 2 == 1) 54 | Speed.X *= -1; 55 | if (_random.Next() % 2 == 1) 56 | Speed.Y *= -1; 57 | 58 | // Set bounds 59 | LeftmostX = 0; 60 | RightmostX = playAreaRect.Width - GameGeometry.BallSize.X; 61 | TopmostY = 0; 62 | BottommostY = playAreaRect.Height - GameGeometry.BallSize.Y; 63 | } 64 | 65 | // Moves the ball, should only be called on the server 66 | public void ServerSideUpdate(GameTime gameTime) 67 | { 68 | float timeDelta = (float)gameTime.ElapsedGameTime.TotalSeconds; 69 | 70 | // Add the distance 71 | Position = Vector2.Add(Position, timeDelta * Speed); 72 | } 73 | 74 | // Draws the ball to the screen, only called on the client 75 | public void Draw(GameTime gameTime, SpriteBatch spriteBatch) 76 | { 77 | spriteBatch.Draw(_sprite, Position); 78 | } 79 | } 80 | } 81 | 82 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/GameGeometry.cs: -------------------------------------------------------------------------------- 1 | // Filename: GameGeometry.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | 5 | using System; 6 | using Microsoft.Xna.Framework; 7 | 8 | namespace PongGame 9 | { 10 | // This is a class that contains all the information for the geometry 11 | // of the objects in the game (play area, paddle/ball sizes, etc.) 12 | public static class GameGeometry 13 | { 14 | public static readonly Point PlayArea = new Point(320, 240); // Client area 15 | public static readonly Vector2 ScreenCenter // Center point of the screen 16 | = new Vector2(PlayArea.X / 2f, PlayArea.Y / 2f); 17 | public static readonly Point BallSize = new Point(8, 8); // Size of Ball 18 | public static readonly Point PaddleSize = new Point(8, 44); // Size of the Paddles 19 | public static readonly int GoalSize = 12; // Width behind paddle 20 | public static readonly float PaddleSpeed = 100f; // Speed of the paddle, (pixels/sec) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/NetworkMessage.cs: -------------------------------------------------------------------------------- 1 | // Filename: NetworkMessage.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | 5 | using System; 6 | using System.Net; 7 | 8 | namespace PongGame 9 | { 10 | // Data structure used to store Packets along with their sender 11 | public class NetworkMessage 12 | { 13 | public IPEndPoint Sender { get; set; } 14 | public Packet Packet { get; set; } 15 | public DateTime ReceiveTime { get; set; } 16 | } 17 | } 18 | 19 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/PlayerInfo.cs: -------------------------------------------------------------------------------- 1 | // Filename: PlayerInfo.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | 5 | using System; 6 | using System.Net; 7 | 8 | namespace PongGame 9 | { 10 | // Data structure for the server to manage clients 11 | public class PlayerInfo 12 | { 13 | public Paddle Paddle; 14 | public IPEndPoint Endpoint; 15 | public DateTime LastPacketReceivedTime = DateTime.MinValue; // From Server Time 16 | public DateTime LastPacketSentTime = DateTime.MinValue; // From Server Time 17 | public long LastPacketReceivedTimestamp = 0; // From Client Time 18 | public bool HavePaddle = false; 19 | public bool Ready = false; 20 | 21 | public bool IsSet { 22 | get { return Endpoint != null; } 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("07.e.UdpPongServer")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/ThreadSafe.cs: -------------------------------------------------------------------------------- 1 | // Filename: ThreadSafe.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | 5 | using System; 6 | 7 | namespace PongGame 8 | { 9 | // Makes one variable thread safe 10 | public class ThreadSafe 11 | { 12 | // The data & lock 13 | private T _value; 14 | private object _lock = new object(); 15 | 16 | // How to get & set the data 17 | public T Value 18 | { 19 | get 20 | { 21 | lock (_lock) 22 | return _value; 23 | } 24 | 25 | set { 26 | lock (_lock) 27 | _value = value; 28 | } 29 | } 30 | 31 | // Initializes the value 32 | public ThreadSafe(T value = default(T)) 33 | { 34 | Value = value; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /07.e.UdpPongServer/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /07.g.UdpPongClient/07.g.UdpPongClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {2CB992D3-8E60-44F5-B833-98204CA46FBD} 7 | Exe 8 | PongGame 9 | 07.g.UdpPongClient 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | false 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | false 30 | x86 31 | 32 | 33 | 34 | 35 | ..\packages\MonoGame.Framework.DesktopGL.3.5.1.1679\lib\net40\MonoGame.Framework.dll 36 | 37 | 38 | ..\packages\MonoGame.Framework.DesktopGL.3.5.1.1679\lib\net40\NVorbis.dll 39 | 40 | 41 | ..\packages\MonoGame.Framework.DesktopGL.3.5.1.1679\lib\net40\OpenTK.dll 42 | 43 | 44 | 45 | 46 | 47 | 48 | PreserveNewest 49 | 50 | 51 | 52 | PreserveNewest 53 | 54 | 55 | PreserveNewest 56 | 57 | 58 | PreserveNewest 59 | 60 | 61 | PreserveNewest 62 | 63 | 64 | 65 | PreserveNewest 66 | 67 | 68 | PreserveNewest 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | Ball.cs 78 | 79 | 80 | GameGeometry.cs 81 | 82 | 83 | NetworkMessage.cs 84 | 85 | 86 | Packet.cs 87 | 88 | 89 | Paddle.cs 90 | 91 | 92 | ThreadSafe.cs 93 | 94 | 95 | -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/ball-hit.bfxrsound: -------------------------------------------------------------------------------- 1 | 2,0.5,,0.11,,0.32,0.2,0.45,,,,,,,,,,,,,,,,,,1,,,0.1,,,,masterVolume -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/ball-hit.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/07.g.UdpPongClient/Content/ball-hit.wav -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/07.g.UdpPongClient/Content/ball.png -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/establishing-connection-msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/07.g.UdpPongClient/Content/establishing-connection-msg.png -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/game-over-msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/07.g.UdpPongClient/Content/game-over-msg.png -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/paddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/07.g.UdpPongClient/Content/paddle.png -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/score.bfxrsound: -------------------------------------------------------------------------------- 1 | 4,0.5,,0.0459,0.35,0.585,0.145,0.845,0.25,-0.01,,,,,,,,,,,,,,,,1,,,,,,,masterVolume -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/score.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/07.g.UdpPongClient/Content/score.wav -------------------------------------------------------------------------------- /07.g.UdpPongClient/Content/waiting-for-game-start-msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/define-private-public/CSharpNetworking/e5d6a6ccd69e4bcb447977b296ea1d314f420e5f/07.g.UdpPongClient/Content/waiting-for-game-start-msg.png -------------------------------------------------------------------------------- /07.g.UdpPongClient/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /08.Ping/08.Ping.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {E0FDD020-2683-473C-A176-5C21DB912103} 7 | Exe 8 | Ping 9 | 08.Ping 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /08.Ping/PingExample.cs: -------------------------------------------------------------------------------- 1 | // Filename: PingExample.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.Threading; 7 | using System.Net.NetworkInformation; 8 | 9 | namespace PingExample 10 | { 11 | // This is an example of how to use Ping both sychrnously 12 | // and asychronously (with callbacks instead of Tasks). 13 | class PingExample 14 | { 15 | private static string _hostname; 16 | private static int _timeout = 2000; // 2 seconds in milliseconds 17 | private static object _consoleLock = new object(); 18 | 19 | // Locks console output and prints info about a PingReply (with color) 20 | public static void PrintPingReply(PingReply reply, ConsoleColor textColor) 21 | { 22 | lock(_consoleLock) 23 | { 24 | Console.ForegroundColor = textColor; 25 | 26 | Console.WriteLine("Got ping response from {0}", _hostname); 27 | Console.WriteLine(" Remote address: {0}", reply.Address); 28 | Console.WriteLine(" Roundtrip Time: {0}", reply.RoundtripTime); 29 | Console.WriteLine(" Size: {0} bytes", reply.Buffer.Length); 30 | Console.WriteLine(" TTL: {0}", reply.Options.Ttl); 31 | 32 | Console.ResetColor(); 33 | } 34 | } 35 | 36 | // A callback for doing an Asynchronous ping 37 | public static void PingCompletedHandler(object sender, PingCompletedEventArgs e) 38 | { 39 | // Canceled, error, or fine? 40 | if (e.Cancelled) 41 | Console.WriteLine("Ping was canceled."); 42 | else if (e.Error != null) 43 | Console.WriteLine("There was an error with the Ping, reason={0}", e.Error.Message); 44 | else 45 | PrintPingReply(e.Reply, ConsoleColor.Cyan); 46 | 47 | // Notify the calling thread 48 | AutoResetEvent waiter = (AutoResetEvent)e.UserState; 49 | waiter.Set(); 50 | } 51 | 52 | // Performs a Synchronous Ping 53 | public static void SendSynchronousPing(Ping pinger, ConsoleColor textColor) 54 | { 55 | PingReply reply = pinger.Send(_hostname, _timeout); // will block for at most 2 seconds 56 | if (reply.Status == IPStatus.Success) 57 | PrintPingReply(reply, ConsoleColor.Magenta); 58 | else 59 | { 60 | Console.WriteLine("Synchronous Ping to {0} failed:", _hostname); 61 | Console.WriteLine(" Status: {0}", reply.Status); 62 | } 63 | } 64 | 65 | public static void Main(string[] args) 66 | { 67 | // Setup the pinger 68 | Ping pinger = new Ping(); 69 | pinger.PingCompleted += PingCompletedHandler; 70 | 71 | // Poll the user where to send the Ping to 72 | Console.Write("Send a Ping to whom: "); 73 | _hostname = Console.ReadLine(); 74 | 75 | // Send async (w/ callback) 76 | AutoResetEvent waiter = new AutoResetEvent(false); // Set to not-signaled 77 | pinger.SendAsync(_hostname, waiter); 78 | 79 | // Check immediately for the async ping 80 | if (waiter.WaitOne(_timeout) == false) 81 | { 82 | pinger.SendAsyncCancel(); 83 | Console.WriteLine("Async Ping to {0} timed out.", _hostname); 84 | } 85 | 86 | // Send it synchronously 87 | SendSynchronousPing(pinger, ConsoleColor.Magenta); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /08.Ping/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("08.Ping")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /09.b.BerkelySocketCCode/09.b.BerkelySocketCCode.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {05C245AA-A126-4849-BDFD-209B02A2B9D8} 7 | Exe 8 | b.BerkelySocketCCode 9 | 09.b.BerkelySocketCCode 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | false 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | false 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /09.b.BerkelySocketCCode/Makefile: -------------------------------------------------------------------------------- 1 | # Filename: Makefile 2 | # Author: Benjamin N. Summerton 3 | # License: Unlicense (https://unlicense.org/) 4 | 5 | # Change the value you're preferred c compiler (e.g. `clang` or `gcc`) 6 | CC=clang 7 | 8 | all: server client 9 | 10 | server: server.c 11 | $(CC) -o server server.c 12 | 13 | client: client.c 14 | $(CC) -o client client.c 15 | 16 | clean: 17 | rm server 18 | rm client 19 | 20 | -------------------------------------------------------------------------------- /09.b.BerkelySocketCCode/client.c: -------------------------------------------------------------------------------- 1 | // Filename: client.c 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | // 5 | // Adapted From: 6 | // https://en.wikibooks.org/wiki/C_Programming/Networking_in_UNIX 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | const int MAX_RECV_LEN = 255; 20 | const int PORT_NUM = 6000; 21 | 22 | 23 | // Main method 24 | int main(int argc, char *argv[]) { 25 | char buffer[MAX_RECV_LEN + 1]; 26 | int len, clientSocket; 27 | struct sockaddr_in serv; 28 | 29 | // Create a TCP/IP socket 30 | clientSocket = socket(AF_INET, SOCK_STREAM, 0); 31 | memset(&serv, 0, sizeof(serv)); 32 | serv.sin_family = AF_INET; 33 | serv.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // 127.0.0.1 (a.k.a. localhost) 34 | serv.sin_port = htons(PORT_NUM); 35 | 36 | // Connect to the server 37 | printf("Connecting to the server...\n"); 38 | connect(clientSocket, (struct sockaddr *)&serv, sizeof(struct sockaddr)); 39 | 40 | // Get a message (blocks) 41 | len = recv(clientSocket, buffer, MAX_RECV_LEN, 0); 42 | buffer[len] = '\0'; // Null terminate the string 43 | printf("Got a message from the server [%i bytes]:\n%s", len, buffer); 44 | 45 | // cleanup 46 | close(clientSocket); 47 | return 0; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /09.b.BerkelySocketCCode/server.c: -------------------------------------------------------------------------------- 1 | // Filename: server.c 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | // 5 | // Adapted From: 6 | // https://en.wikibooks.org/wiki/C_Programming/Networking_in_UNIX 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | const int PORT_NUM = 6000; 21 | int running = 0; 22 | int serverSocket = 0; // Socket to listen for incoming connections 23 | 24 | 25 | // SIGINT handler 26 | void sigintHandler(int sig) { 27 | if (sig == SIGINT) { 28 | printf("Received SIGINT, shutting down server.\n"); 29 | 30 | // Cleanup 31 | running = 0; 32 | if (serverSocket) 33 | close(serverSocket); 34 | 35 | // end program 36 | exit(0); 37 | } 38 | } 39 | 40 | 41 | // Main Method 42 | int main(int argc, char *argv[]) { 43 | char *msg = "Hello, Client!\n"; 44 | 45 | struct sockaddr_in dest; // socket info about remote machine 46 | struct sockaddr_in serv; // socket info about us 47 | int clientSocket; // Socket FD for the remote client 48 | socklen_t socksize = sizeof(struct sockaddr_in); 49 | 50 | // Init and create the socket 51 | memset(&serv, 0, sizeof(serv)); // Zero out struct before filling 52 | serv.sin_family = AF_INET; // Mark as TCP/IP 53 | serv.sin_addr.s_addr = htonl(INADDR_ANY); // Put it on any interface 54 | serv.sin_port = htons(PORT_NUM); // Set server port number 55 | 56 | serverSocket = socket(AF_INET, SOCK_STREAM, 0); 57 | 58 | // Bind serv information to the socket 59 | bind(serverSocket, (struct sockaddr *)&serv, sizeof(struct sockaddr)); 60 | 61 | // Start listening for new connections (queue of 5 max) 62 | listen(serverSocket, 5); 63 | 64 | // Setup SIGINT handler 65 | if (signal(SIGINT, sigintHandler) != SIG_ERR) { 66 | running = 1; 67 | printf("Running the TCP server.\n"); 68 | } 69 | 70 | // Main loop 71 | while (running) { 72 | // Wait for a new client (blocks) 73 | clientSocket = accept(serverSocket, (struct sockaddr *)&dest, &socksize); 74 | 75 | // print some info about the remote client 76 | printf("Incoming connection from %s, replying.\n", inet_ntoa(dest.sin_addr)); 77 | 78 | // Send a reply (blocks) 79 | send(clientSocket, msg, strlen(msg), 0); 80 | 81 | // Close the connection 82 | close(clientSocket); 83 | } 84 | 85 | return 0; 86 | } 87 | 88 | -------------------------------------------------------------------------------- /09.c.TcpSocketClientExample/09.c.TcpSocketClientExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {BF8A9E7D-CA03-439A-BA6E-E2D545204D11} 7 | Exe 8 | c.TcpSocketClientExample 9 | 09.c.TcpSocketClientExample 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /09.c.TcpSocketClientExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("09.c.TcpSocketClientExample")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /09.c.TcpSocketClientExample/TcpSocketClientExample.cs: -------------------------------------------------------------------------------- 1 | // Filename: TcpSocketClientExample.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | // 5 | // Adapted & Ported From: 6 | // https://en.wikibooks.org/wiki/C_Programming/Networking_in_UNIX 7 | 8 | using System; 9 | using System.Text; 10 | using System.Net; 11 | using System.Net.Sockets; 12 | 13 | namespace Client 14 | { 15 | class TcpSocketClientExample 16 | { 17 | public static int MaxReceiveLength = 255; 18 | public static int PortNumber = 6000; 19 | 20 | 21 | // Main method 22 | public static void Main(string[] args) 23 | { 24 | int len; 25 | byte[] buffer = new byte[MaxReceiveLength + 1]; 26 | 27 | // Create a TCP/IP Socket 28 | Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 29 | IPEndPoint serv = new IPEndPoint(IPAddress.Loopback, PortNumber); 30 | 31 | // Connect to the server 32 | Console.WriteLine("Connecting to the server..."); 33 | clientSocket.Connect(serv); 34 | 35 | // Get a message (blocks) 36 | len = clientSocket.Receive(buffer); 37 | Console.Write("Got a message from the server[{0} bytes]:\n{1}", 38 | len, Encoding.ASCII.GetString(buffer, 0, len)); 39 | 40 | // Cleanup 41 | clientSocket.Close(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /09.c.TcpSocketServerExample/09.c.TcpSocketServerExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {87F99A1D-9BEA-4140-A703-759E629DC46E} 7 | Exe 8 | c.TcpSocketServerExample 9 | 09.c.TcpSocketServerExample 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /09.c.TcpSocketServerExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("09.c.TcpSocketServerExample")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /09.c.TcpSocketServerExample/TcpSocketServerExample.cs: -------------------------------------------------------------------------------- 1 | // Filename: TcpSocketServerExample.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (https://unlicense.org/) 4 | // 5 | // Adapted & Ported From: 6 | // https://en.wikibooks.org/wiki/C_Programming/Networking_in_UNIX 7 | 8 | using System; 9 | using System.Text; 10 | using System.Net; 11 | using System.Net.Sockets; 12 | 13 | namespace Server 14 | { 15 | class TcpSocketServerExample 16 | { 17 | public static int PortNumber = 6000; 18 | public static bool Running = false; 19 | public static Socket ServerSocket; 20 | 21 | 22 | // An interrupt handler for Ctrl-C presses 23 | public static void InterruptHandler(object sender, ConsoleCancelEventArgs args) 24 | { 25 | Console.WriteLine("Received SIGINT, shutting down server."); 26 | 27 | // Cleanup 28 | Running = false; 29 | ServerSocket.Shutdown(SocketShutdown.Both); 30 | ServerSocket.Close(); 31 | } 32 | 33 | 34 | // Main method 35 | public static void Main(string[] args) 36 | { 37 | Socket clientSocket; 38 | byte[] msg = Encoding.ASCII.GetBytes("Hello, Client!\n"); 39 | 40 | // Set the endpoint options 41 | IPEndPoint serv = new IPEndPoint(IPAddress.Any, PortNumber); 42 | 43 | ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 44 | ServerSocket.Bind(serv); 45 | 46 | // Start listening for connections (queue of 5 max) 47 | ServerSocket.Listen(5); 48 | 49 | // Setup the Ctrl-C 50 | Console.CancelKeyPress += InterruptHandler; 51 | Running = true; 52 | Console.WriteLine("Running the TCP server."); 53 | 54 | // Main loop 55 | while (Running) 56 | { 57 | // Wait for a new client (blocks) 58 | clientSocket = ServerSocket.Accept(); 59 | 60 | // Print some infor about the remote client 61 | Console.WriteLine("Incoming connection from {0}, replying.", clientSocket.RemoteEndPoint); 62 | 63 | // Send a reply (blocks) 64 | clientSocket.Send(msg, SocketFlags.None); 65 | 66 | // Close the connection 67 | clientSocket.Close(); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /CSharpNetworking.userprefs: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /DnsExample/DnsExample.cs: -------------------------------------------------------------------------------- 1 | // Filename: DnsExample.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.Net; 7 | 8 | namespace DnsExample 9 | { 10 | class DnsExample 11 | { 12 | public static string domain = "16bpp.net"; 13 | 14 | public static void Main(string[] args) 15 | { 16 | // Print a little info about us 17 | Console.WriteLine("Local Hostname: {0}", Dns.GetHostName()); 18 | Console.WriteLine(); 19 | 20 | // Get DNS info synchronously 21 | IPHostEntry hostInfo = Dns.GetHostEntry(domain); 22 | 23 | // Print aliases 24 | if (hostInfo.Aliases.Length > 0) 25 | { 26 | Console.WriteLine("Aliases for {0}:", hostInfo.HostName); 27 | foreach (string alias in hostInfo.Aliases) 28 | Console.WriteLine(" {0}", alias); 29 | Console.WriteLine(); 30 | } 31 | 32 | // Print IP addresses 33 | if (hostInfo.AddressList.Length > 0) 34 | { 35 | Console.WriteLine("IP Addresses for {0}", hostInfo.HostName); 36 | foreach(IPAddress addr in hostInfo.AddressList) 37 | Console.WriteLine(" {0}", addr); 38 | Console.WriteLine(); 39 | } 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /DnsExample/DnsExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {EEF79935-9D21-4F81-9095-3B93411C5472} 7 | Exe 8 | DnsExample 9 | DnsExample 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /DnsExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("DnsExample")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /IPAddressExample/IPAddressExample.cs: -------------------------------------------------------------------------------- 1 | // Filename: IPAddressExample.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.Net; 7 | 8 | namespace IPAddressExample 9 | { 10 | class IPAddressExample 11 | { 12 | public static readonly byte[] ipv6 = { 13 | 0x20, 0x01, 14 | 0x0d, 0xb8, 15 | 0x00, 0x00, 16 | 0x00, 0x42, 17 | 0x00, 0x00, 18 | 0x8a, 0x2e, 19 | 0x03, 0x70, 20 | 0x73, 0x34 21 | }; 22 | 23 | public static void Main(string[] args) 24 | { 25 | // Make an IP address 26 | IPAddress ipAddr; 27 | ipAddr = new IPAddress(new byte[] {107, 70, 178, 215}); // IPv4, byte array 28 | //ipAddr = new IPAddress(ipv6); // IPv6, byte array 29 | //ipAddr = IPAddress.Parse("127.0.0.1"); // IPv4, string 30 | //ipAddr = IPAddress.Parse("::1"); // IPv6, string 31 | 32 | // Print some info 33 | Console.WriteLine("IPAddress: {0}", ipAddr); 34 | Console.WriteLine("Address Family: {0}", ipAddr.AddressFamily); 35 | Console.WriteLine("Loopback: {0}", IPAddress.IsLoopback(ipAddr)); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /IPAddressExample/IPAddressExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {D9EF0C15-6A4F-49C2-8D54-A57863F6D868} 7 | Exe 8 | IPAddressExample 9 | IPAddressExample 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /IPAddressExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("IPAddressExample")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /IPEndPointExample/IPEndPointExample.cs: -------------------------------------------------------------------------------- 1 | // Filename: IPEndPointExample.cs 2 | // Author: Benjamin N. Summerton 3 | // License: Unlicense (http://unlicense.org/) 4 | 5 | using System; 6 | using System.Net; 7 | 8 | namespace IPEndPointExample 9 | { 10 | class IPEndPointExample 11 | { 12 | public static void Main(string[] args) 13 | { 14 | // Print some static info 15 | Console.WriteLine("Min. Port: {0}", IPEndPoint.MinPort); 16 | Console.WriteLine("Max. Port: {0}", IPEndPoint.MaxPort); 17 | Console.WriteLine(); 18 | 19 | // Create one 20 | IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("107.70.178.215"), 6000); 21 | Console.WriteLine("Address: {0}", endPoint.Address); 22 | Console.WriteLine("Port: {0}", endPoint.Port); 23 | Console.WriteLine("Address Family: {0}", endPoint.AddressFamily); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /IPEndPointExample/IPEndPointExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | x86 6 | {A5206533-0E7E-461D-BC07-E62E224D466F} 7 | Exe 8 | IPEndPointExample 9 | IPEndPointExample 10 | v4.5 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug 17 | DEBUG; 18 | prompt 19 | 4 20 | true 21 | x86 22 | 23 | 24 | full 25 | true 26 | bin\Release 27 | prompt 28 | 4 29 | true 30 | x86 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /IPEndPointExample/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | 4 | // Information about this assembly is defined by the following attributes. 5 | // Change them to the values specific to your project. 6 | 7 | [assembly: AssemblyTitle("IPEndPointExample")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("")] 12 | [assembly: AssemblyCopyright("ben")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". 17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision, 18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision. 19 | 20 | [assembly: AssemblyVersion("1.0.*")] 21 | 22 | // The following attributes are used to specify the signing key for the assembly, 23 | // if desired. See the Mono documentation for more information about signing. 24 | 25 | //[assembly: AssemblyDelaySign(false)] 26 | //[assembly: AssemblyKeyFile("")] 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | C# Networking 2 | ============= 3 | 4 | This repo is the collection of source examples for the C# networking tutorials 5 | that can be found here: 6 | 7 | https://16bpp.net/tutorials/csharp-networking 8 | 9 | -------------------------------------------------------------------------------- /html/01.HttpClient.html: -------------------------------------------------------------------------------- 1 |

HttpClient

2 | 3 |

To start things off simple, were just going to make a small utility that will download a web page for us and save it.  To do this, we will use the HttpClient class.

4 | 5 |

 

6 | 7 |
8 |

You will need to add the System.Net.Http Assembly to your project's references.

9 |
10 | 11 |

 

12 | 13 |

HttpClient works by establishing a connection over TCP to get a response from any server that serves web pages (or responds to HTTP requests).  It's pretty simple to use, but you need to use asynchronous code.  This is probably a slightly more complex example of HttpClient, but it shows simple usage of async, await, and Task.

14 | 15 |

 

16 | 17 |

Our program first sets up a page to get, waits to hear back from the server (a 200 HTTP Response), converts the data to a UTF-8 encoded byte array, then lastly saves it to a file name we specify at the top.  In the Main() body, as soon as we call DownloadWebPage(), it starts to execute the code inside, but as soon as it hits an await statement execution returns back outside, waiting for the awaited statement to finish.  But since the call to Thread.Sleep() is there, it pauses execution inside the main thread for at least five seconds; this doesn't halt the downloading at all.  So if you're on a good internet connection, the web page should finish downloading before the five seconds are over.  If not, then the call to GetAwaiter().GetResult() at the end will block execution until the Task completes.

18 | 19 |

 

20 | 21 | 22 |

 

23 | 24 |

You might notice that there is a lengthier call to dlTask.GetAwaiter().GetResult() at the end of Main(), where we could have just called dlTask.Wait().  We do this because if an exception is thrown by the Task, we will get that original exception thrown to us instead of it being wrapped inside of a AggregateException that would be produced by the Wait()method.

25 | 26 |

 

27 | 28 |

Right now this program just downloads a single web page from a single resource.  A fun exercise for you would be to modify the code to download many different resources from any website.

29 | 30 |

I would like to thank AngularBeginner of Reddit for suggesting some code improvements.

-------------------------------------------------------------------------------- /html/03.b.tcp-chat-server.html: -------------------------------------------------------------------------------- 1 |

TCP Chat - Server

2 | 3 |

I've seen many networking tutorials that start you with the client code.  I think it's better to start off with the server code first.  And there is quite a bit here.

4 | 5 |

The main two classes that are used here are TcpClient & TcpListener.   In a nutshell, we use TcpListener to listen for incoming connections, then once someone has connected, we spin up a new TcpClient to talk to that remote process.

6 | 7 |

Here is the code for the server:

8 | 9 |

 

10 | 11 | 12 |

 

13 | 14 |

When instancing the class TcpChatServer, the listener is created but doesn't start listening until we call Start() on it in the Run() method.  IPAddress.Any, means "let anyone from any network interface connect to me."

15 | 16 |

The Run() method is the main heart of the program.  Like said above, when we make the call to _listener.Start(), the TcpListener will start listening for new incoming TCP connections the port we fed it (6000 our case).  If there is a new pending connection (checked via the Pending() method), we break into a function see who the new client is.

17 | 18 |

_handleNewConnection() does what the name implies.  After the connection has been established we can grab it's TcpClient instance by calling AcceptTcpClient in the listener, the client should send a message stating either viewer or name:MY_NAME.

19 | 20 |
    21 |
  • If we have a new Viewer, recognize that client as a viewer, and then send them a welcome message
  • 22 |
  • If we have a new Messenger, verify that MY_NAME hasn't been taken yet 23 |
      24 |
    • If so, enqueue a new message to tell the Viewers that a new friend has joined us
    • 25 |
    • Else, close the connection
    • 26 |
    27 |
  • 28 |
  • Anything else, say that we couldn't identify who they are, and then close the connection
  • 29 |
30 | 31 |

Back in Run(), we check for disconnects, new messages, and then send queued ones in the rest of the loop.  There is also a call to Thread.Sleep(10), so that we save on CPU resources.

32 | 33 |

_checkForDisconnects()  are two foreach loops that go through all of the clients and use the _isDisconnected() method to test for the FIN/ACK packets that might have been sent.

34 | 35 |

 

36 | 37 |
38 |

The Connected property of TcpClient might seem like the right thing to use here instead of _isDisconnected(), but there's a problem with that.  Connected returns true only if the last IO operation was a success.  This means that the FIN/ACK might have been sent, but since there were no IO operations on our end, Connected will still return true.  Be careful of this.

39 |
40 | 41 |

 

42 | 43 |

_checkForNewMessages() is also a foreach check of all the Messengers.  We can use the Available property to see how big their message is (in bytes), and then read that from the client's NetworkStream.

44 | 45 |

_sendMessages() empties the _messageQueue by writing the the Viewer's streams.

46 | 47 |

There is also the function _cleanupClient().  It's a small helper that closes both the TcpClient's NetworkStream and itself.

48 | 49 |

 

50 | 51 |
52 |

If you are wondering why we need to manually close the stream ourselves, is because the stream is created from the underlying Socket object (which can be accessed via TcpClient's Client property).  This means you need to clean it up manually.  Read more about it here.

53 |
-------------------------------------------------------------------------------- /html/03.c.tcp-chat-messenger.html: -------------------------------------------------------------------------------- 1 |

TCP Chat - Messenger

2 | 3 |

The Messenger is a lot smaller of a program that the server.  It connects, checks if its supplied name is okay, then lets the user spit messages until they want to quit.

4 | 5 |

 

6 | 7 | 8 |

 

9 | 10 |

There are many different constructors for TcpClient.   All of them, except for the one with no parameters, will try to connect to the supplied address & port as soon as its instantiated.  We use the one with no parameters for ours.

11 | 12 |

Our Connect() function is what will try to initialize the connection.  If we connected successfully, it will send send the message name:MY_NAME, where MY_NAME is what is provided by the user at run time.  If we're still connected after that, then we're good to start sending messages.  Note that if the user supplies a name that is the empty string, the server will boot them.

13 | 14 |

SendMessages() is our main loop for this program.  If we are connected (checked with the Running property), poll the user for some input.

15 | 16 |
    17 |
  • If the input is either quit or exit, start the process to disconnect from the chat server
  • 18 |
  • If the input is non-empty, then send the message to the server
  • 19 |
20 | 21 |

After that, take a 10 millisecond nap (like what the server does).  Once rested, verify that we're still connected by calling _isDisconnected() on our TcpClient.

22 | 23 |

_cleanupNetworkResources() is called just to make sure our NetworkStream is closed as well as the TcpClient we're using.

24 | -------------------------------------------------------------------------------- /html/03.d.tcp-chat-viewer.html: -------------------------------------------------------------------------------- 1 |

TCP Chat - Viewer

2 | 3 |

This is also a much tinier one like the Messenger, in fact, most of the code is pretty similar.  Once it has connected to the server, it will get a "hello," message, and then keep printing out messages until the user presses Ctrl-C.

4 | 5 |

 

6 | 7 |

 

8 | 9 |

Similar to the Messenger, we're using the constructor of TcpClient that doesn't start a connection.  We do that in our Connect() method.  If we're connected, we immediately send over the text viewerto tell it that we are, well, a Viewer client.  If we are still connected, then we're safe to assume that the server recognises us as a viewer and will send us messages.  As this point, the server has already sent us a message with "Welcome to the 'SERVER_NAME' Chat Server".  Though, we don't find out about it until we enter ListenForMessages().

10 | 11 |

In that function, if we are connected, we test to see if there is a new message or not.  If there is, TcpClient.Available will report how many bytes are incoming.  If so, we then try to read that many bytes from the NetworkStream.   The call to Read() will block until that many bytes have been read in.  Once the message is received, it will print it out to the user.  Like the other programs, we also sleep for 10 milliseconds to conserve CPU resources.  Then we do a check for that FIN/ACK packet and a check to see if the user pressed Ctrl-C.

12 | 13 |

Once we've exited the while(Running) loop, we clean up any network resources that we have left over.

14 | -------------------------------------------------------------------------------- /html/03.e.tcp-chat-recap.html: -------------------------------------------------------------------------------- 1 |

TCP Chat - Recap

2 | 3 |

And here we have the collection of programs running on many different Linux machines.  The one on the left is my laptop, in the middle is a cloud instance I spun up, and on the right is this server hosting the Server portion.  If you're wondering what my order of operations was:

4 | 5 |
    6 |
  1. I start the server on 16bpp.net, port 6969
  2. 7 |
  3. I connect with the Viewer & Messenger on my laptop
  4. 8 |
  5. I connect with the Viewer & Messenger on the cloud VM
  6. 9 |
  7. I have the "two users," exchange some playful banter
  8. 10 |
  9. I exit out on my laptop
  10. 11 |
  11. I then kill the server on 16bpp.net 12 |
      13 |
    • The Viewer & Messenger on the cloud VM then detect the FIN/ACK packets and close
    • 14 |
    15 |
  12. 16 |
17 | 18 |

 

19 | 20 |

Running the TCP Chat programs

21 | 22 |

 

23 | 24 |

With all that has been said, done, and typed, there are still a few things to go over.  This is not a good implementation of a internet chatting application.  Keep in mind my goal is to teach you the C# # APIs of System.Net.  Lets go over what's oh-so-very wrong:

25 | 26 |
    27 |
  1. The application protocol that was defined back here is pretty bad. 28 | 29 |
      30 |
    1. One thing that there should be, is a message where one end tells the other end that it's disconnecting.  I'm not talking about the FIN/ACK packets here.  I'm saying that an actual bye message should be sent by the clients/server to tell the other that it will disconnect.  Sure, the FIN/ACK packets will still be sent after the bye is sent, but this is a much more graceful method and more descriptive.  There are many applications out there (like online video games) that use this to tell if some has quit the application normally (i.e. ragequit) or not.
    2. 31 |
    3. There should also be a "heartbeat," message somewhere.  This is where the clients & server send each other an "I'm still alive," message every couple of seconds (e.g. 30).  These are typically very tiny messages that would just have something such as the time the time the heartbeat was sent, but sometimes I've seen people stuff some extra metadata into them.  Pings can also double as these too
    4. 32 |
    5. The server should also be sending error messages to clients.  Say for example if a Messenger tries to use a name that is already taken, the server should respond with "Error, name taken by someone else." before closing the connection.
    6. 33 |
    34 |
  2. 35 |
  3. 36 |
    37 |

    Look at the function _handleNewConnection() in TcpChatServer.cs, there's something really-really-bad about it.  Walk through it and see if you can spot the really-really-bad problem.  Hint: netStream.Read()

    38 | 39 |

    If a client connects, but doesn't send a message. The server is just going to hold here. Forever. We didn't set any timeouts in the code, so by default the stream will just keep trying to read for a really-really-long time.
    40 |
    41 | Someone could just telnet into the Chat Server, but not send a message to identify themselves. This would hold the server hostage.

    42 | 43 |

    44 |
    45 |
  4. 46 |
  5. We should be using timeouts when reading data from the network.  Right now the NetworkStreams  are waiting for data indefinitely.  Take a look at the timeout related properties of NetworkStream.
  6. 47 |
  7. Split the user-end application into two things (Viewer and Messenger) isn't the most user friendly thing to do.  It's nice for those that just want to listen in, but a hassle for those who want to send & receive messages.  This was done though so we wouldn't have to use any GUI code for this application.
  8. 48 |
  9. Any real world networking application should use async code, be multi-threaded, and have timeouts; there is none here.  Normally, servers will spin up a new thread or process when a new client connects, so it can handle both the newbie as well as the currently connected clients.  We could have also used the asynchronous read/write methods on NetworkStream.
  10. 49 |
50 | 51 |
52 |

 

53 |
54 | 55 |

 

56 | 57 |

If you have anything to say about this, send me a message.  There might have been some major flaws that I've missed.  I would like to hear about them and put them up here.  Also, if you want to have your own fun and try to make this server multi-threade, asynchronous and time'd-out, feel free to do that and send me a link to the code when you're done.  It would be nice to see a "more correct," version of this.

58 | 59 |

This also was a much more lengthy example than my other stuff.  I'll try to make the rest of them a bit simpler.

60 | -------------------------------------------------------------------------------- /html/04.a.tcp-games.html: -------------------------------------------------------------------------------- 1 |

TCP Games

2 | 3 |

Alright.  Since we've already done a single-threaded networked application, what is there to do next?  Why a multithreaded one of course!  But let's go the extra mile and use some async code too.  This one turned out to be more complex than I envisioned, but bear with me because it can do more.  Hold tight, it will be fun.

4 | 5 |

Please note that this section does not have any async Socket programming.  That's something completely different and it will be covered in a later section.

6 | 7 |

I always find video games as a great way of teaching programming concepts, languages, and libraries.  But since I want to keep this tutorial series as "GUI-less," as possible we're going to do some text (console) based gaming.  Get ready to party like its 1989 because George H. W. Bush has been elected, Tim Burton is directing a Batman film, Milli Vanilli is topping the charts,  and Game Boys are all the rage;  We're making a BBS game!! (sort-of)

8 | 9 |

 

10 | 11 |

12 | 13 |

Pictured: 1989

14 | 15 |

 

16 | 17 |
18 |

Just as a side note, if you want to make a real video game, it would be much, MUCH better to use UDP instead of TCP for most cases.  TCP here is optimal since we're doing text based gaming only and any of the games that we have on the server should be turn-based.  UDP is a much better choice if your networked game runs in real time.

19 |
20 | 21 |
22 |

 

23 |
24 | 25 |

 

26 | 27 |

Before Starting

28 | 29 |

If you haven't read the previous section, TCP Chat, go do so so you can familiarise yourself with TcpClient and TcpListener.  Part of the design of the server side is so that we can switch out different games.  Because of time constraints, I've only implemented one game called "Guess My Number," which is single player.  I also wanted to do Tic-Tac-Toe as well (multi-player), but I'll leave that an exercise for you.  It shouldn't be too difficult.

30 | 31 |

We only should have two separate projects in our solution for this section.  One being the TcpGamesServer and the other the TcpGamesClient.  Our namespace for both will be TcpGames.

32 | 33 |

We also will be using the same _isDisconnected() method to check for ungraceful disconnects.  Note that it's name is slightly different in the server code.  Here it is again:

34 | 35 |

 

36 | -------------------------------------------------------------------------------- /html/04.b.tcp-games-application-protocol.html: -------------------------------------------------------------------------------- 1 |

TCP Games - Application Protocol

2 | 3 |

Let's first talk about how our clients and server are going to pass around messages/packets.  To make this more structured than last time, we will be using JSON, which will only contain plaintext.  Each JSON object will only have two fields, command and message.

4 | 5 |
    6 |
  • command: this tells the client/server how the packet should be processed
  • 7 |
  • message: this adds some extra context for the command.  It may be an empty string sometimes
  • 8 |
9 | 10 |

 

11 | 12 |

We have only three simple commands for each type of packet:

13 | 14 |
    15 |
  1. bye: this can be sent by either the server or the client.  It notifies the other end that the sender is disconnecting gracefully.  16 | 17 |
      18 |
    • If sent by the server to the client, the client should display the contained message and clean up its network resources
    • 19 |
    • If sent by the client to the server, the server can choose what to do with the message (if there is one).  It should also clean up any resources associated with the client
      20 |  
    • 21 |
    22 |
  2. 23 |
  3. message: note that this is a command.  It just sends a plaintext message 24 |
      25 |
    • If sent by the server to the client, the client should print out the contents of the message field
    • 26 |
    • If sent by the client to the server, the server can chose to ignore it.  The client shouldn't be sending message packets anyways
      27 |  
    • 28 |
    29 |
  4. 30 |
  5. input:  requests some input 31 |
      32 |
    • If sent by the server to the client, the client should ask the user for some input 33 |
        34 |
      • the contents of the message field should be treated as a prompt for the user
      • 35 |
      36 |
    • 37 |
    • If sent by the client to the server, this should only be in response to an input request 38 |
        39 |
      • If this wasn't a response to a request, the server should just ignore the packet then
      • 40 |
      41 |
    • 42 |
    43 |
  6. 44 |
45 | 46 |

 

47 | 48 |

If you're a little confused, here's a diagram of how our apps will operate:

49 | 50 |

51 | 52 |
53 |

 

54 |
55 | 56 |

 

57 | 58 |

Packet class

59 | 60 |

Here is our Packet class.  ne sure to add it to both projects in your solution.  The main reason we have this is so we can access the data in a more C# like way.  We will be using Newtonsoft's Json.NET library for parsing.

61 | 62 |

 

63 | 64 | 65 |

 

66 | 67 |

How it will be sent over the network

68 | 69 |

Before we go and shove the Packet into the internet tubes there is a little pre-processing that needs to be done first:

70 | 71 |
    72 |
  1. Call the ToJson() method on the Packet to get it as a string.  Then after that, encode it (in UTF-8) into a byte array
  2. 73 |
  3. Get how many bytes are in that first byte array, then encode that number as an unsigned 16 bit integer into a second byte array.  The resulting array should be exactly two bytes long
  4. 74 |
  5. Join those two arrays into a new one with the "length buffer," ahead of the "JSON buffer,"
  6. 75 |
76 | 77 |

That final byte array that we have is what will be sent over the network.  This way whoever receives a Packet knows how long it is.

78 | 79 |

 

80 | 81 |
82 |

You might be wondering why we have to do this.  Here's the thing, TcpClient.Available will report how many bytes have been received but not read yet, and only that.   There is the possibility that two Packets may have been received before our app has had a chance to read from the NetworkStream.  This poses the question of how we will pull out the data into two separate Packets.

83 | 84 |

Since we are using JSON, we could match first level curly brackets.  Instead, stuffing the length of the "true message," right before the actual message is a common technique that is used, and thus it's best that we practice that here.

85 |
86 | 87 |

 

88 | 89 |

I chose to use unsigned an unsigned 16 bit integer because it lets us have JSON strings that are almost 64 KB long; that's plenty of data for text based games.  We could use just an 8 bit unsigned integer too but that will only give us 255 bytes max, so our messages we send would have to be quite tiny.  

90 | -------------------------------------------------------------------------------- /html/04.d.tcp-games-guess-my-number.html: -------------------------------------------------------------------------------- 1 |

TCP Games - Guess My Number

2 | 3 |

Guess My Number is a very simple game.  It's played by only one player.  The server will think of a number between 1 and 100 (included) and then ask the client to guess it.  It will tell the client if their guess was too high or too low.  Once the user guesses correctly the game will then tell the server to disconnect the player.  Keep in mind the Run() method is run in a separate thread.  While the rest of the server is asynchronous and multithreaded, we're going to run stuff synchronously and single threaded here.

4 | 5 |

 

6 | 7 | 8 |

 

9 | 10 |

Like said before, it needs to implement the IGame interface.  At the top we store a pointer to the TcpGamesServer that is running this game.  This way we can access the server's methods to transmit Packets.  We simply call our game "Guess My Number," with the Name property.  And the RequiredPlayers only is one.  In our constructor we also initialize a random number generator.

11 | 12 |

AddPlayer() will only accept one player for the game.  If another one is provided, we ignore them and return false.

13 | 14 |

DisconnectClient() is a notifying method.  In case we have one and it's our player, then we need to disconnect them (thus also quitting the Game too).

15 | 16 |

The Run() method will not start the game unless we have a player.  If we do, we send them a message Packet containing the rules.  Then we generate our number.  In the main game loop we send the user a request for input.  Since ReceivePacket() might return a null, we keep checking in a loop until we have one.  Our Game has to check for disconnects since we're now handling the client.  If is there is a graceful one we mark it.  If we get a response to our input request, we then check it.  Based on the input, we see if they were right or wrong and send them a message based upon that.  At the end of the main game loop we check for our disconnects (graceful or not).

17 | 18 |

Once the client has disconnected or guessed the right number, we exit the Run() function.  And then our game is over.

19 | -------------------------------------------------------------------------------- /html/04.e.tcp-games-client.html: -------------------------------------------------------------------------------- 1 |

TCP Games - Client

2 | 3 |

Now the last thing that we have to do is write the client code.  Remember to add the Packet to the client project.

4 | 5 |

 

6 | 7 |
8 |

Don't forget to include the Newtonsoft.Json package in this project too.

9 |
10 | 11 |

 

12 | 13 | 14 |

 

15 | 16 |

This code is somewhat similar to the clients that we wrote for the TCP Chat application.  Because of the packets, we have added a Dictionary at the top the store functions and to dispatch messages that we've received.  In the constructor we setup the TcpClient (but don't connect it) and init some other resources.  _cleanupNetworkResources() will close the TcpClient for us and it's NetworkStream.

17 | 18 |

The Connect() method is a bit more robust than last time.  First it will try to create the connection in a try-catch block.  If it wasn't successful, then nothing else will work.  If successful, we yank out the underlying NetworkStream and hook up our Packet dispatchers.  Disconnect() will attempt a graceful disconnect with the server by sending a bye Packet.

19 | 20 |

The Run() method of this class is also the most important part.  If we're connected, _handleIncomingPackets() will do what the name says.  Then after that there is a short nap to save on CPU usage and a check for any type of disconnect.  After the main loop, we give it one second for any other incoming packets to be handled.  Lastly, we clean up the network resources.

21 | 22 |

_sendPacket() will asynchronously send a Packet over to the server.  Likewise _handleIncomingPackets() will check for available messages and then dispatch them to their correct handlers.

23 | 24 |

Since we have three different types of commands that can be sent in a Packet, we've got three handlers:

25 | 26 |
    27 |
  • _handlebye() will shutdown the client
  • 28 |
  • _handleMessage() will print out the contents of the message field to the console
  • 29 |
  • _handleInput() will request the user for some input from the command line and then send a response to the server
  • 30 |
31 | 32 |

 

33 | 34 |

_isDisconnected() is used to check for ungraceful disconnects from the server (like always).  And the last bits of the file are for program execution.

-------------------------------------------------------------------------------- /html/04.f.tcp-games-recap.html: -------------------------------------------------------------------------------- 1 |

TCP Games - Recap

2 | 3 |

I didn't feel like putting this up on my server, so here's it running locally.  The order of operations were:

4 | 5 |
    6 |
  1. Startup the server (top box)
  2. 7 |
  3. Connect with a client (bottom left), the server starts a Game
  4. 8 |
  5. Connect with a second client (bottom right), the server starts a second Game
  6. 9 |
  7. I fool around a bit until I get the correct answer in the bottom left client
  8. 10 |
  9. I then press Ctrl-C in the top box, shutting down the server, killing any running games and disconnecting (gracefully) any clients
  10. 11 |
12 | 13 |

 

14 | 15 |

16 | 17 |

 

18 | 19 |

As always, there are some problems with anything we do and a few things here we can improve on:

20 | 21 |
    22 |
  1. I mentioned in the server programming part that there was a possible race condition with handling graceful client disconnects.  To repeat myself, it was because the server may send a bye Packet and then cleanup the network resources on its end before the client can process that Packet (even though the ACK for that Packet should have been sent to the server before resources are cleaned up).  We fixed it by sleeping the calling thread for 100ms.  A possible solution might be that the client needs to respond by sending its own bye Packet.
  2. 23 |
  3. It's possible that the _nextGame might lock the main server thread when attempting to add players.  Say if there was only one client in the lobby, but the game kept on rejecting adding them, we'd be in the loop at lines 84 through 95 forever.  Any running games would still run, but nothing new would be able to start or connect.
  4. 24 |
  5. Just collecting Task objects returned by_handleNewConnection() (in the server) or _handleIncomingPackets() (in the client) is bad.  It's very possible that those Lists could grow to immense sizes.  It would be better to create a structure that could house Tasks until they've completed and then throw them into the garbage collector.
  6. 25 |
  7. The Games shouldn't really be handling client disconnects and Packets as much as they do.  In a much better designed application we would have a more complex "Player," object, handle all types of disconnects, and let the server dispatch Packets to the Games.  The games also notify the server when they are done, placing any current player back into the waiting lobby.
  8. 26 |
  9. There is a lot more we can parallelise and multithread in the server code.  Just saying.
  10. 27 |
  11. /u/EntroperZero over on Reddit pointed out that our calls to ReadAsync on the NetworkStreams return a Task that tells us how many bytes were actually read (instead of how many were requested in the third parameter).  Most of the time we will get the requested amount of bytes, but there is a chance that we could get less.  We should be checking the requested count versus how many were actually read.  You can read his original comment here.
  12. 28 |
29 | 30 |

 

31 | 32 |

I think this has been a good example of a multithreaded/async TCP server.  I'm a little disappointed in myself that I got lazy and didn't implement Tic-Tac-Toe too, but I'll leave that as an exercise for you to do.  I'd love to see it if you get it done (send me an email)!  If you have any other suggestions for the review, drop me a line.  And if you address any the problems I have listed above don't' hesitate to send me your solution.

33 | -------------------------------------------------------------------------------- /html/06.a.udp-file-transfer.html: -------------------------------------------------------------------------------- 1 |

UDP File Transfer

2 | 3 |

UDP is very different from TCP.  We have to work with datagrams instead of streams & connections.  It can be very fast (and unreliable), but there are many things that we have to do ourselves.  Trying to think of a good (and interesting example), I decided upon building a simple file transfer application (over UDP) would be a nice place to start.

4 | 5 |

 

6 | 7 |
8 |

On /r/programming there was an interesting article about a new web protocol that Google is working on called QUIC.  The tl;dr is that it's meant to replace TCP usage in some protocols, thus giving some speed improvements.  Read the article here.

9 |
10 | 11 |
12 |

 

13 |
14 | 15 |

 

16 | 17 |

Before Starting

18 | 19 |

This program will be separated into two parts.  The Sender and the Receiver.  The Sender will act as the server (of sorts) that will transfer the files to the Receivers.

20 | 21 |

The code is going to cover synchronous, singled threaded usage of the UdpClient class.  Friendly reminder that this is not a tutorial about UDP, but just how to use the APIs in C#.  I'd recommend making a Google search or checking YouTube for some other resources that can explain UDP better on a theory level.  The code is a lot more complex than I originally anticipated; but just as always, don't get discouraged.

22 | 23 |

 

24 | 25 |

Testing File

26 | 27 |

Seeing as we're dealing with file transfers, I think using the video Don't Copy That Floppy is the most appropriate test case here.

28 | 29 |

 

30 | 31 |
32 |
33 |
90's PSAs really had some character to them.
34 |
35 |
36 | -------------------------------------------------------------------------------- /html/06.b.udp-file-transfer-protocol-design.html: -------------------------------------------------------------------------------- 1 |

UDP File Transfer - Protocol Design

2 | 3 |

This protocol is going to be a bit complex.  Since UDP doesn't have ACKs built in, we're going to have to add them manually.  Not every type of message with have an ACK, just some control ones.  The Packet class (code in next part), contains two fields, PacketType (control) and Payload (data).  For each type, the data in Payload will be structured differently.

4 | 5 |

 

6 | 7 |

Block

8 | 9 |

File data will be transferred in Blocks.  They have simply two fields, an unsigned 32 bit integer that is it's ID Number, and a byte array that is the contained Data.  Typically the Data in the Block is compressed.  The code for this class is in the next part.

10 | 11 |

 

12 | 13 |

Packet Types

14 | 15 |

These are used to tell our app what each datagram is supposed to mean, they are:

16 | 17 |
    18 |
  • ACK  - An acknowledgement. Mostly for control messages, sent by both sides.
  • 19 |
  • BYE  - An "end transfer," message. Can be sent by either the Sender or Receiver at any time.
  • 20 |
  • REQF - A request for a file.  Sent by the Receiver to the Sender.  Needs to be ACK'd by the Sender.
  • 21 |
  • INFO - Information regarding a transferable file.  Sent by the Sender to the Receiver after the REQF.  Needs to be ACK'd by the Receiver.
  • 22 |
  • REQB - A request for a Block of data.  Sent by the Receiver to the Sender after the INFO.
  • 23 |
  • SEND - A response to a Block request, containing Block data.  Sent by the Sender to the Receiver of a corresponding REQB.
  • 24 |
25 | 26 |

Here is a helpful diagram that explains how the exchanges work:

27 | 28 |

 

29 | 30 |

UDP File Transfer Diagram

31 | 32 |

 

33 | 34 |

Packet Data (Payloads)

35 | 36 |

Here's the data that some exchanges should contain.

37 | 38 |
    39 |
  • REQF - Payload should contain a UTF8 encoded string that is the desired file. 40 | 41 |
      42 |
    • An ACK must always be sent by the Sender.  If the file is present, it should contain the same Payload that was in the REQF, if not, then it should send an empty Payload.
    • 43 |
    44 |
  • 45 |
  • INFO - First 16 bytes of the Payload must be a MD5 checksum of the original file (uncompressed).  The next 4 bytes are a UInt32 of the file size (in bytes).  The 4 bytes after that is the maximum Block size (in bytes),  The last 4 bytes are the total number of Blocks that will be transferred. 46 |
      47 |
    • An ACK must be sent by the Receiver.  It should have an empty Payload.
    • 48 |
    49 |
  • 50 |
  • REQB - Payload is an UInt32 number that is the Block.Number that is being requested by the Receiver.
  • 51 |
  • SEND - Payload is a Block who's Number is that of one requested in a previous REQB.
  • 52 |
  • BYE  - No Payload is needed.
  • 53 |
54 | 55 |

 If you are confused, take a look at the diagram as it has some examples:

56 | 57 |

 

58 | 59 |
60 |
UDP File Transfer - Packet Examples 61 | 62 |
I'm sorry that the lines on the gridded paper didn't come out any clearer.  If you look really, really hard you should be able to see them.
63 |
64 |
65 | -------------------------------------------------------------------------------- /html/06.c.udp-file-transfer-common-files.html: -------------------------------------------------------------------------------- 1 |

UDP File Transfer - Common Files

2 | 3 |

This UDP example is split into two projects, but they share some common code.  Feel free to just paste these in (I do recommend typing them in, but it can get lengthy), though make sure you read the summaries at the bottom of each.

4 | 5 |

 

6 | 7 |

Block

8 | 9 |

 

10 | 11 | 12 |

 

13 | 14 |

This is a little data container that's used to chop up a larger file into smaller chunks.  We have two constructors.  One for creating a new one with a set Number, and the other for reconstructing it from a byte array.  The GetBytes() method is used to well, get the Block as a byte array.

15 | 16 |
17 |

 

18 |
19 | 20 |

 

21 | 22 |

Packet

23 | 24 |

 

25 | 26 | 27 |

 

28 | 29 |

Packet is what we're bouncing around the inter-networks.  It has two only data fields, PacketType and Payload.  All Packets should have one of the types listed at the top under the region "Message Types."  Like with Block, there is also a second constructor to create a Packet from a byte array, and a GetBytes() method to get a Packet as a byte array.

30 | 31 |

At the bottom there are "specific packets," which are there to make working with Payload data easier with some message types.  You'll notice there isn't one for BYE as it doesn't need any Payload.

32 | 33 |
34 |

 

35 |
36 | 37 |

 

38 | 39 |

NetworkMessage

40 | 41 |

 

42 | 43 | 44 |

 

45 | 46 |

NetworkMessage is a tiny data structure that used to pair a Packet with a Sender.   These are only used when reading data we've received.

47 | -------------------------------------------------------------------------------- /html/06.d.udp-file-transfer-sender.html: -------------------------------------------------------------------------------- 1 |

UDP File Transfer - Sender

2 | 3 |

Here is what will wait for file requests and respond to them.  This acts as a server of sorts.  Note that in Main() you can comment out the hard coded arguments and used command line args instead.

4 | 5 |

 

6 | 7 | 8 |

 

9 | 10 |

So there is quite some code here, let's break it down.

11 | 12 |

In the constructor for UdpFileSender, we tell it what directory we want to use for files that are able to be transferred and we create the UdpClient.  We create it to listen for incoming datagrams on a specific port, and only IPv4 connections.

13 | 14 |

 

15 | 16 |

Init() will scan the FilesDirectory (non-recursively) for any files and mark them as transferable.  It also sets Running to true so we can start listening for Receivers.  Shutdown() is used to simply shut down the "server."

17 | 18 |

Run() is the main meat of UdpFileSender.  At the top we have some variables that are used for the transfer state as well as a mini-helper function to reset the state in case we have to.  In the beginning of the loop we see if we have any new Packets that were sent to us.  If one was a BYE message, it means the client has prematurely disconnected and we should reset the transfer state to wait for a new client.  The switch statement is used to run through the transfer process.  Each state will wait for a certain type of Packet, do some processing (e.g. creating a response or preparing a file for transfer) then move to the next one in line.  I won't go into the gory details as it's pretty easy to read (IMO).  At the bottom of the while loop we take a short nap to save on CPU resources.  And at the end of the function we send a BYE message to a client if we have one currently "connected," (in case the Sender operator wanted to shut down in the middle of a transfer).

19 | 20 |

 

21 | 22 |
23 |

Talking about the Send method of UdpClient, we are using the overload that requires you list and endpoint.  If you application may connect with multiple clients, I recommend using this method over the other ones.  Reminder that UDP is connectionless, therefore we don't have an established connection with any client.  Please ignore that Connect method you might have seen for now...

24 |
25 | 26 |

 

27 | 28 |

And Close() will clean up the underlying UdpClient; 'nuff said.

29 | 30 |

_checkForNetworkMessages() will see if we have any new datagrams that have ben received.  Since all of our Packets are at least four bytes in length, we want to make sure that many bytes are ready.  We create a dummy IPEndPoint object.  Using UdpClient.Receive, it will grab exactly one datagram that has been received by the network.  Unlike TcpClient, we don't have to mess around with NetworkStreams.  After we have it, we shove its sender and the Packet into a NetworkMessage queue for later processing.

31 | 32 |

_prepareFile() will do what the name implies and makes a file ready for transfer.  It takes in a filepath, computes a checksum of the original bytes, compresses it, and chops it into Blocks.  If all went well, it will return true, and its out parameters will be populated.  If you missed my last tutorial on Compression in C# be sure to read it, find it over here.

33 | 34 |

That's really all there is to UdpFileSender.  If you glossed over the switch statement in Run() that handles the state of the file transfer process, I recommend you read it well.

35 | -------------------------------------------------------------------------------- /html/06.e.udp-file-transfer-receiver.html: -------------------------------------------------------------------------------- 1 |

UDP File Transfer - Receiver

2 | 3 |

This is what requests a file from a Sender and downloads it to a local machine.  Just like with the other program you can twiddle with some of the hard coded arguments and replace them with command line ones in the Main() function at the bottom.

4 | 5 |

 

6 | 7 | 8 |

 

9 | 10 |

Like with the Sender, in our constructor we create our UdpClient with a default remote host set (which should be that of the Sender).

11 | 12 |

 

13 | 14 |
15 |

I mentioned in the last part that there is a Connect method in UdpClient (doc link).  Please note that this is a dirty filthy lie as UDP is a connectionless protocol.  In reality, what it does is set a "default remote host," for you so every time that you call this overload of the Send method you don't need to specify where you want to go, as it will only go to that one host.  This also applies to some of the constructors for UdpClient (like the one we're using).

16 |
17 | 18 |

 

19 | 20 |

Shutdown() in this class will just request a cancel of an existing file transfer, you can see later in the GetFile() method how this works.

21 | 22 |

GetFile() is the most important function of the class and its very similarly structured to UdpFileSender.Run().  It's what does all of the communication with the Sender.  Let's break it down.  Up at the top of the function's body there are some variables for the file transfer state and a helper function to reset them if needed.  In the beginning of the while loop we check for any new NetworkMessages.  First checking if the latest received Packet is a BYE, it then passes that onto the switch statement.  Inside of the switch is what manages the state of the file transfer (REQF, wait for ACK, wait for INFO, ACK, etc...).  Inside of the ReceiverState.Transfering case it will empty the Block request queue, check for any SEND messages (i.e. Block data).  If the request queue is empty but we don't have all the Blocks, it will refill the request queue with the missing Block.Number values.  But if we have all of the Blocks, then we move onto the state where we say BYE to the Sender (so it can accept new transfers) and then we reconstruct the Blocks into a byte array, uncompress them, and save the file.  At the end of the function we make a check to see if the transfer was ended prematurely by user request or the Sender.

23 | 24 |

Close() will clean up the underlying network resources, which is just the UdpClient.

25 | 26 |

_checkForNetworkMessages() is identical to the one seen in Sender.  It sees if there are enough bytes for a Packet message then queues it up for processing.

27 | 28 |

_saveBlocksToFile() is where we take the Blocks, reassemble them, uncompress the data, verify it with a checksum and then save it to the disk.

29 | -------------------------------------------------------------------------------- /html/06.f.udp-file-transfer-recap.html: -------------------------------------------------------------------------------- 1 |

UDP File Transfer - Recap

2 | 3 |

Here's a screenshot of it in action using a tinier version of my test file of choice.  Using SFTP took around a 14 seconds to transfer the video, whereas it took about 9 using the application we built.  There are many reasons SFTP was slower in this case, but I'll leave that to you to figure out.

4 | 5 |

 

6 | 7 |

UDP File Transfer Screenshot

8 | 9 |
10 |

 

11 |
12 | 13 |

 

14 | 15 |

With every other project that we've made so far, there are some deficiencies and improvements we could make.  Let's analyze a few of them:

16 | 17 |
    18 |
  • This program can sure take up a lot of heap memory.  When I was testing it with a larger file I had some OutOfMemoryExceptions thrown at me.  What we should be doing instead is saving the Blocks to the disk (instead of storing them in memory) as we send and receive them.
  • 19 |
  • The Sender needs some better client management.  Say if a second (malicious) client were to connect and send a BYE message in the middle of a transfer, it would stop the transfer with the first.  The first Receiver wouldn't even know what's going on, it would just keep queuing up block requests.  The Sender really should ignore any datagrams from other clients when its in the middle of a transfer with one.
  • 20 |
  • Timeouts & Retries.  This one is so important I'm putting in bold, underlined, italicized and red text.  Our code is completely void of any timeouts and retries (except for Block requeueing).  When one of the apps is waiting for an ACK, it should wait for a set amount of time and then resend its Packet in case the original one was dropped.  In the real world (and future applications) we need to be adding in our own retries and timeouts for the control Packets. (REQF, INFO, ACK, etc...). 21 |
      22 |
    • In fact the Block requeueing method is pretty bad.  It should wait for a set amount of time first before making another request for a block.  When I first tested this over the internet, my Receiver created many REQBs before the Sender could even respond to the first one it got.
    • 23 |
    24 |
  • 25 |
26 | 27 |

 Keep in mind that this is supposed to be a sample application showcasing UDP (and not a real world one).  If you were to add in UDP based file transfer to any application you are building, you would really need to make these changes.

28 | 29 |

As always, if you spot any other issues, be sure to send me an email and I'll evaluate them.  Anything is appreciated.

30 | -------------------------------------------------------------------------------- /html/07.a.udp-pong.html: -------------------------------------------------------------------------------- 1 |

UDP Pong

2 | 3 |

We're going to something a little bit more fun for this example.  We're going to write a clone of Pong that we can play over the Internet.  There will be a central server that many players can connect to and play one on one matches.  Pong really is the "Hello, World!" of video game programming, so I think it would be a good first game to try to network.

4 | 5 |

 

6 | 7 |
8 |

Since we're doing video game programming I chose to use MonoGame as our media library.  It's cross platform, uses C#, and it's pretty easy to use.  I wanted to keep as much of this tutorials in this series in the command line, but I had to break that guideline here.  I used the MonoGame.Framework.DesktopGL package.  If you're on an OS other than Linux, you might want to try Windows or MacOS versions.

9 | 10 |

Note: Audio playback is umm... kind of broken with MonoGame 3.5 (the current stable release at the time of writing this).  As far as I know, this only applies to the DesktopGL package.  This is really an un-fun problem because I've got projects that use an older version of MonoGame that still plays sounds fine; If you're on a Linux system you might want to try getting a slightly older version if you really want to play sounds.

11 | 12 |

I've included a #define that allows you to toggle audio playback on the Client app at compile time.  It's disabled by default.

13 |
14 | 15 |

 

16 | 17 |
18 |
Buster's NuGet Packages are broken. 19 | 20 |
I was shocked too Buster.
21 |
22 |
23 | 24 |

 

25 | 26 |

Architecture Overview

27 | 28 |

This tutorial is going to be broken up into two applications, the PongServer and the PongClient (make two separate Projects in your Solution).  The Server is going to be authoritative in handling collisions (Ball with Paddles, Goals, and top/bottom bounds), notifying the client to play sound effects, and position updates.  All the client will need to do connect to the Server and shout it's Paddle's Y position.

29 | 30 |

In a game instance (referred to as an Arena in the Server code), there will be only two players maximum, though the server should be able to handle multiple games at the same time.  A game instance will not start unless two Clients have connected.  If a Client connects and then disconnects, the game instance is considered done, regardless if the match had started or not.

31 | 32 |

 

33 | 34 |

Game Assets

35 | 36 |

Put the below files inside of a folder called Content in the Client's Project.  Make sure that all of these files in the Project are copied to the build directory in compilation (right click on file > Quick Properties > Copy to Output Directory).  I've also got them all in .zip and .tar.gz format if you'd like.

37 | 38 | 47 | 48 |

 

49 | 50 |

Special Thanks

51 | 52 |

I'd like to thank Jason Francis (@halfspiral) for helping me test this.

53 | -------------------------------------------------------------------------------- /html/07.b.udp-pong-protocol-design.html: -------------------------------------------------------------------------------- 1 |

UDP Pong - Protocol Design

2 | 3 |

There's a lot more we have to do here than in the UDP File Transfer app.  By design UDP is connectionless, but the problem is that we need to have our clients to be able to establish a connection, maintain it, transfer data in between, and then quit when they want.  We'll be implementing these things ourselves.

4 | 5 |

 

6 | 7 |

Basic Packet Structure

8 | 9 |

The first four bytes of each packet will denote its PacketType.  Instead of encoding some ASCII bytes like we did with the File Transfer example we'll be using 32 bit unsigned integers (4 bytes).  The next eight bytes will be a Timestamp encoded in a signed 64 bit integer (8 bytes).  After that will be the Payload, it's just an array of bytes.  It can be empty or it might have something to it.  That all depends on the PacketType.  So at minimum all Packets will be 12 bytes long.

10 | 11 |

 

12 | 13 |

Types of Packets

14 | 15 |

Here is what each PacketType is, with a description:

16 | 17 |
    18 |
  1. RequestJoin - Sent by the Client to the Server requesting to join a game.
  2. 19 |
  3. AcceptJoin- Sent by the Server to the Client as a response to the above.  It contains which Paddle the Client will be.
  4. 20 |
  5. AcceptJoinAck - Sent by the Client the Server to acknowledge the above.
  6. 21 |
  7. Heartbeat - Sent by the Client to the Server to notify its still connected.
  8. 22 |
  9. HeartbeatAck- Sent by the Server to the Client to acknowledge the above.
  10. 23 |
  11. GameStart - Sent by the Server to the Client to notify the game is going to start.
  12. 24 |
  13. GameStartAck - Sent by the Client to the Server to acknowledge the above.
  14. 25 |
  15. PaddlePosition - Sent by the Client to the Server to tell its Paddle's current position.
  16. 26 |
  17. GameState - Sent by the Server to the Client to tell it the current state of the game (Paddle positions, scores, Ball position, etc.).
  18. 27 |
  19. PlaySoundEffect - Sent by the Server to the Client to play a sound effect.
  20. 28 |
  21. Bye - Sent by either the Server or the Client to notify the other it's done with the game.
  22. 29 |
30 | 31 |

 

32 | 33 |

Connection Establishment

34 | 35 |

This is our version of the TCP Three-Way Handshake.  The Client will send a RequestJoin to the Server.  The Server will respond with an AcceptJoin message; it is the only one that contains data (an unsigned 32 bit integer that denotes the Client's Paddle).  Then the Client will need to respond with the AcceptJoinAck.  Here is a diagram of the handshake:

36 | 37 |

UDP Pong Handshake

38 | 39 |

 

40 | 41 |

Maintaining the Connection

42 | 43 |

This is pretty simple, the client will send Heartbeat messages and the server will send back HeartbeatAck messages in response.  The Client and Server will also record the times they got them and use it to determine if a connection has dropped or not.  The heartbeat timeout value will be 20 seconds.  These are sent after a connection has been established, while waiting for a GameStart/GameStartAck, and while the game is being played.

44 | 45 |

 

46 | 47 |

Game Start

48 | 49 |

The Server will not start the game until two Clients are connected.  Once that criteria has been met, it will send out the GameStart messages.  The Clients will both need to respond with a GameStartAck before the Server will deliver any GameState messages.

50 | 51 |

 

52 | 53 |

In Game

54 | 55 |

A few times per second both the Clients and the Server will send each other info about their current state.  The Client only needs to send PaddlePosition Packets; it contains one floating point number that is the Paddle's Y position.  The Server will send a GameState Packet to both Clients.  It will contain the Paddle & Ball positions as well as the scores (see the diagram below for the data layout).  Periodically the Server will send a PlaySoundEffect message.  This will tell the Client it should play a sound effect.  The name of the sound effect is encoded as a UTF-8 string.

56 | 57 |

Check the code for Packet.cs for details, specifically the classes GameStatePacket, PaddlePositionPacket, and PlaySoundEffect.  They will show you how the data is laid out.

58 | 59 |

 

60 | 61 |

Ending the Connection

62 | 63 |

At any time a Client or Server could send a Bye.  This simply tells the other that the connection is over.  If a Client sends it to the Server while in the middle of a game, the Server then needs to notify the other client the game is over.  When the Server shuts down, it should send a Bye message to all connected Clients.

64 | -------------------------------------------------------------------------------- /html/07.c.udp-pong-game-mechanics.html: -------------------------------------------------------------------------------- 1 |

UDP Pong - Game Mechanics

2 | 3 |

As stated before, Pong really is the "Hello, World!" of game development.  I'm pretty sure that most of you have done it at some point, but I chose to handle it slightly differently, so I think it's fair to go over what needs to happen.

4 | 5 |

 

6 | 7 |

Scoring

8 | 9 |

If the Ball hits the left side of the screen, it's a score for the right side player.  And vice-versa if the Ball touches the right side.  The Ball should then reset to the center of the screen.  A "score," noise should also be played.

10 | 11 |

 

12 | 13 |

Key Presses

14 | 15 |

This only applies to the Client application.  If a user presses the Esc key at any time, it will close the application and notify the Server.  The Up and Down arrow keys will be used by the user to control the position of the Paddle.

16 | 17 |

 

18 | 19 |

Hitting Top & Bottom Bounds

20 | 21 |

If the Ball hits the top or the bottom of the screen it should flip the Y component of its velocity.  It's speed will also slightly increase by a random amount and a "ballHit," sound should be played.

22 | 23 |

 

24 | 25 |

Hitting the Paddles

26 | 27 |

Each Paddle has three collision areas.  One on the top, one on the bottom, and one in the middle (called it's "Front,").  If the Ball hits any of these its speed will increase slightly and its X direction will be flipped (a sound will also play too).  But if the top or the bottom collision areas are hit, it will also flip the Y component of the Ball's velocity.  Below is a diagram of the hitboxes:

28 | 29 |

 

30 | 31 |

Ball & Paddle Hitboxes

32 | -------------------------------------------------------------------------------- /html/07.d.udp-pong-common-files.html: -------------------------------------------------------------------------------- 1 |

UDP Pong - Common Files

2 | 3 |

Add all of these files to both the Client Project and the Server Project.  If you want to copy & paste all of Packet.cs, I don't blame you (it's very long, boring and repetitive), but be sure to pay attention to the Packet class and it's subclasses that have Payload data.

4 | 5 |

 

6 | 7 |
8 |

The proper thing to do would be to create a separate Project in the Solution that contains the common code (e.g. PongFramework), but what I do instead is put all of the common files in one of the Projects (e.g. PongServer), and then add those as linked files in the other Project (e.g. PongClient).  That way everything is nice and synced.

9 |
10 | 11 |

 

12 | 13 |

GameGeometry

14 | 15 |

This is only a static data containment class.  Instead of littering our program with hard coded values, it's better to put there here.

16 | 17 |

 

18 | 19 | 20 |

 

21 | 22 |

NetworkMessage

23 | 24 |

This is almost identical to the one in the File Transfer example, but this also adds field where we mark the time when one side has received the Packet

25 | 26 |

 

27 | 28 | 29 | 30 |

 

31 | 32 |

ThreadSafe

33 | 34 |

This makes one variable (of a generic type) thread safe (via locking).  It can only be accessed via the Value property.

35 | 36 |

 

37 | 38 | 39 | 40 |

 

41 | 42 |

Packet

43 | 44 |

All right, this is the big one.  It's quite similar to the one of the File Transfer example, except that is has an extra field denoting the time the Packet was created.  There are many subclasses that have specific implementations of the PacketType enumeration.  Like last time, you use the GetBytes() method to get the Packet data as byte array that can be sent over the network.  The only subclasses that have Payloads are AcceptJoinPacket, PaddlePositionPacket, GameStatePacket, and PlaySoundEffectPacket.  Be sure to pay attention to how those ones store data.  Feel free to Ctrl-C, Ctrl-V the rest of the code if you find it too much to type.

45 | 46 |

 

47 | 48 | 49 | 50 |

 

51 | 52 |

Ball

53 | 54 |

This is the game object for the Ball that will be bounced around.  LoadContent() and Draw() should only be called on the Client app.  Where asInitialize() and ServerSideUpdate() will be called on the Server.

55 | 56 |

 

57 | 58 | 59 | 60 |

 

61 | 62 |

Paddle

63 | 64 |

There are two enumerations at the top of this file.  The first determines what side of the board the Paddle is one (using None will cause an error).  The other is to mark what kind of collision has happened with the Paddle.  LoadContent(), Draw(), and ClientSideUpdate() are called only on the Client.  That last function is what checks to see if the user has tried to move the Paddle (it also does a bit of bounds checking).  Initialize() and Collides() only need to be called on the Server portion.

65 | 66 |

The function Collides() is what checks if the Ball has collided with the Paddle.  If it has, the function will return true and the out parametertypeOfCollision will be filled with a value from the PaddleCollision enumeration.

67 | 68 |

 

69 | 70 | 71 | -------------------------------------------------------------------------------- /html/07.f.udp-pong-arena.html: -------------------------------------------------------------------------------- 1 |

UDP Pong - Arena

2 | 3 |

This is where the Pong game logic lives on the Server.

4 | 5 |

 

6 | 7 | 8 |

 

9 | 10 |

At the top of the class we have our member variables.  State needs to be encased in a ThreadSafe since it will be accessed by multiple threads.  The Paddle objects are contained for us in the PlayerInfo classes.  We store a reference to the running Server instance.  We also have our own message queue for Packets that have been dispatched to us.

11 | 12 |

TryAddPlayer() is called when we get a new Client connecting to the Server.  It will only add two players to this Arena.  The function will return true if the Client was added successfully, in any other case it will return false.

13 | 14 |

Start() will shift the State of the Arena to where it can accept new players and will begin running the underlying _arenaThread which runs the _arenaRun() method.

15 | 16 |

_arenaRun() is the heart of this class.  It is another looping function.  In the beginning of the loop it will check for new NetworkMessages and will make decisions based upon it's current State.  While we are waiting for two players, it will try to handle a connection setup.  Once two players have connected it will send them the GameStart Packets.  Once the GameStart Packets have been Ack'd by the Clients it will then shift the State to InGame, start the _gameTimer, and begin processing/handling PaddlePosition & GameState Packets.  In case a Bye message has been sent by one of the clients it will then stop the loop and notify the other Client the match is over.  At the end of the loop it will check for player time outs or see if a stop has been requested by the Server.  Once the loop has been exited it will queue up a ByePacket to any connected clients and notify the Server the game is done.

17 | 18 |

JoinThread() will give the underlying _arenaThread 1/10 of a second to complete execution.

19 | 20 |

EnqueMessage() is called by the PongServer to tell the Arena it's gotten something from one of it's Clients.

21 | 22 |

_sendTo() sets a Packet to be sent to a Client.  It will record the time when we wanted to send it.

23 | 24 |

_timedOut() is used to check the last time we've received a Packet from them versus how long a timeout should be (20 seconds in our case)

25 | 26 |

_handleConnection() is what instantiates the connection between the Client and the Server (though it's stored in the Arena code).  It will assign a Paddle to a Client and respond to Heartbeat messages.

27 | 28 |

_sendAcceptJoin() is a small helper method to tell a Client which Paddle they are.

29 | 30 |

_notifyGameStart() will tell a Client a game has started, and will retry every time the timeout has been reached.

31 | 32 |

_sendGameState() will tell a Client about the current state of the match.  This is the Paddle positions, scores, and Ball position.

33 | 34 |

_playSoundEffect() will queue up a PlaySoundEffectPacket to a Client.

35 | 36 |

_handlePaddleUpdate() will look at a PaddlePosition message from a Client and update it's Paddle's Y position.

37 | 38 |

The next two methods are for collisions.  _checkForBallCollision() will do all the math to see what the Ball is hitting something, and take an appropriate action (bounce, score, sound, etc.).  _processBallHitWithPaddle() is used to alter the Ball's velocity for Ball-Paddle collisions.

39 | -------------------------------------------------------------------------------- /html/07.g.udp-pong-client.html: -------------------------------------------------------------------------------- 1 |

UDP Pong - Client

2 | 3 |

This is the "fun," part of the application ecosystem that we will send to our users.  Where they can control their very own Paddle that goes up and down.  This is also the graphical portion of the app.  Before, we were just using the MonoGame/XNA libs for collision detection but here we're using them for their original intention (video games)!

4 | 5 |

 

6 | 7 |
8 |

Don't forget, if you want sounds working, you'll need to uncomment line number 7.  Remember that audio isn't quite working properly with MonoGame 3.5 (DesktopGL version).

9 |
10 | 11 |

 

12 | 13 |

 

14 | 15 | 16 |

 

17 | 18 |

You've probably noticed a lot of similarities with the Arena and PongServer code already, but there are some differences.

19 | 20 |

At the top of the class we have our time measurement members.  They are exactly similar in meaning to those of PlayerInfo, except that the Server/Client relationship is flip-flopped.

21 | 22 |

In the constructor we setup our game's asset directory, the display settings, initialise the game objects and instantiate the UdpClient.  For those of you unexperienced with MonoGame/XNA, LoadContent() will load our assets into memory.  UnloadContent() will be used to clean up some other things.

23 | 24 |

Update() is another one of those MonoGame/XNA methods.  At the top it will check for an Esc key press.  If so, it will quit the Client and notify the Server.  After that a timeout check is made.  Next we check for any new NetworkMessages and do some stuff based on that.  First we check for a Bye.  After that we will try to send a JoinRequest if we don't have a connection.  If we're waiting for a game start, we will ping-pong Heartbeats with the Server.  Once we're in the game we will continue to send Heartbeats.  We will periodically send our Paddle's Y position.  If we got a GameState we'll update the rest of the game objects.  If a PlaySoundEffect was sent, we will then try to play that named effect.

25 | 26 |

Start() will mark the Client as running and start the underlying _networkThread (with the _networkRun() method).

27 | 28 |

Draw() is once again a MonoGame/XNA method.  Depending upon the current state it will either display a message to the user or draw the game objects.

29 | 30 |

_drawCentered() is a utility method to draw a Texture2D object to the center of the screen.  _updateWindowTitleWithScore() is another "graphical method" that updates the window's title with the current score (hence the name).  If you notice, it will also denote which side the Client is on.

31 | 32 |

_networkRun() is a looping function that runs in the _networkThread.  It reads new messages from the Server and tries to send ones we've queued up.  After the loop is done, if we've set _sendBye to true (via the user pressing Esc) it will send a ByePacket.

33 | 34 |

_sendPacket() will queue up a Packet to be sent and mark the _lastPacketSentTime variable.

35 | 36 |

I'm not going to spend my time on the last few private methods.  They are all related to Packet transfer.  You can probably guess what they do based upon their names.

37 | 38 |

And at the end of the class we have our Main() method which will start the Client and try to connect.

39 | -------------------------------------------------------------------------------- /html/07.h.udp-pong-recap.html: -------------------------------------------------------------------------------- 1 |

UDP Pong - Recap

2 | 3 |

 

4 | 5 |

UDP Pong Client screenshot

6 | 7 |

 

8 | 9 |

This was the largest tutorial to date.  It took me three weeks to write and cleanup (where as it normally takes me one).  As always, there are some good things, and some bad.  Let's take a look at what we could fix and possible problem areas:

10 | 11 |
    12 |
  1. This game Server really isn't the most optimal thing out there.  In practicality, we should be pre-allocating a lot of the dynamic objects and reusing things. For example, maybe discarding the Arenas instead of resetting them is a bad idea.  But I want to keep things as simple as possible for this tutorial series.  I'll leave the ricing to you readers.
  2. 13 |
  3. Should the Arena code manage the connection with the clients?  I don't think it should in retrospect.  That should more be a responsibility of the PongServer.  This includes setting up the connection, handling Heartbeats, and disconnects.  All the Arena should do is manage the game data and make communication requests.
  4. 14 |
  5. I'm wondering if the HeartbeatAck was a good idea since it requires the Server to respond to a Heartbeat.  If the Client sends one, but it drops during transmission, there will be no Ack created.  And even if the Heartbeat reaches the server, what's there to guarantee that the Ack will reach back to the Client?  Nothing really.
    15 |
    16 | When I was testing this earlier with a friend somewhere else in the USA, I had the Heartbeats send once every second, and it would time out if for 15 seconds no Heartbeat (or Ack) was recorded.  It would usually time out after a few minutes (we were both on crappy connections).  This means that 15 Heartbeats and their Acks were dropped in a row (not unlikely), which is pretty bad.  So in this final version it now sends 5 Heartbeats per second and has a timeout of 20 seconds.  This means that it would need to miss 400 consecutive Heartbeats to time out, which is a lot less likely.
    17 |
    18 | Maybe a better solution would be to have both the Client & Server send Heartbeats & Acks, or just Heartbeats only.
  6. 19 |
  7. Bye should probably have its own Ack.  In the Client code we waited one second to make sure the message was sent.  This is a little bad.  Maybe instead waiting a second or hearing an Ack first would be a better idea.  This also better models a TCP connection termination.
  8. 20 |
  9. Possibly counting an integer instead of a Timestamp might be better for managing Packet order.  Time really is meta-data instead of an actual Packet ordering.
  10. 21 |
  11. 22 |
    23 |

    There's a bit of a problem with trusting data sent by the Client to the Server.  Take a look in the code for PongClient that has to deal with the Paddle.

    24 | 25 |

    It's very possible (and easy) for a player to lie about their Paddle movement.  As of right now, the Server doesn't have any code to verify the Paddle's movements have been under 100 pixels/sec.  It might be a fun exercise for you to make a Client that will match the Ball's Y position with that of your Paddle's.

    26 | 27 |

    28 |
    29 |
  12. 30 |
31 | -------------------------------------------------------------------------------- /html/09.a.socket-class-intro.html: -------------------------------------------------------------------------------- 1 |

Socket Class Intro

2 | 3 |

In  TcpClientTcpListener, and UdpClient (as well as many other classes) there is an underlying Socket object.  If you're looking for something simple to work with or want to get an application developed quickly, I'd recommend using those objects first.  Though if you need more performance or want something that is much closer to the Berkeley/POSIX Socket API, then the Socket class is what you'll want to use.

4 | 5 |

For this part of the series, I will present you with some C code (that uses the Berkeley Socket API) and then show you a C# port of that code.  The C code is adapted from this Wikibooks article.  I will not be showing you any usage of the asynchronous method right now.  I want to start things off simple. We will cover async usage of the Socket class in the next section.

6 | 7 |

 

8 | 9 |

The Application

10 | 11 |

The app is pretty simple.  The server will open up a socket and wait for incoming connections.  When a new client has connected, it will print some info to the terminal window and respond to the client with a short message.  Then once it has been sent it will close the connection with the client.  All of this will run over TCP.

12 | 13 |

I'm not going to do an explanation of what's' going on like I usually do.  I think the code is very straightforward; there are comments sprinkled all over that should explain what each line does.

14 | 15 |
16 |

s

17 |
18 | 19 |

 

20 | 21 |

Compiling & running the C code for this section is completely optional.   But make sure to read through it and try to understand a little bit of what's going on.  If you want to compile and run the C code, you'll need to grab yourself a C compiler.  I used clang (on Linux) when writing the C code, but it will also compile with something like gcc (on Windows via Cygwin).  If you don't know or understand C that well (or the POSIX interface), don't worry about it.

22 | -------------------------------------------------------------------------------- /html/09.b.socket-class-intro-original-c-code.html: -------------------------------------------------------------------------------- 1 |

Socket Class Intro - Original C Code

2 | 3 |

There are only two files that really matter in part client.c and server.c, but a Makefile is provided for your convince and the end of this page.

4 | 5 |

 

6 | 7 |

Server

8 | 9 | 10 |

 

11 | 12 |

Client

13 | 14 | 15 |

 

16 | 17 |

Makefile

18 | 19 |

If you are using a different C compiler (other than clang), change the value of CC to be whatever it is (e.g. gcc).

20 | 21 |

 

22 | -------------------------------------------------------------------------------- /html/09.c.socket-class-intro-csharp-port.html: -------------------------------------------------------------------------------- 1 |

Socket Class Intro - C# Port

2 | 3 |

In my opinion, the C# API for Berkeley Sockets is much cleaner, thus nicer look and easier to work with.  I'd recommend putting the Server and Client code into their own projects, but go ahead and do what you want.

4 | 5 |

 

6 | 7 |

Server

8 | 9 | 10 |

 

11 | 12 |

Client

13 | 14 | -------------------------------------------------------------------------------- /html/09.d.socket-class-intro-recap.html: -------------------------------------------------------------------------------- 1 |

Socket Class Intro - Recap

2 | 3 |

 

4 | 5 |

C Server on the left, C# client on the right

6 | 7 |

 

8 | 9 |

There wasn't too much that was covered here, but we got our feet wet with using the raw Socket class.  In the next section we will cover usage of the async methods in Socket.  Get ready for callbacks everywhere!

10 | 11 |
12 |

 

13 |
14 | 15 |

 

16 | 17 |

Why is using the Socket class directly more efficient than something like TcpClient or UdpClient?

18 | 19 |

As I said before, classes like TcpClient and UdpClient have and underlying Socket object (which can actually be accessed via the Client property in both).  "Why would we even want to use Socket if it's more complicated?"  you might ask.

20 | 21 |

If you chose to use TcpClient or UdpClient there is a slight amount of overhead versus working directly with a Socket.  If you truly need a high octane riced app, then  go with Socket.  The performance/efficiency benefits mostly come from using the async methods of Socket, which are all callback based.  Yes, yes.  TcpClient and UdpClient do have async methods too, but the Socket class has a lot more of them which gives you way more flexibility than the other two do.

22 | -------------------------------------------------------------------------------- /html/conclusion.html: -------------------------------------------------------------------------------- 1 |

Conclusion

2 | 3 |

And now that we have covered the basics of Socket, I would like to end this tutorial series here.  I feel that I have given you enough example projects and explanations of how to use the networking APIs (and then some) in the System.Net namespace.  From here, I'm fairly confident that anyone who has gone through this course should be able to write their own networked C# appliations and delve into some more advanced topics on their own.

4 | 5 |

 

6 | 7 |

Where To Go From Here

8 | 9 |

If you want to learn more, the best thing to do would be to pick and a project you want to make, and then build it!  I'm really partial to video games; I already showed you how to network Pong earlier so you have someplace to start.

10 | 11 |

There were two topics that I didn't get to cover that I really wanted to.  (Sorry, life got a little busy for me in the past few months).

12 | 13 |
    14 |
  1. Asynchronous Sockets.  This isn't simply calling functions that are marked with the async keyword, it's a hole different beast & paradigm.  Here are some good places to start on the MSDN documentation:
    15 |
    16 | Using an Asynchronous Server Socket
    17 | Asynchronous Server Socket Example
    18 | Using an Asynchronous Client Socket
    19 | Asynchronous Client Socket Example
    20 |  
  2. 21 |
  3. SSL/TLS & Security.  This is vital in modern computing and communications.  There are way too many things to count that I could go over.  Security in the .NET Framework is a good stepping stone, but poking through SslStream should be something to do.
  4. 22 |
23 | 24 |

There are an ocean of third party (higher level) communication libraries available for C#.  Some of the more popular ones include RakNet, Lidgren, and ZeroMQ.  Each has its own purpose and complexity.  You might find them a bit more useful and easier than the System.Net namespace.

25 | 26 |

If you'd like to study distributed data comm. here is pretty cool article on how to implement the BitTorrent protocol in C#.

27 | 28 |

 

29 | 30 |

All of the Source

31 | 32 |

If you were following these tutorials as I posted them, I only gave you the code for the projects in gists.  Available now is a repo that contains everything (and the project files).  You can find it here (GitHub mirror).

33 | 34 |

 

35 | 36 |

Least Significant Bits

37 | 38 |

My gratitude to Jason Francis (@halfspiral) for helping me test the Pong clone.

39 | 40 |

I'd like to make note of the folks over at /r/csharp.  They provided some ideas for tutorials, extra insight on things, a code correction or two, and were the majority of my views.  I don't think I'd put in as much effort as I did if it wasn't for the community there.  Thank you.

41 | 42 | -------------------------------------------------------------------------------- /html/dns.html: -------------------------------------------------------------------------------- 1 |

DNS

2 | 3 |

The Dns class is a static object contained in System.Net that stands as a tool for domain name services.  This is very handy if you want to let your users type in a domain like 16bpp.net instead of 107.170.178.215.  For those of you who don't know what DNS is or what it does, it's magic that turns letters into numbers.

4 | 5 |

The Dns class contains a few methods for resolving host names and other things.  You can do it both synchronously and asynchronously.  The important methods of this class will either return IPAddress objects the more detailed IPHostEntry.  The latter is nice if you want to get an aliases associated with the queried hostname.  Keep in mind that one domain name might have multiple IP addresses associated with it.

6 | 7 |

 

8 | 9 | 10 |

 

11 | 12 |

As I said, there is an asynchronous version of Dns.GethostEntry().  If you just care about the addresses, there are methods for that too.

13 | -------------------------------------------------------------------------------- /html/ipaddress.html: -------------------------------------------------------------------------------- 1 |

IPAddress

2 | 3 |

IPAddress is a very small class in the System.Net namespace.  It's job is to just represent, well, an IP address.  It supports both IPv4 and IPv6 type addresses.

4 | 5 |

You can make new IP address either by supplying a byte array to the constructor, or using the IPAddress.Parse() (or IPAddress.TryParse()) method to get one from a string. Exceptions will be thrown if the IP address is malformed (either via strings or byte arrays).  When using the constructor, if you supply a byte array of exactly four bytes, it will assume you want an IPv4 address.

6 | 7 |

 

8 | 9 | 10 |

 

11 | 12 |

Please also note the static fields of IPAddress are very important when creating servers, all of them are read only.

13 | 14 |
    15 |
  • Any - This tells a Socket to listen for connections on any network devices.  Which may be coming in locally or via a network
  • 16 |
  • Loopback - This tells a Socket to listen for connections that are only coming in from the local machine (127.0.0.1)
  • 17 |
  • Broadcast - This provides the broadcast IP address to a socket
  • 18 |
  • None - This tells a Socket not to listen in for any connections
  • 19 |
20 | 21 |

All of the fields above are for IPv4, but IPAddress has IPv6 versions of most of these static fields.

22 | -------------------------------------------------------------------------------- /html/ipendpoint.html: -------------------------------------------------------------------------------- 1 |

IPEndPoint

2 | 3 |

IPEndPoint is a data structure that simply contains an IPAddress, and a port number.  It essentially is supposed to represent a resource that you an connect to over the internet.

4 | 5 |

 

6 | -------------------------------------------------------------------------------- /html/preface.html: -------------------------------------------------------------------------------- 1 |

Preface

2 | 3 |

While there are resources out there to learn about C#'s networking capabilities, I had some issues with finding the information myself in some nicely organized and well written tutorials.  I've don network programming in other languages, but had no idea how to use C#'s APIs.  I know that there are other people out there like me, so I thought about writing a set of tutorials about the System.Net namespace.

4 | 5 |

This is not a tutorial on networking (explicitly), but how to use C#'s networking API.

6 | 7 |

I work primarily with Linux, therefore Mono will be my runtime of choice (don't worry, it should still work with Microsoft's .NET VM).  I'll make sure the code provided can be run in just a command line interface, so we won't need to worry about GUI programming at all.  I'll try to keep the example simple and short, but I may include some larger and more complex applications.  I will also include full source code for each example/tutorial.

8 | 9 |

Through this tutorial's sections, you might see messages like the one below, it's a good idea to pay attention to them (like the one below for Linux users).

10 | 11 |

 

12 | 13 |
14 |

If you run Ubuntu (or a variant of it like me), make sure you have Mono installed from the project's official repository.  It's more up to date than the one in the Canonical repos.  Be sure to install the ca-certificates-mono package.  This makes it much easier for you to use SSL with Mono.

15 |
16 | 17 |

 

18 | 19 |

I'll try to keep the amount of extra dependencies & packages to a minimum. For the basic stuff, we shouldn't need anything more than what's provided to us in System.Net.

20 | 21 |

 

22 | 23 |

Source Code

24 | 25 |

Each of the tutorial sections have thier own full source examples.  If you want, you can download the entire collection (and project files) over here (GitHub mirror).  All of the code examples that I have included fall under the Unlicense (a.k.a. Public Domain), unless otherwise noted.  If you end up using my code somewhere, credit for authorship is appreciated.

26 | 27 |

 

28 | 29 |

How To Use This Guide

30 | 31 |

The main tutorial sequence is numbered off on the left.  You don't have to follow it in order, or read all of them, but it's recommended.  Everything else that isn't numbered are just one-off, "micro-tutorials."

32 | 33 |

I recommend reading all of the text.  Do not skimming anything.  When it comes to code, try not to just copy n' paste the example.  While it may take more time and effort, copying the code by writing it out in your editor is probably the best way to learn and memorize it.  It's what I do with most of the tutorials I read online.  I suggest you make one solution to contain this tutorial, but one project in the solution for each topic/section.

34 | 35 |

Since we're also dealing with communications, I highly advise that you have a computer somewhere else in the world where you can run and test these programs.  While running a server & client on your local machine is fine for development, it's not realistic of what happens in the real world.  I'd recommend spinning up a VM on a service like Google Cloud Platform, Digital Ocean, or Amazon Web Services.

36 | 37 |

 

38 | 39 |

Contacting Me

40 | 41 |

If you have any questions, comments, or requests send me an email.  If you send me a message like "Can you look at my code.  I don't know what's wrong," or any other technical questions, I'm not likely to respond.  If you do copy n' paste code from here, and it turns out to be wrong, please do notify me so I can fix it.  All of the examples will be hosted on Github's Gist service.

42 | 43 | --------------------------------------------------------------------------------