Sending message from C# client to C server - c#

I'm fairly new to socket programming, but, using an online tutorial, I've successfully sent a short string from one machine to another using C.
The problem I'm having is with trying to send a string from a client written in C#. The server (written in C) prints out a blank/empty string.
Here is the C code that runs on the "server" machine (in this case a router running OpenWRT):
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
char recvBuff[1025];
int bytesRead;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(1234);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
printf("Listening for string on port 1234...\n");
listen(listenfd, 10);
while(1)
{
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
bytesRead = recv(connfd, recvBuff, 1024, 0); // Receive string
if (bytesRead < 0)
{
printf("Error reading from stream\n");
}
recvBuff[bytesRead] = 0; // null-terminate the string
printf("%d:%s\n", bytesRead, recvBuff);
close(connfd);
sleep(1);
}
}
When sending this little server a string from another C program it works exactly as expected (prints the string out then waits for another one). [Note: I don't think the C client's code is relevant, but I can post it if need be]
However, when trying to send it a string from a C# program (copied below), the server prints out 0: (i.e. 0 bytes read, followed by an empty string) and I can't for the life of me figure out what the issue is. Both apps are pretty straightforward, so I'm assuming I should be using something other than WriteLine (I've also tried Write but to no avail).
C# Client:
namespace SocketTest
{
class Program
{
static void Main(string[] args)
{
TcpClient client = new TcpClient("10.45.13.220", 1234);
Stream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine("Testing...");
client.Close();
}
}
}

To null terminate a string use
recvBuff[bytesRead] = '\0';
And call close on writer (which causes the writer to flush any pending bytes)
writer.WriteLine("Testing...");
writer.Close();
client.Close();

Related

C# tcpclient not writing to socket...or maybe I'm not reading all of the response?

I'm trying to write a C# console app to interface with a PC Miler telnet server. I managed to get this to work in powershell, but my C# code is not working.
static void Main(string[] args)
{
byte[] ReadBuffer = new byte[1024];
string[] stringSeparators = new string[]{"READY"};
try
{
TcpClient Socket = new TcpClient("myServer", 8320);
if (Socket.Connected) {
NetworkStream netStream = Socket.GetStream();
string PCMSResponse = "";
// Check for the READY prompt
if (netStream.CanRead) {
int byteRead = 0;
while (netStream.DataAvailable) {
byteRead = netStream.Read(ReadBuffer, 0, ReadBuffer.Length);
PCMSResponse += Encoding.ASCII.GetString(ReadBuffer, 0 , byteRead).Trim();
}
} else {
Console.WriteLine("Cannot read from myServer");
}
Thread.Sleep(1000);
// Get New Trip
if (netStream.CanWrite) {
Byte[] PCMSCommandBuffer = Encoding.ASCII.GetBytes("PCMSNewTrip");
netStream.Write(PCMSCommandBuffer, 0 , PCMSCommandBuffer.Length);
Thread.Sleep(1000);
} else {
Console.WriteLine("Cannot write to myServer.");
}
if (netStream.CanRead) {
int byteRead = 0;
while (netStream.DataAvailable) {
byteRead = netStream.Read(ReadBuffer, 0, ReadBuffer.Length);
PCMSResponse += Encoding.ASCII.GetString(ReadBuffer, 0, byteRead).Trim();
}
} else {
Console.WriteLine("Cannot read from myServer");
}
string[] ResponseArray = PCMSResponse.Split(stringSeparators, StringSplitOptions.None);
int c = ResponseArray.Length - 2;
string TripID = ResponseArray[c].Replace(System.Environment.NewLine, "").Replace("\0", "");
Console.WriteLine(PCMSResponse);
}
}
catch (SocketException)
{
Console.WriteLine("Unable to connect to server")
}
}
Expected output
ALK PCMILER SERVER READY
pcmsnewtrip
53
READY
Actual output
ALK PCMILER SERVER READY
It seems like I am not actually writing to the server because if I were to write an invalid command to the server I would see an error like this:
ALK PCMILER SERVER READY
pmctripnew
NO SUCH FUNCTION
READY
I know I can write to the socket because I'm not getting my error message Cannot write to myServer.
Not sure what I'm doing wrong. :(
Let me know if you'd also like to see the powershell code.
EDIT - If you're going to edit this post, at least keep the expected output AS EXPECTED! Changing the expected output changes the intent of the post. (EG, if I want apples but you edit my post to ask for oranges, I'm not going to get the correct answer.)
So the issue turns out to be that the NetworkStream.Write method does not end a string with a line terminator. As a result, when I wrote my PCMSCommandBuffer bytes to the stream, the command was never processed by the server (because its waiting for more of the command or the enter key to let it know you're done). My solution was to assign the stream to a StreamWriter. Then I can use the WriteLine method whichdoes send a line terminator.
streamWriter = new StreamWriter(netStream);
// Get New Trip
if (netStream.CanWrite) {
Byte[] PCMSCommandBuffer = Encoding.ASCII.GetBytes("PCMSNewTrip");
//netStream.Write(PCMSCommandBuffer, 0 , PCMSCommandBuffer.Length);
streamWriter.WriteLine("PCMSNewTrip");
//netStream.Flush();
streamWriter.Flush();
Console.WriteLine(PCMSCommandBuffer.Length + " PCMSNewTrip");
Thread.Sleep(1000);
} else {
Console.WriteLine("Cannot write to EW-APP1.");
}

How to make sure that stream is read completely?

I am using Network Stream, TcpListener and Sockets.
I want to make sure that all the data from sender is received by receiver.
I have below code for receiver
private void StartReciever()
{
util.LoadSettings();
string tcpIpAddress = util.svrSettings["IpAddress"];
string port = util.svrSettings["Port"];
string outDir = util.svrSettings["isOutput"];
new Thread(
() =>
{
if (!File.Exists(util.settingFile))
Logger("Please setup the services first.");
else
{
try
{
IPAddress ipAddress = IPAddress.Parse(tcpIpAddress);
TcpListener tcpListener = new TcpListener(ipAddress, Convert.ToInt32(port));
tcpListener.Start();
Logger("\nWaiting for a client to connect...");
//blocks until a client connects
Socket socketForClient = tcpListener.AcceptSocket();
Logger("\nClient connected");
//Read data sent from client
NetworkStream networkStream = new NetworkStream(socketForClient);
int bytesReceived, totalReceived = 0;
string fileName = "testing.txt";
byte[] receivedData = new byte[10000];
do
{
bytesReceived = networkStream.Read
(receivedData, 0, receivedData.Length);
totalReceived += bytesReceived;
Logger("Progress of bytes recieved: " + totalReceived.ToString());
if (!File.Exists(fileName))
{
using (File.Create(fileName)) { };
}
using (var stream = new FileStream(fileName, FileMode.Append))
{
stream.Write(receivedData, 0, bytesReceived);
}
}
while (bytesReceived != 0);
Logger("Total bytes read: " + totalReceived.ToString());
socketForClient.Close();
Logger("Client disconnected...");
tcpListener.Stop();
}
catch (Exception ex)
{
// Error : "Only one usage of each socket address (protocol/network address/port) is normally permitted"
Logger("There is some error: " + ex.Message);
}
}
}).Start();
}
How can I make sure that my code after do-while loop executes ?
Sender Code:
private static void SendData(string tcpIpAddress, string port, string filename)
{
new Thread(
() =>
{
TcpClient tcpClient = new TcpClient(tcpIpAddress, Convert.ToInt32(port));
//const int bufsize = 8192;
const int bufsize = 10000;
var buffer = new byte[bufsize];
NetworkStream networkStream = tcpClient.GetStream();
using (var readFile = File.OpenRead(filename))
{
int actuallyRead;
while ((actuallyRead = readFile.Read(buffer, 0, bufsize)) > 0)
{
networkStream.Write(buffer, 0, actuallyRead);
}
}
}).Start();
}
How can I make sure that my code after do-while loop executes?
Here, it's easy because you can close the connection on the sender which will cause the receiver to read zero bytes and terminate the loop.
In fact you forgot to clean up the sender. Dispose of all resources. This fixes the problem.
Your code would benefit from the Code Review SE. I see about a dozen issues immaterial to this question. Would be a great way for you to improve yourself.
For example you can replace the copy loops with Stream.Copy.
The usual way I've seen this done to prepend the data being sent with the number of bytes about to be sent. That way the receiver knows that the packet has been read. Even if the network gets interrupted or the sender sends something else without a break.
Note that doing this you can also read the first 8 bytes (or 4 if an Int32 will do the job) before allocating the read buffer which can help optimise the buffer size
Another common way of doing it is with a specific terminator, but then you have to guarantee that whatever you're sending cannot contain that terminator

Duplex NamePipe between C# and C++

I'am having some trouble to make a Duplex communication between a C# server and a C++ work:
The objective is to create the pipe, read something from the client and write something back.
Everything is working fine if i'm just reading from the client or just writing to the client but I can't do both one after the other!
Here's my C# server:
// Create a name pipe
using (NamedPipeServerStream pipeStream = new NamedPipeServerStream("TestPipe"))
{
Console.WriteLine("[Server] Pipe created {0}", pipeStream.GetHashCode());
// Wait for a connection
pipeStream.WaitForConnection();
Console.WriteLine("[Server] Pipe connection established");
//Reading Part
using (StreamReader sr = new StreamReader(pipeStream))
{
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("{0}: {1}", DateTime.Now, temp);
}
}
//Writing Part
using (StreamWriter sw = new StreamWriter(pipeStream))
{
sw.AutoFlush = true;
String st = "send Back\0";
sw.WriteLine(st);
}
}
And Here is the C++ client:
HANDLE hFile;
BOOL flg;
DWORD dwWrite;
char szPipeUpdate[200];
hFile = CreateFile(L"\\\\.\\pipe\\TestPipe", GENERIC_WRITE|GENERIC_READ,
0, NULL, OPEN_EXISTING,
0, NULL);
strcpy(szPipeUpdate,"Sending some data from client to server!");
if(hFile == INVALID_HANDLE_VALUE)
{
DWORD dw = GetLastError();
printf("CreateFile failed for Named Pipe client\n:" );
}
else
{
flg = WriteFile(hFile, szPipeUpdate, strlen(szPipeUpdate), &dwWrite, NULL);
if (FALSE == flg)
{
printf("WriteFile failed for Named Pipe client\n");
}
else
{
printf("WriteFile succeeded for Named Pipe client\n");
}
}
printf("Let's read!\n");
//Read the datas sent by the server
BOOL fFinishRead = FALSE;
do
{
char chResponse[200];
DWORD cbResponse, cbRead;
cbResponse = sizeof(chResponse);
fFinishRead = ReadFile(
hFile, // Handle of the pipe
chResponse, // Buffer to receive the reply
cbResponse, // Size of buffer in bytes
&cbRead, // Number of bytes read
NULL // Not overlapped
);
if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
{
DWORD dwError = GetLastError();
wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
break;
}
std::cout << chResponse;
} while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA
CloseHandle(hFile);
With the Server & Client NamedPipeStreams you need to make sure at least one end of the connection is attempting to read at all times, otherwise writes will fail. I'd guess it's a timing issue between your client & server around whichever is listening/writing. Here's a quick blog post explaining this behavior as well.
I'd recomend using two streams on the client & server, both reading/writing from the same pipe.

Two way C++ to C# communication using named pipes

I am trying to have a 2-way communication between a VC++ 6 app and a C# app. I am using named pipes. In my C++ code I can read the message from the C# client but then the server "dies" and I have to restart it again.
What I want to do is have the C# app connect to the C++ app, request a status, and the C++ app goes off and checks the status, and then returns either "busy" or "idle".
I can't write anything back to the C# client as it says the connection has been closed. Some things I have commented out are things I have tried already.
C++ code (started as a thread)
UINT CNamedPipe::StartNamedPipeServer()
{
LPTSTR lpszPipename = "\\\\.\\pipe\\SAPipe";
HANDLE hPipe;
BOOL flg;
DWORD dwWrite,dwRead;
char szServerUpdate[200];
char szClientUpdate[200];
hPipe = CreateNamedPipe ( lpszPipename,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_NOWAIT, //changed from nowait
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // no security attribute
if (hPipe == INVALID_HANDLE_VALUE)
return 0;
ConnectNamedPipe(hPipe, NULL);
while(m_bServerActive) //This seems to work well ....
{
//Read from client
flg = ReadFile(hPipe,szClientUpdate,strlen(szClientUpdate),&dwRead, NULL);
if(flg) //Read something from the client!!!!
{
CString csMsg,csTmp;
for(int i=0;i<dwRead;i++){
csTmp.Format("%c",szClientUpdate[i]);
csMsg += csTmp;
}
AfxMessageBox("Client message: " + csMsg);
strcpy( szServerUpdate,"busy");
//Write status to Client
flg = WriteFile(hPipe, szServerUpdate, strlen(szServerUpdate), &dwWrite, NULL);
EndServer();
StartServer();
}
}
return 0;
}
C# Code:
public void ThreadStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
// Only continue after the server was created -- otherwise we just fail badly
// SyncClientServer.WaitOne();
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "SAPipe"))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
//Write from client to server
using (StreamWriter sw = new StreamWriter(pipeStream))
{
sw.WriteLine("What's your status?");
}
//Read server reply
/*using (StreamReader sr = new StreamReader(pipeStream))
{
string temp = "";
temp = sr.ReadLine(); //Pipe is already closed here ... why?
MessageBox.Show(temp);
}*/
//pipeStream.Close();
}
}
}
Disposing of a StreamWriter or StreamReader will close the underlying stream.
Your using statements therefore will be causing the stream to close.
public void ThreadStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
// Only continue after the server was created -- otherwise we just fail badly
// SyncClientServer.WaitOne();
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "SAPipe"))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
//Write from client to server
StreamWriter sw = new StreamWriter(pipeStream))
sw.WriteLine("What's your status?");
//Read server reply
StreamReader sr = new StreamReader(pipeStream)
string temp = "";
temp = sr.ReadLine(); //Pipe is already closed here ... why?
MessageBox.Show(temp);
}
}
It should also be noted that because you wrap your stream in a using statement, the commented out pipeStream.Close() function isn't needed.
Ok, got it working for my application .... thanks Blam !
Here's the C++ server (run this inside a thread):
UINT CNamedPipe::StartNamedPipeServer()
{
if(!m_bServerActive)
return 0;
LPTSTR lpszPipename = "\\\\.\\pipe\\MyPipe";
HANDLE hPipe;
BOOL flg;
DWORD dwWrite,dwRead;
char szServerUpdate[200];
char szClientUpdate[200];
hPipe = CreateNamedPipe ( lpszPipename,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT, //HAS TO BE THIS
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // no security attribute
if (hPipe == INVALID_HANDLE_VALUE)
return 0;
ConnectNamedPipe(hPipe, NULL);
strcpy( szServerUpdate,"busy");
//Write status to Client
flg = WriteFile(hPipe, szServerUpdate, strlen(szServerUpdate), &dwWrite, NULL);
EndServer();
StartServer();
return 0;
}
And here's the C# client:
public void ThreadStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "MyPipe", PipeDirection.InOut))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
if (!pipeStream.IsConnected) //It thinks it's connected but can't read anything ....
{
MessageBox.Show("Failed to connect ....");
return;
}
//Read server reply
StreamReader sr = new StreamReader(pipeStream);
char[] c = new char[200];
while (sr.Peek() >= 0)
{
sr.Read(c, 0, c.Length);
}
string s = new string(c);
MessageBox.Show(s);
}
}
I'm not actually sending anything from the client to the server because I don't need to ... the key thing in this was the PIPE_WAIT parameter in the CreateNamedPipe() function. This makes the server wait for a client connection.

File Transfer using sockets and multiple clients

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
}

Categories