I have a receiver and sender client that pretty much transfer files. This is what I have so far on the receiver:
Thread t1;
int flag = 0;
string receivedPath;
public delegate void MyDelegate(string s);
int bytesRead = 0;
bool endReceive = false;
public Form1()
{
t1 = new Thread(new ThreadStart(StartListening));
t1.Start();
InitializeComponent();
}
public class StateObject
{
// Client socket.
public Socket workSocket = null;
public const int BufferSize = 1024*100;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
}
public static ManualResetEvent allDone = new ManualResetEvent(false);
public void StartListening()
{
byte[] bytes = new Byte[1024*1000];
IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, 9050);
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(ipEnd);
listener.Listen(100);
while (true)
{
allDone.Reset();
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
allDone.WaitOne();
if (endReceive)
{
listener.Disconnect(true);
}
}
}
catch (Exception ex)
{
}
}
public void AcceptCallback(IAsyncResult ar)
{
allDone.Set();
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
flag = 0;
}
public void ReadCallback(IAsyncResult ar)
{
int fileNameLen = 1;
String content = String.Empty;
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
try
{
bytesRead= handler.EndReceive(ar);
}
catch (SocketException x)
{
MessageBox.Show("File Transfer was cancelled");
Invoke(new MyDelegate(LabelWriter), new object[] { "Waiting for connections" });
if (File.Exists(receivedPath))
{
File.Delete(receivedPath);
return;
}
}
if (endReceive)
{
handler.Disconnect(true);
if (File.Exists(receivedPath))
{
File.Delete(receivedPath);
}
return;
}
if (bytesRead > 0)
{
if (flag == 0)
{
fileNameLen = BitConverter.ToInt32(state.buffer, 0);
string fileName = Encoding.UTF8.GetString(state.buffer, 4, fileNameLen);
receivedPath = fileName;
Invoke(new MyDelegate(LabelWriter), new object[] { "Receiving File: " + fileName });
flag++;
}
if (flag >= 1)
{
BinaryWriter writer = new BinaryWriter(File.Open(receivedPath, FileMode.Append));
if (flag == 1)
{
writer.Write(state.buffer, 4 + fileNameLen, bytesRead - (4 + fileNameLen));
flag++;
ReleaseMemory();
}
else
writer.Write(state.buffer, 0, bytesRead);
writer.Close();
try
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
catch (SocketException exc)
{
MessageBox.Show("File Transfer was cancelled");
Invoke(new MyDelegate(LabelWriter), new object[] { "Waiting for connections" });
if (File.Exists(receivedPath))
{
File.Delete(receivedPath);
return;
}
}
}
}
else
Invoke(new MyDelegate(LabelWriter), new object[] {"Data Received"});
}
What I want to achieve is the ability to cancel the transfer while it's taking place. What I have in mind was setting a boolean "EndReceive" to false and every time the ReadCallBack Method is called, EndReceive is checked. If it's false, I disconnect the socket. It works in stopping the receiving of the file, however, the sender application just freezes. This is basically how I send:
while (true)
{
int index = 0;
while (index < fs.Length)
{
int bytesRead = fs.Read(fileData, index, fileData.Length - index);
if (bytesRead == 0)
{
break;
}
index += bytesRead;
}
if (index != 0)
{
try
{
clientSock.Send(fileData, index, SocketFlags.None);
}
catch (Exception sexc)
{
MessageBox.Show("Transfer Cancelled");
return;
}
ReleaseMemory();
if ((progressBar1.Value + (1024 * 1000)) > fs.Length)
{
progressBar1.Value += ((int)fs.Length - progressBar1.Value);
}
else
progressBar1.Value += (1024 * 1000);
lblSent.Text = index.ToString();
}
if (index != fileData.Length)
{
ReleaseMemory();
progressBar1.Value = 0;
clientSock.Close();
fs.Close();
break;
}
}
Any thoughts?
I think it's difficult to force an interrupt for the sort of bulk transfers you're doing and have it close cleanly just using TCP. What if you take your block size of 1024, and after each kB transfers, send a quick ack or nack byte from the receiver to sender? Nack indicating the transfer is to be interrupted, ack indicating continue.
Unfortunately this requires full duplexing for the receiver/sender, and be careful! Even though it's asynchronous IO, you do need to call BeginReceive and BeginSend on separate threads if you plan and reading and writing from the same socket.