diff --git a/FTPClient/.vs/FTPClient/v16/.suo b/FTPClient/.vs/FTPClient/v16/.suo
index 333043fc55c3c93d0306c8bb762461de8e1df6ec..3e81d42b12a0398435db4ee212d2f06efdce020e 100644
Binary files a/FTPClient/.vs/FTPClient/v16/.suo and b/FTPClient/.vs/FTPClient/v16/.suo differ
diff --git a/FTPClient/FTPClient/Client.cs b/FTPClient/FTPClient/Client.cs
index 9fbf69059cc94f2ced57c1d49db9f32389886e5c..27d6c83a737ea7fcf472132f7820d8fa2d0b4dae 100644
--- a/FTPClient/FTPClient/Client.cs
+++ b/FTPClient/FTPClient/Client.cs
@@ -23,6 +23,7 @@ namespace FTPClient
             TftpClient client = new TftpClient("test", 69);
             client.InitializeAndRead("README");
             client.SendWRQ("wrqFile");
+            client.SendRRQ("SmallText");
         }
 
         private static void FtpConversation()
diff --git a/FTPClient/FTPClient/TftpClient.cs b/FTPClient/FTPClient/TftpClient.cs
index 3586ff1df4a3846e9335922270b128b0e8b8ba86..09ebeec705ce9d450a610c5e4e6279efd2e48971 100644
--- a/FTPClient/FTPClient/TftpClient.cs
+++ b/FTPClient/FTPClient/TftpClient.cs
@@ -81,7 +81,8 @@ namespace FTPClient
 
             socket.SendTo(packet, endpoint);
             Console.WriteLine($"Sent Write Request for file: {writeFile}");
-            HandleWriteRequest();
+            if (!WaitAck()) return;
+            HandleWriteRequest(writeFile);
         }
 
         /// <summary>
@@ -97,6 +98,16 @@ namespace FTPClient
             HandleReadRequest();
         }
 
+        private bool WaitAck()
+        {
+            byte[] buffer = new byte[512];
+            socket.ReceiveFrom(buffer, ref endpoint);
+            int opcode = buffer[0] | buffer[1] << 8;
+            if (opcode == (int)PACKET_TYPE.ACK) return true;
+            if (opcode == (int)PACKET_TYPE.ERROR) ReportError(buffer);
+            return false;
+        }
+
         private byte[] MakeRequestPacket(PACKET_TYPE type)
         {
             byte[] packet = new byte[4 + filename.Length + MODE.Length];
@@ -131,9 +142,47 @@ namespace FTPClient
             endpoint = (EndPoint)ipEndp;
         }
 
-        private void HandleWriteRequest()
+        private void HandleWriteRequest(string filename)
+        {
+            string path = Path.Combine(FILE_PATH, filename + ".txt");
+            using (FileStream fsSource = new FileStream(path,
+            FileMode.Open, FileAccess.Read))
+            {
+                // Read the source file into a byte array.
+                int numBytesToRead = (int)fsSource.Length;
+                int numBytesRead = 0;
+                int packetNumber = 1;
+
+                while (numBytesToRead > 0)
+                {
+                    byte[] buffer = Create512Buffer((int)PACKET_TYPE.DATA);
+                    buffer[2] = (byte)packetNumber;
+                    buffer[3] = (byte)(packetNumber >> 8);
+
+                    // Read may return anything from 0 to numBytesToRead.
+                    int n = fsSource.Read(buffer, 4, 508);
+                    socket.SendTo(buffer, endpoint);
+                    // Break when the end of the file is reached.
+                    if (n == 0)
+                        break;
+
+                    numBytesRead += n;
+                    numBytesToRead -= n;
+                    packetNumber += 1;
+                }
+                Console.WriteLine($"Sent {numBytesRead} bytes");
+            }
+        }
+
+        /// <summary>
+        /// Creates a data packet 512 bytes 
+        /// </summary>
+        private static byte[] Create512Buffer(int opcode)
         {
-            // TODO: Create handler
+            byte[] buffer = new byte[512];
+            buffer[0] = (byte)PACKET_TYPE.DATA;
+            buffer[1] = (byte)((int)PACKET_TYPE.DATA >> 8);
+            return buffer;
         }
 
         private void HandleReadRequest()
@@ -167,29 +216,24 @@ namespace FTPClient
             // CheckPacketNumber();
 
             string path = Path.Combine(FILE_PATH, filename + ".txt");
-            if (!File.Exists(path))
+            
+            // Create a file to write to.
+            using (StreamWriter sw = File.CreateText(path))
             {
-                // Create a file to write to.
-                using (StreamWriter sw = File.CreateText(path))
+                bool conversation = ReadData(packet, sw);
+                while (conversation)
                 {
-                    bool conversation = ReadData(packet, sw);
-                    while (conversation)
-                    {
-                        byte[] buffer = new byte[512];
-                        socket.ReceiveFrom(buffer, ref endpoint);
-                        conversation = ReadData(buffer, sw);
-                    }
-                    Console.WriteLine("File received");
+                    byte[] buffer = new byte[512];
+                    socket.ReceiveFrom(buffer, ref endpoint);
+                    conversation = ReadData(buffer, sw);
                 }
+                Console.WriteLine("File received");
             }
-            else Console.WriteLine($"File: \"{path}\" already exists.");
         }
 
         /// <summary>
         /// Read data from packet and write it to file
         /// </summary>
-        /// <param name="packet">packet to read from</param>
-        /// <param name="sw">streamwriter to write with</param>
         /// <returns>true if wasn't last packet (not below 508 bytes)</returns>
         private bool ReadData(byte[] packet, StreamWriter sw)
         {
diff --git a/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.dll b/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.dll
index 5d45d24e1d1ba45efc207a2bc0abacb692db7b99..0ba8900f321ecc0a1921acf83544b67cb938c4f5 100644
Binary files a/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.dll and b/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.dll differ
diff --git a/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.pdb b/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.pdb
index 814b168d23af78a6b087a54e6ebe09c8e960ff13..77ac1f7cc1e40ef7b8452628ac939c8dd1f63012 100644
Binary files a/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.pdb and b/FTPClient/FTPClient/bin/Debug/netcoreapp3.1/FTPClient.pdb differ
diff --git a/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.dll b/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.dll
index 5d45d24e1d1ba45efc207a2bc0abacb692db7b99..0ba8900f321ecc0a1921acf83544b67cb938c4f5 100644
Binary files a/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.dll and b/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.dll differ
diff --git a/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.pdb b/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.pdb
index 814b168d23af78a6b087a54e6ebe09c8e960ff13..77ac1f7cc1e40ef7b8452628ac939c8dd1f63012 100644
Binary files a/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.pdb and b/FTPClient/FTPClient/obj/Debug/netcoreapp3.1/FTPClient.pdb differ
diff --git a/TFTPServer/.vs/TFTPServer/v16/.suo b/TFTPServer/.vs/TFTPServer/v16/.suo
index e7b4799492c9fbd4e348ff82277548131e12a6fe..4eb4a6a282f1ecc8bb0bc7eb1a347b0bfb12c260 100644
Binary files a/TFTPServer/.vs/TFTPServer/v16/.suo and b/TFTPServer/.vs/TFTPServer/v16/.suo differ
diff --git a/TFTPServer/TFTPServer/Server.cs b/TFTPServer/TFTPServer/Server.cs
index 05a8896ca657cf680298be22e5598354c8da51dc..9f5bda5c175704baec8fa5a30fc98c0f31d83237 100644
--- a/TFTPServer/TFTPServer/Server.cs
+++ b/TFTPServer/TFTPServer/Server.cs
@@ -40,27 +40,23 @@ namespace TFTPServer
             No_such_user
         }
 
-        private static void ReadRequest(byte[] buffer, EndPoint remote)
+        /// <summary>
+        /// Sends a acknowledgement packet
+        /// </summary>
+        private static void SendAck(EndPoint remote)
         {
-            string filename = string.Empty;
-            int index = 2;
-            do
-            {
-                filename += Convert.ToChar(buffer[index]);
-                index += 1;
-            } while (buffer[index] != 0);
-            Console.WriteLine($"Read request for file : {filename}");
+            byte[] packet = new byte[4];
+            packet[0] = (byte)PACKET_TYPE.ACK;
+            packet[1] = (byte)((int)PACKET_TYPE.ACK >> 8);
+            packet[2] = 0;
+            packet[3] = 0;
 
-            string file_path = Path.Combine(FILE_DIR, filename+".txt");
-            if (!String.IsNullOrEmpty(filename) && File.Exists(file_path)) {
-                SendData(file_path, remote);
-            } else
-            {
-                string message = "File not found.";
-                SendError(remote, (int)ERROR_CODE.File_not_found, message);
-            }
+            socket.SendTo(packet, remote);
         }
 
+        /// <summary>
+        /// Sends the file data from path to client
+        /// </summary>
         private static void SendData(string path, EndPoint remote)
         {
             using (FileStream fsSource = new FileStream(path,
@@ -69,11 +65,13 @@ namespace TFTPServer
                 // Read the source file into a byte array.
                 int numBytesToRead = (int)fsSource.Length;
                 int numBytesRead = 0;
+                int packetNumber = 1;
+
                 while (numBytesToRead > 0)
                 {
                     byte[] buffer = Create512Buffer((int)PACKET_TYPE.DATA);
-                    buffer[2] = (byte)blockNumber;
-                    buffer[3] = (byte)(blockNumber >> 8);
+                    buffer[2] = (byte)packetNumber;
+                    buffer[3] = (byte)(packetNumber >> 8);
 
                     // Read may return anything from 0 to numBytesToRead.
                     int n = fsSource.Read(buffer, 4, 508);
@@ -84,12 +82,15 @@ namespace TFTPServer
 
                     numBytesRead += n;
                     numBytesToRead -= n;
-                    blockNumber += 1;
+                    packetNumber += 1;
                 }
                 Console.WriteLine($"Sent {numBytesRead} bytes");
             }
         }
 
+        /// <summary>
+        /// Creates a data packet 512 bytes 
+        /// </summary>
         private static byte[] Create512Buffer(int opcode)
         {
             byte[] buffer = new byte[512];
@@ -98,14 +99,17 @@ namespace TFTPServer
             return buffer;
         }
 
-        private static void SendError(EndPoint remote, int error_code, string message)
+        /// <summary>
+        /// Sends a error with given error code and message
+        /// </summary>
+        private static void SendError(EndPoint remote, ERROR_CODE error_code, string message)
         {
             byte[] messageBytes = Encoding.ASCII.GetBytes(message);
             byte[] packet = new byte[4 + messageBytes.Length + 1];
             packet[0] = (byte)PACKET_TYPE.ERROR;
             packet[1] = (byte)((int)PACKET_TYPE.ERROR >> 8);
             packet[2] = (byte)error_code;
-            packet[3] = (byte)(error_code >> 8);
+            packet[3] = (byte)((int)error_code >> 8);
 
             int index = 4;
             for(int i = 0; i < messageBytes.Length; i++)
@@ -115,9 +119,103 @@ namespace TFTPServer
             socket.SendTo(packet, remote);
         }
 
-        private static void WriteRequest(byte[] buffer)
+        /// <summary>
+        /// Get filename from packet
+        /// </summary>
+        private static string FilenameFromPacket(byte[] packet)
         {
-            throw new NotImplementedException();
+            string filename = String.Empty;
+            int index = 2;
+            do
+            {
+                filename += Convert.ToChar(packet[index]);
+                index += 1;
+            } while (packet[index] != 0);
+            return filename;
+        }
+
+        /// <summary>
+        /// Handles a read request from client
+        /// </summary>
+        private static void ReadRequest(byte[] buffer, EndPoint remote)
+        {
+            string filename = FilenameFromPacket(buffer);
+            
+            Console.WriteLine($"Read request for file : {filename}");
+
+            string file_path = Path.Combine(FILE_DIR, filename + ".txt");
+            if (!String.IsNullOrEmpty(filename) && File.Exists(file_path))
+            {
+                SendData(file_path, remote);
+            }
+            else
+            {
+                string message = "File not found.";
+                SendError(remote, ERROR_CODE.File_not_found, message);
+            }
+        }
+
+        /// <summary>
+        /// Handles a write request from client
+        /// </summary>
+        private static void WriteRequest(byte[] buffer, EndPoint remote)
+        {
+            string filename = FilenameFromPacket(buffer);
+            string path     = Path.Combine(FILE_DIR, filename + ".txt");
+            
+            if(File.Exists(path)) {
+                string message = "File already exists";
+                SendError(remote, ERROR_CODE.File_already_exists, message);
+                return;
+            }
+
+            SendAck(remote);
+            HandleWRQ(remote, filename);
+        }
+
+        /// <summary>
+        /// Listens clients data packets
+        /// </summary>
+        private static void HandleWRQ(EndPoint remote, string filename)
+        {
+            string path = Path.Combine(FILE_DIR, filename + ".txt");
+            byte[] packet = new byte[512];
+            socket.ReceiveFrom(packet, ref remote);
+            int opcode = packet[0] | packet[1] << 8;
+                
+            if (opcode == (int)PACKET_TYPE.DATA)
+            {
+                // Create a file to write to.
+                using (StreamWriter sw = File.CreateText(path))
+                {
+                    bool conversation = ReadData(packet, sw);
+                    while (conversation)
+                    {
+                        byte[] buffer = new byte[512];
+                        socket.ReceiveFrom(buffer, ref remote);
+                        conversation = ReadData(buffer, sw);
+                    }
+                    Console.WriteLine("File received");
+                }
+            }
+        }
+
+        /// <summary>
+        /// Read data from packet and write it to file
+        /// </summary>
+        /// <returns>true if wasn't last packet (not below 508 bytes)</returns>
+        private static bool ReadData(byte[] packet, StreamWriter sw)
+        {
+            string data = String.Empty;
+
+            for (int i = 4; i < packet.Length; i++)
+            {
+                if (packet[i] == 0) break;
+                data += Convert.ToChar(packet[i]);
+            }
+            sw.Write(data);
+            if (data.Length == 508) return true;
+            return false;
         }
 
         private static void ErrorResponse(byte[] buffer)
@@ -146,12 +244,12 @@ namespace TFTPServer
             }
             catch (Exception ex)
             {
-                Console.WriteLine("Virhe... " + ex.Message);
+                Console.WriteLine("Error... " + ex.Message);
                 Console.ReadKey();
                 return;
             }
 
-            Console.WriteLine("Odotetaan asiakasta...");
+            Console.WriteLine("TFTP server up, listening for clients...");
 
             while (!Console.KeyAvailable)
             {
@@ -166,7 +264,7 @@ namespace TFTPServer
                         ReadRequest(buffer, remote);
                         break;
                     case (int)PACKET_TYPE.WRQ:
-                        WriteRequest(buffer);
+                        WriteRequest(buffer, remote);
                         break;
                     case (int)PACKET_TYPE.DATA:
                         DataRequest(buffer);
diff --git a/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.dll b/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.dll
index b058fc3c300dd99d861d7bbf67d5f824814836ff..084a1b12e9795a2b92ae5eee9d121406d90978ad 100644
Binary files a/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.dll and b/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.dll differ
diff --git a/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.pdb b/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.pdb
index 72cbb1aa296d1dfa3b542efeeaf3f917ecc8b666..cfe8abc3c584a0d3069531d2dab1bef3c8c25d3d 100644
Binary files a/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.pdb and b/TFTPServer/TFTPServer/bin/Debug/netcoreapp3.1/TFTPServer.pdb differ
diff --git a/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.dll b/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.dll
index b058fc3c300dd99d861d7bbf67d5f824814836ff..084a1b12e9795a2b92ae5eee9d121406d90978ad 100644
Binary files a/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.dll and b/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.dll differ
diff --git a/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.pdb b/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.pdb
index 72cbb1aa296d1dfa3b542efeeaf3f917ecc8b666..cfe8abc3c584a0d3069531d2dab1bef3c8c25d3d 100644
Binary files a/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.pdb and b/TFTPServer/TFTPServer/obj/Debug/netcoreapp3.1/TFTPServer.pdb differ