Related
I ran into a strange problem. When I'm trying to send the file via a TCP socket, the first 4 bytes of sended information cuts off.
That is sending and receives pieces of code.
Client side
for (var i = 0; i < fileContentByte.Length; i += buffer.Length)
{
var size = (i + buffer.Length > fileContentByte.Length) ? fileContentByte.Length - i : buffer.Length;
clientSocket.Write(fileContentByte, i, size);
}
Server side
using(var file = File.Create("C:\\test\\"+fileName.Substring(0, fileName.IndexOf('\0'))))
while(bytesReceived < numberOfBytes && (count = clientStream.Read(buffer, 0, buffer.Length)) > 0)
{
file.Write(buffer, 0, count);
bytesReceived += count;
}
Here is link on full code - http://pastebin.com/VwTgTxgb
You're doing something very strange here.
First of all, retrieval of file name can be greatly simplified down to Path.GetFileName() call.
Second, are you sure ASCII will suffice?
Third, reading the entire file into memory is OK-ish for a proof-of-concept project, but be ready to switch to streaming operations.
Fourth, your protocol is somewhat wonky. When sending variable-size payload, it is required to first tell the receiving party exactly how much bytes are you going to send. This is exactly what you don't do when sending file name.
Here's a snippet to get you started:
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace FolderSync
{
class Program
{
static void Main()
{
var server = new Server();
server.Start();
new Client().TransmitFile(
new IPEndPoint(IPAddress.Loopback, 35434),
#"f:\downloads\ubuntu-14.04.3-desktop-amd64.iso");
Console.ReadLine();
server.Stop();
}
}
class Server
{
private readonly TcpListener tcpListener;
public Server()
{
tcpListener = new TcpListener(IPAddress.Loopback, 35434);
}
public void Start()
{
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
}
public void Stop()
{
tcpListener.Stop();
}
private void AcceptTcpClientCallback(IAsyncResult asyncResult)
{
//
// Big fat warning: http://stackoverflow.com/a/1230266/60188
tcpListener.BeginAcceptTcpClient(AcceptTcpClientCallback, null);
using(var tcpClient = tcpListener.EndAcceptTcpClient(asyncResult))
using(var networkStream = tcpClient.GetStream())
using(var binaryReader = new BinaryReader(networkStream, Encoding.UTF8))
{
var fileName = binaryReader.ReadString();
var length = binaryReader.ReadInt64();
var mib = length / 1024.0 / 1024.0;
Console.WriteLine("Receiving '{0}' ({1:N1} MiB)", fileName, mib);
var stopwatch = Stopwatch.StartNew();
var fullFilePath = Path.Combine(Path.GetTempPath(), fileName);
using(var fileStream = File.Create(fullFilePath))
networkStream.CopyTo(fileStream);
var elapsed = stopwatch.Elapsed;
Console.WriteLine("Received in {0} ({1:N1} MiB/sec)",
elapsed, mib / elapsed.TotalSeconds);
}
}
}
class Client
{
public void TransmitFile(IPEndPoint endPoint, string fileFullPath)
{
if(!File.Exists(fileFullPath)) return;
using(var tcpClient = new TcpClient())
{
tcpClient.Connect(endPoint);
using(var networkStream = tcpClient.GetStream())
using(var binaryWriter = new BinaryWriter(networkStream, Encoding.UTF8))
{
var fileName = Path.GetFileName(fileFullPath);
Debug.Assert(fileName != null, "fileName != null");
//
// BinaryWriter.Write(string) does length-prefixing automatically
binaryWriter.Write(fileName);
using(var fileStream = File.OpenRead(fileFullPath))
{
binaryWriter.Write(fileStream.Length);
fileStream.CopyTo(networkStream);
}
}
}
}
}
}
I am transferring file via socket programming in C# with the code given below. But when I take pdf as input file, it is not received correctly. I have seen sent and received files in notepad, they are not same.
I am not able to figure it out. any help appreciated !!!
Server Code:
class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
public int client;
public string fileName;
}
class Program
{
static int chunkSize = 16 * 1024;
static int chucksizeWithoutHeaderData = chunkSize - 8;
static List<ConnectionInfo> list = new List<ConnectionInfo>();
static Socket serverSocket;
static int nClient = 0;
static void AcceptCallback(IAsyncResult result)
{
ConnectionInfo info = new ConnectionInfo();
info.Socket = serverSocket.EndAccept(result);
info.Buffer = new byte[chunkSize * 4];
Console.WriteLine("Client connected");
nClient++;
info.client = nClient;
list.Add(info);
info.Socket.BeginReceive(info.Buffer, 0, info.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), info);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
static void ReceiveCallBack(IAsyncResult result)
{
ConnectionInfo info = result.AsyncState as ConnectionInfo;
try
{
Int32 nSegmentNumber = BitConverter.ToInt32(info.Buffer, 0);
Int32 nMaxSegment = BitConverter.ToInt32(info.Buffer, 4);
string strFileName = string.Format(#"D:\xyz.pdf", info.client);
//int bySize = info.Socket.EndReceive(result);
int bySize = 0;
try
{
bySize = info.Socket.EndReceive(result);
}
catch (Exception ex)
{
Console.WriteLine("Error from client {0}: {1}", info.client, ex.Message);
return;
}
if (bySize <= 0)
{
return;
}
using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate))
{
Console.WriteLine("Received segment {0} of {1} from client {2}", nSegmentNumber, nMaxSegment, info.client);
fs.Position = fs.Length;
fs.Write(info.Buffer, 8, bySize - 8);
Console.Write("Content in FILE: " + Encoding.ASCII.GetString(info.Buffer));
if (nSegmentNumber >= nMaxSegment)
{
Console.WriteLine("Completed receipt from client {0}", info.client);
}
}
info.Socket.BeginReceive(info.Buffer, 0, info.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), info);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void Main(string[] args)
{
try
{
Console.WriteLine("Server");
IPAddress address = IPAddress.Parse("127.0.0.1"); //The IP address of the server
IPEndPoint myEndPoint = new IPEndPoint(address, 6503);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(myEndPoint);
serverSocket.Listen(1000);
Console.WriteLine("Press enter to end server");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
}
}
Client code:
class Program
{
static TcpClient socket = new TcpClient();
static int chunkSize = 16 * 1024;
static int chucksizeWithoutHeaderData = chunkSize - 8;
static byte[] byReceiveBuffer = new byte[chunkSize * 4];
static void ReceiveCallBack(IAsyncResult result)
{
Socket socket = result.AsyncState as Socket;
try
{
int bySize = socket.EndReceive(result);
Console.WriteLine("Recieved bytes {0}", bySize);
if (bySize != 0)
{
Int32 nSegmentNumber = BitConverter.ToInt32(byReceiveBuffer, 0);
Int32 nMaxSegment = BitConverter.ToInt32(byReceiveBuffer, 4);
Console.WriteLine("Received segment {0} of {1}", nSegmentNumber, nMaxSegment);
string strFileName = string.Format(#"c:\temp\client-from-server.dat");
using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate))
{
fs.Position = fs.Length;
fs.Write(byReceiveBuffer, 8, bySize - 8);
}
if (nSegmentNumber >= nMaxSegment)
{
Console.WriteLine("all done");
}
}
socket.BeginReceive(byReceiveBuffer, 0, byReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void Main(string[] args)
{
Console.WriteLine("Press enter to go");
Console.ReadLine();
socket.Connect("127.0.0.1", 6503);
Console.WriteLine("Client");
Console.ReadLine();
string filePath = "";
string fileName = "C:\\abc.pdf";
//fileName = fileName.Replace("\\", "/");
while (fileName.IndexOf("\\") > -1)
{
filePath += fileName.Substring(0, fileName.IndexOf("\\") + 1);
fileName = fileName.Substring(fileName.IndexOf("\\") + 1);
}
byte[] byBuffer;
using (FileStream fs = new FileStream(#filePath+fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
using (BinaryReader br = new BinaryReader(fs))
{
int nMaxChunk = 0;
int nCurrentChunk = 0;
nMaxChunk = (int)(fs.Length / chucksizeWithoutHeaderData);
if ((nMaxChunk * chucksizeWithoutHeaderData) < fs.Length)
{
++nMaxChunk;
}
byte[] byMaxChunk = BitConverter.GetBytes(nMaxChunk);
Int64 nAmount = 0;
while (fs.Length > nAmount)
{
++nCurrentChunk;
byte[] byCurrentChunk = BitConverter.GetBytes(nCurrentChunk);
byBuffer = br.ReadBytes(chucksizeWithoutHeaderData);
Console.WriteLine("Sending {0}bytes, chunk {1} of {2}", byBuffer.Length, nCurrentChunk, nMaxChunk);
byte[] byTransmitBuffer = new byte[byBuffer.Length + 8];
Array.Copy(byCurrentChunk, byTransmitBuffer, 4);
Array.Copy(byMaxChunk, 0, byTransmitBuffer, 4, 4);
Array.Copy(byBuffer, 0, byTransmitBuffer, 8, byBuffer.Length);
socket.Client.Send(byTransmitBuffer);
nAmount += byBuffer.Length;
}
}
}
socket.Client.Disconnect(false);
Console.WriteLine("done");
Console.ReadLine();
}
}
My Server Class:
namespace Net_Send_File
{
class Server
{
private TcpListener listener;
private IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 15550);
private bool active;
string arg;
// private Socket xxx;
public Server()
{
Console.Clear();
Console.Title = "Server";
Main();
}
private void Main()
{
listener = new TcpListener(ipep);
try
{
listener.Start();
active = true;
ListenForConnections();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
private void ListenForConnections()
{
Console.Clear();
Console.WriteLine("Listening for connections...");
while (active)
{
TcpClient client = listener.AcceptTcpClient();
Console.BackgroundColor = ConsoleColor.Green;
Console.WriteLine("Connection # {0}", TCPIP(client));
new Thread(new ParameterizedThreadStart(HandleClientData)).Start(client);
}
}
private void HandleClientData(object _c)
{
TcpClient c = (TcpClient)_c;
string ipaddr = TCPIP(c);
NetworkStream s = c.GetStream();
// I tried this byte[] buffer = new byte[c.ReceiveBufferSize]; It throws an Exeption.
byte[] buffer = new byte[1024];
int bytesRead;
while (active)
{
bytesRead = 0;
try
{
bytesRead = s.Read(buffer, 0, buffer.Length/2);
}
catch (Exception ex)
{
Console.WriteLine("Socket error # {0}:\r\n{1}", ipaddr, ex.Message);
Console.ReadLine();
break;
}
if (bytesRead == 0)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine("Disconnected # {0}", ipaddr);
//new Thread(new ParameterizedThreadStart.ListenForConnections);
break;
}
string dataStr = Encoding.ASCII.GetString(buffer, 0, buffer.Length);
using (var fs = File.OpenWrite("test.txt"))
{
fs.Write(buffer, 0, buffer.Length);
fs.Close();
}
}
}
private string TCPIP(TcpClient c)
{
return ((IPEndPoint)c.Client.RemoteEndPoint).Address.ToString();
}
};
My Client Class:
class Client
{
private TcpClient client;
// private TcpClient client1;
private IPEndPoint ipep;
private int port;
public Client()
{
Console.Clear();
Console.Title = "Client";
bool error = false;
while (true)
{
Console.WriteLine("IPEndPoint: ");
string input = Console.ReadLine();
if (!input.Contains(':'))
{
Console.WriteLine("IPEndPoint in bad format");
break;
}
string[] s1 = input.Split(':');
IPAddress ipaddr;
if (!IPAddress.TryParse(s1[0], out ipaddr) || !int.TryParse(s1[1], out port))
{
Console.WriteLine("IPEndPoint in bad format");
Console.ReadLine();
error = true;
break;
}
ipep = new IPEndPoint(ipaddr, port);
try
{
client = new TcpClient();
client.Connect(ipep);
Console.WriteLine("client 1 is Ready!");
//client1 = new TcpClient();
//client1.Connect(ipep);
//Console.WriteLine("client 2 is Ready!");
}
catch (Exception ex)
{
Console.WriteLine("Unable to connect\r\nReason: {0}", ex.Message);
Console.ReadLine();
error = true;
}
break;
}
while (!error)
{
Console.Clear();
Console.WriteLine("File path: ");
string filePath = Console.ReadLine();
if (File.Exists(filePath) == false)
{
Console.WriteLine("File does not exist\r\nPress ENTER to try again");
Console.ReadLine();
}
byte[] buffer;
using (var fs = File.OpenRead(filePath))
{
buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
Int64 a = client.SendBufferSize; ;
fs.Close();
}
if (SendData(buffer))
{
// client.SendBufferSize(buffer);
//int a = client.SendBufferSize; ;
Console.WriteLine("File sent\r\nFile size: {0} KB", (buffer.Length / 1024));
//a.SetLength((buffer.Length / 1024));
Console.ReadLine();
}
break;
}
}
private bool SendData(byte[] data)
{
try
{
using (NetworkStream ns = client.GetStream())
{
ns.Write(data, 0, data.Length);
ns.Close();
}
return true;
}
catch (Exception ex)
{
Console.WriteLine("Unable to send file\r\nReason: {0}\r\nDetailed:\r\n{1}", ex.Message, ex.ToString());
Console.ReadLine();
return false;
}
}
}
}
}
First of all accept my apologise for my code is badly commented. Or to be honest no comments at all, almost.
Server class, under the method called private void HandleClient Data(object _) I have a buffer. Buffer is set to byte[1024]. I want to receive bufferSize (of my test file) from client, set Server buffer = to ClientBuffer and there after receive file. I have a test file which is 60MB. I tried to send my buffer size from client to server but it doesn't work well. Can someone tell me what I can do and how?
thanks in advance
You should implement an application level protocol, for example:
MESSAGE:
[SIZE (8 BYTES)][DATA (SIZE BYTES)]
Then, use BinaryReader and BinaryWriter
//client
var writer = new BinaryWriter(client.GetStream());
FileInfo fileInfoToSend = new FileInfo(path);
long fileSize = fileInfoToSend.Length;
writer.Write(fileSize);
using (FileStream fileStream = fileInfoToSend .Open(FileMode.Open, FileAccess.Read))
{
fileStream.CopyTo(writer.BaseStream);
fileStream.Close();
}
//server
var stream = c.GetStream();
var reader = new BinaryReader(c.GetStream());
FileInfo fileInfoToWrite = new FileInfo(path);
long fileSize = reader.ReadInt64();
using (FileStream fileStream = fileInfoToWrite.Create())
{
int read = 0;
for (long i = 0; i < fileSize; i += (long)read)
{
byte[] buffer = new byte[1024];
read = stream.Read(buffer, 0, Math.Min(fileSize - i, 1024));
if (read == 0)
return;//client disconnected!(or throw Exception)
fileStream.Write(buffer, 0, read);
}
}
untested
I have a large application written using .Net remoting for file transfer. This was borking in some circumstances with the sockets being forcibly closed - I wasn't using sockets directly, but a .Net remoting call with byte arrays (I wasn't sending the whole file in one transfer, I was splitting it up).
So, I decided to change the actual file transfer part to use sockets.
As a proof of concept, to see if I got the principles right, I have written a simple console client and a server.
I am using ASynch recieves but synchronous writes - I have tried with both being ASync but same reseult, and keeping it synchronous made debugging easier.
What the apps do (code below) is the server sits and waits for files to be transfered, and it stores them in a directory at a given name.
When enter is pressed, the server then reads the files recieved and sends them back to the clients who store them under a different name. I wanted to test file transfer both ways.
Using one instance of the client application, all is well - the server recieves it and then sends it back to the client. All is well. Yes, the client throws an exception when you terminate the server - but that is fine - I know that the socket was forcibly closed...I can deal with tidying upu the code when it is working.
However, when I create 2 instances of the client code (not forgetting to modify the code slightly to read a different file to send, and also to store the received file under a different name) - the server receives both files from the clients, sends the first one back just fine, and then a few segments into the second file it throws with a "non blocking socket operation could not be completed immediatly" - which is odd because nothing is blocking, and the recieves are async - and the sends are actually blocking!
Any suggestions please as to what I am doing wrong - no doubt it is something stupid, but still...
The aim of the final code is to be able to have n clients contact the server and send files to it, and also, at random intervals have the server send 1 or more files back to some/all of the clients.
Cheers folks!
Server code
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace SocketServer
{
class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
public int client;
}
class Program
{
static int chunkSize = 16 * 1024;
static int chucksizeWithoutHeaderData = chunkSize - 8;
static List<ConnectionInfo> list = new List<ConnectionInfo>();
static Socket serverSocket;
static int nClient = 0;
static void AcceptCallback(IAsyncResult result)
{
ConnectionInfo info = new ConnectionInfo();
info.Socket = serverSocket.EndAccept(result);
info.Buffer = new byte[chunkSize];
Console.WriteLine("Client connected");
nClient++;
info.client = nClient;
list.Add(info);
info.Socket.BeginReceive(info.Buffer,0,info.Buffer.Length,SocketFlags.None, new AsyncCallback(ReceiveCallBack), info);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback),null);
}
static void ReceiveCallBack(IAsyncResult result)
{
ConnectionInfo info = result.AsyncState as ConnectionInfo;
try
{
Int32 nSegmentNumber = BitConverter.ToInt32(info.Buffer,0);
Int32 nMaxSegment = BitConverter.ToInt32(info.Buffer,4);
string strFileName = string.Format(#"c:\temp\from-client-{0}.dat",info.client);
int bySize = info.Socket.EndReceive(result);
using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate))
{
Console.WriteLine("Received segment {0} of {1} from client {2}", nSegmentNumber, nMaxSegment, info.client);
fs.Position = fs.Length;
fs.Write(info.Buffer, 8, bySize-8);
if (nSegmentNumber >= nMaxSegment)
{
Console.WriteLine("Completed receipt from client {0}", info.client);
}
}
info.Socket.BeginReceive(info.Buffer, 0, info.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), info);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void Main(string[] args)
{
try
{
Console.WriteLine("Server");
IPAddress address = IPAddress.Parse("127.0.0.1"); //The IP address of the server
IPEndPoint myEndPoint = new IPEndPoint(address, 6503);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(myEndPoint);
serverSocket.Listen(1000);
for (int n = 0; n < 10; ++n)
{
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
Console.WriteLine("Server now waiting");
Console.ReadLine();
foreach (ConnectionInfo info in list)
{
string strFileName = string.Format(#"c:\temp\from-client-{0}.dat", info.client);
using (FileStream fs = new FileStream(strFileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
int nMaxChunk = 0;
int nCurrentChunk = 0;
nMaxChunk = (int)(fs.Length / chucksizeWithoutHeaderData);
if ((nMaxChunk * chucksizeWithoutHeaderData) < fs.Length)
{
++nMaxChunk;
}
using (BinaryReader br = new BinaryReader(fs))
{
byte[] byBuffer;
Int64 nAmount = 0;
byte[] byMaxChunk = BitConverter.GetBytes(nMaxChunk);
while (fs.Length > nAmount)
{
++nCurrentChunk;
byte[] byCurrentChunk = BitConverter.GetBytes(nCurrentChunk);
byBuffer = br.ReadBytes(chucksizeWithoutHeaderData);
Console.WriteLine("Sending {0}bytes, chunk {1} of {2} to client {3}", byBuffer.Length,nCurrentChunk,nMaxChunk, info.client);
byte [] byTransmitBuffer = new byte[byBuffer.Length + 8];
Array.Copy(byCurrentChunk, byTransmitBuffer, 4);
Array.Copy(byMaxChunk, 0,byTransmitBuffer, 4, 4);
Array.Copy(byBuffer, 0, byTransmitBuffer, 8, byBuffer.Length);
info.Socket.Send(byTransmitBuffer);
nAmount += byBuffer.Length;
}
}
}
}
Console.WriteLine("Press enter to end server");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex);
Console.ReadLine();
}
}
}
}
Client code
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace SocketClient
{
class Program
{
static TcpClient socket = new TcpClient();
static int chunkSize = 16 * 1024;
static int chucksizeWithoutHeaderData = chunkSize - 8;
static byte[] byReceiveBuffer = new byte[chunkSize];
static void ReceiveCallBack(IAsyncResult result)
{
Socket socket = result.AsyncState as Socket;
try
{
int bySize = socket.EndReceive(result);
Console.WriteLine("Recieved bytes {0}", bySize);
if (bySize != 0)
{
Int32 nSegmentNumber = BitConverter.ToInt32(byReceiveBuffer, 0);
Int32 nMaxSegment = BitConverter.ToInt32(byReceiveBuffer, 4);
Console.WriteLine("Received segment {0} of {1}", nSegmentNumber, nMaxSegment);
string strFileName = string.Format(#"c:\temp\client-from-server.dat");
using (FileStream fs = new FileStream(strFileName, FileMode.OpenOrCreate))
{
fs.Position = fs.Length;
fs.Write(byReceiveBuffer, 8, bySize-8);
}
if (nSegmentNumber >= nMaxSegment)
{
Console.WriteLine("all done");
}
}
socket.BeginReceive(byReceiveBuffer, 0, byReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
static void Main(string[] args)
{
Console.WriteLine("Press enter to go");
Console.ReadLine();
socket.Connect("127.0.0.1", 6503);
Console.WriteLine("Client");
Console.ReadLine();
byte[] byBuffer;
socket.Client.BeginReceive(byReceiveBuffer, 0, byReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socket.Client);
using (FileStream fs = new FileStream(#"c:\temp\filetosend.jpg", FileMode.Open, FileAccess.Read, FileShare.None))
{
using (BinaryReader br = new BinaryReader(fs))
{
int nMaxChunk = 0;
int nCurrentChunk = 0;
nMaxChunk = (int)(fs.Length / chucksizeWithoutHeaderData);
if ((nMaxChunk * chucksizeWithoutHeaderData) < fs.Length)
{
++nMaxChunk;
}
byte[] byMaxChunk = BitConverter.GetBytes(nMaxChunk);
Int64 nAmount = 0;
while (fs.Length > nAmount)
{
++nCurrentChunk;
byte[] byCurrentChunk = BitConverter.GetBytes(nCurrentChunk);
byBuffer = br.ReadBytes(chucksizeWithoutHeaderData);
Console.WriteLine("Sending {0}bytes, chunk {1} of {2}", byBuffer.Length, nCurrentChunk, nMaxChunk);
byte[] byTransmitBuffer = new byte[byBuffer.Length + 8];
Array.Copy(byCurrentChunk, byTransmitBuffer, 4);
Array.Copy(byMaxChunk, 0, byTransmitBuffer, 4, 4);
Array.Copy(byBuffer, 0, byTransmitBuffer, 8, byBuffer.Length);
socket.Client.Send(byTransmitBuffer);
nAmount += byBuffer.Length;
}
}
}
Console.WriteLine("done");
Console.ReadLine();
}
}
}
Since you are modifyng the infrastructure, use an FTP library in C# and install some free FTP server ( ie FileZilla or anyone else ). You can easily use some FTP library as for example this one that is reliable ( i used it on production code ).
Socket programming can be tricky. If you send 100 bytes, it doesn't mean you will receive 100 bytes in your server. You can receive those 100 bytes in multiple packets, you have to add code to control that.
I said that because of this line in your server code:
fs.Write(info.Buffer, 8, bySize-8);
you are assuming you will receive at least 8 bits and it can be wrong. bySize can be smaller than your chunk size (and it can be zero if the connection has been closed by the client or <0 if there was an error.)
About your error, I tested your code and I could replicate your problem:
start the server
start the client and transfer the file
exit the client pressing enter
the server crashes
The server crashes because the socket is waiting for more data after all the file is transferred. And the client closed it.
I solved the problem closing the connection in the client after the file is sent:
socket.Client.Disconnect(false);
Then the server receives bySize=0 bytes, meaning that the connection was closed. In the server I replaced this:
int bySize = socket.EndReceive(result);
for this:
int bySize = 0;
try
{
bySize = info.Socket.EndReceive(result);
}
catch (Exception ex)
{
Console.WriteLine("Error from client {0}: {1}", info.client, ex.Message);
return;
}
if (bySize <= 0)
return;
Have a look here: http://msdn.microsoft.com/en-us/library/5w7b7x5f.aspx#Y240
and here: http://msdn.microsoft.com/en-us/library/fx6588te.aspx
EDIT: I forgot to mention this, you only need to call BeginAccept once. I removed the for statement.
// for (int n = 0; n < 10; ++n)
// {
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
// }
I think that I may have a solution - although I am not 100% confident...I will keep testing and report back if it fails.
Anyways - I have set the socket.SendBufferSize and RecieveBufferSize to 4 x the chunk size, and now all seems to be well.
The thing there (re buffer size) just put the problem off, as I thought it might.
However, I came across a snippet of code elsewhere, and putting these lines into the code fixed the issue.
try
{
bySent = info.Socket.Send(byTransmitBuffer);
}
catch (SocketException ex)
{
Console.WriteLine("Only sent {0}, remaining = {1}", bySent,fs.Length -nAmount);
if (ex.SocketErrorCode == SocketError.WouldBlock ||
ex.SocketErrorCode == SocketError.IOPending ||
ex.SocketErrorCode == SocketError.NoBufferSpaceAvailable)
{
// socket buffer is probably full, wait and try again
Thread.Sleep(30);
}
else
throw ex; // any serious error occurr
}
I'm trying to replace this:
void ProcessRequest(object listenerContext)
{
var context = (HttpListenerContext)listenerContext;
Uri URL = new Uri(context.Request.RawUrl);
HttpWebRequest.DefaultWebProxy = null;
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(URL);
httpWebRequest.Method = context.Request.HttpMethod;
httpWebRequest.Headers.Clear();
if (context.Request.UserAgent != null) httpWebRequest.UserAgent = context.Request.UserAgent;
foreach (string headerKey in context.Request.Headers.AllKeys)
{
try { httpWebRequest.Headers.Set(headerKey, context.Request.Headers[headerKey]); }
catch (Exception) { }
}
using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse())
{
Stream responseStream = httpWebResponse.GetResponseStream();
if (httpWebResponse.ContentEncoding.ToLower().Contains("gzip"))
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
else if (httpWebResponse.ContentEncoding.ToLower().Contains("deflate"))
responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
MemoryStream memStream = new MemoryStream();
byte[] respBuffer = new byte[4096];
try
{
int bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
while (bytesRead > 0)
{
memStream.Write(respBuffer, 0, bytesRead);
bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
}
}
finally
{
responseStream.Close();
}
byte[] msg = memStream.ToArray();
context.Response.ContentLength64 = msg.Length;
using (Stream strOut = context.Response.OutputStream)
{
strOut.Write(msg, 0, msg.Length);
}
}
catch (Exception ex)
{
// Some error handling
}
}
with sockets. This is what I have so far:
void ProcessRequest(object listenerContext)
{
HttpListenerContext context = (HttpListenerContext)listenerContext;
Uri URL = new Uri(context.Request.RawUrl);
string getString = string.Format("GET {0} HTTP/1.1\r\nHost: {1}\r\nAccept-Encoding: gzip\r\n\r\n",
context.Request.Url.PathAndQuery,
context.Request.UserHostName);
Socket socket = null;
string[] hostAndPort;
if (context.Request.UserHostName.Contains(":"))
{
hostAndPort = context.Request.UserHostName.Split(':');
}
else
{
hostAndPort = new string[] { context.Request.UserHostName, "80" };
}
IPHostEntry ipAddress = Dns.GetHostEntry(hostAndPort[0]);
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(ipAddress.AddressList[0].ToString()), int.Parse(hostAndPort[1]));
socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(ip);
BEGIN NEW CODE
Encoding ASCII = Encoding.ASCII;
Byte[] byteGetString = ASCII.GetBytes(getString);
Byte[] receiveByte = new Byte[256];
string response = string.Empty;
socket.Send(byteGetString, byteGetString.Length, 0);
Int32 bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
response += ASCII.GetString(receiveByte, 0, bytes);
while (bytes > 0)
{
bytes = socket.Receive(receiveByte, receiveByte.Length, 0);
strPage = strPage + ASCII.GetString(receiveByte, 0, bytes);
}
socket.Close();
string separator = "\r\n\r\n";
string header = strPage.Substring(0,strPage.IndexOf(separator));
string content = strPage.Remove(0, strPage.IndexOf(separator) + 4);
byte[] byteResponse = ASCII.GetBytes(content);
context.Response.ContentLength64 = byteResponse .Length;
context.Response.OutputStream.Write(byteResponse , 0, byteResponse .Length);
context.Response.OutputStream.Close();
END NEW CODE
After connecting to the socket I don't know how to get the Stream response to decompress, and send back to context.Response.OutputStream
Any help will be appreciated.
Thanks.
Cheers.
EDIT 2:
With this edit now seems to be working fine (same as HttpWebRequest at least). Do you find any error here?
EDIT 3:
False alarm... Still can't get this working
EDIT 4:
I needed to add the following lines to Scott's code ... because not always the first to bytes of reponseStream are the gzip magic number.
The sequence seems to be: 0x0a (10), 0x1f (31), 0x8b (139). The last two are the gzip magic number. The first number was always before in my tests.
if (contentEncoding.Equals("gzip"))
{
int magicNumber = 0;
while (magicNumber != 10)
magicNumber = responseStream.ReadByte();
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
}
Here's some code that works for me.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.IO.Compression;
namespace HttpUsingSockets {
public class Program {
private static readonly Encoding DefaultEncoding = Encoding.ASCII;
private static readonly byte[] LineTerminator = new byte[] { 13, 10 };
public static void Main(string[] args) {
var host = "stackoverflow.com";
var url = "/questions/523930/sockets-in-c-how-to-get-the-response-stream";
IPHostEntry ipAddress = Dns.GetHostEntry(host);
var ip = new IPEndPoint(ipAddress.AddressList[0], 80);
using (var socket = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp)) {
socket.Connect(ip);
using (var n = new NetworkStream(socket)) {
SendRequest(n, new[] {"GET " + url + " HTTP/1.1", "Host: " + host, "Connection: Close", "Accept-Encoding: gzip"});
var headers = new Dictionary<string, string>();
while (true) {
var line = ReadLine(n);
if (line.Length == 0) {
break;
}
int index = line.IndexOf(':');
headers.Add(line.Substring(0, index), line.Substring(index + 2));
}
string contentEncoding;
if (headers.TryGetValue("Content-Encoding", out contentEncoding)) {
Stream responseStream = n;
if (contentEncoding.Equals("gzip")) {
responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
}
else if (contentEncoding.Equals("deflate")) {
responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
}
var memStream = new MemoryStream();
var respBuffer = new byte[4096];
try {
int bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
while (bytesRead > 0) {
memStream.Write(respBuffer, 0, bytesRead);
bytesRead = responseStream.Read(respBuffer, 0, respBuffer.Length);
}
}
finally {
responseStream.Close();
}
var body = DefaultEncoding.GetString(memStream.ToArray());
Console.WriteLine(body);
}
else {
while (true) {
var line = ReadLine(n);
if (line == null) {
break;
}
Console.WriteLine(line);
}
}
}
}
}
static void SendRequest(Stream stream, IEnumerable<string> request) {
foreach (var r in request) {
var data = DefaultEncoding.GetBytes(r);
stream.Write(data, 0, data.Length);
stream.Write(LineTerminator, 0, 2);
}
stream.Write(LineTerminator, 0, 2);
// Eat response
var response = ReadLine(stream);
}
static string ReadLine(Stream stream) {
var lineBuffer = new List<byte>();
while (true) {
int b = stream.ReadByte();
if (b == -1) {
return null;
}
if (b == 10) {
break;
}
if (b != 13) {
lineBuffer.Add((byte)b);
}
}
return DefaultEncoding.GetString(lineBuffer.ToArray());
}
}
}
You could substitute this for the Socket/NetworkStream and save a bit of work.
using (var client = new TcpClient(host, 80)) {
using (var n = client.GetStream()) {
}
}
Socket, by definition, is the low level to access the network. You can even use datagram protocols with a socket. In that case a stream does not make sense at all.
While I'm not sure why are you doing what HttpWebRequest easily accomplishes, to read/write data to a socket, you use the Send/Receive methods. If you want to have a stream like access to a TCP socket, you should use the TcpClient/TcpListener classes which wrap a socket and provide a network stream for it.
There's also the NetworkStream class that takes a Socket as a parameter. ;)