I'm trying to write a simple TCP network tunnel in C# (with MonoDevelop, not VS). Currently, it works up to the point at which I make a connection. After connecting with netcat localhost <portnum>, I get packets filled with nulls sent at me constantly. My code is:
Tunnel.cs
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
public class Tunnel {
public TcpListener listener;
public NetworkStream local, remote;
private Thread threadControl, threadLocalSide, threadRemoteSide;
public Tunnel(int localPort, String remoteServer, int remotePort) {
this.listener = new TcpListener(new IPEndPoint(new IPAddress(new byte[] { 127, 0, 0, 1 }), localPort));
this.remote = new TcpClient(remoteServer, remotePort).GetStream();
this.threadControl = new Thread(new ThreadStart(this.thread));
this.threadControl.Start();
}
public void thread() {
this.listener.Start();
Console.WriteLine("Awaiting connection...");
this.local = this.listener.AcceptTcpClient().GetStream();
Console.WriteLine("Tunnel connected!");
Console.WriteLine("Starting threads...");
this.threadLocalSide = new Thread(new ThreadStart(this.localSide));
this.threadLocalSide.Start();
this.threadRemoteSide = new Thread(new ThreadStart(this.remoteSide));
this.threadRemoteSide.Start();
}
public void localSide() {
byte[] buffer = new byte[2048];
try {
while(true) {
while(!this.local.DataAvailable);
this.local.Read(buffer, 0, buffer.Length);
this.remote.Write(buffer, 0, buffer.Length);
}
} catch {
this.threadRemoteSide.Abort();
}
}
public void remoteSide() {
byte[] buffer = new byte[2048];
try {
while(true) {
while(!this.remote.DataAvailable);
this.remote.Read(buffer, 0, buffer.Length);
this.local.Write(buffer, 0, buffer.Length);
}
} catch {
this.threadLocalSide.Abort();
}
}
}
Stream.Read() method returns number of bytes actually received, but you are always sending the whole buffer.
Try to copy data from one stream to another this way:
static void CopyStream(Stream from, Stream to)
{
var buffer = new byte[2048];
while(true)
{
int bytesRead = from.Read(buffer, 0, buffer.Length);
if(bytesRead == 0) break; // 0 means end of stream
to.Write(buffer, 0, bytesRead);
}
}
localSide() { CopyStream(local, remote); }
remoteSide() { CopyStream(remote, local); }
Related
Now I know that this question has been asked a lot but I really just don't get how to do it. I tried this but the file don't get complete I just receive just a bit of the file and the rest is just NULL here is my code in client part I first send a message to the server that contain the file size like this :
// here I send the a upload request with the size of the file that I want to send
byte[] data = Encoding.Unicode.GetBytes("uploadreq~"+new FileInfo(ofg.FileName).Length);
// here is the socket client
target.Send(data);
Then on the server side :
if (cmd.Contains(update.update_request))
{
// here I set an int var to the file size
update.update_size = int.Parse(cmd.Split('~')[1]);
// here I setup the a new byte array with the given file size
update.update_received = new byte[update.update_size];
// then I send a upload confirm command
Connection.sendCommand(Commands.update_confirme);
update.isupdate = true;
}
Again on the client side when the confirmation has been received :
if (cmd.StartsWith("updateConfirm"))
{
// reading all the bytes of the file and sending them
byte[] datatosend = File.ReadAllBytes("the file path");
Connection.send_bytes(datatosend);
}
Finally on the client side :
private void receiveInfo()
{
byte[] buffer = new byte[999999];
int received = 0;
try
{
received = Connection.clientSocket.Receive(buffer);
}
catch (SocketException)
{
Connection.clientSocket.Close();
Connection.clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Connection.makeConnection();
}
if (received == 0)
return;
byte[] data = new byte[received];
Array.Copy(buffer, data, received);
if (update.isupdate == true)
{
// this calls a method that process the data received
update.process_update(data);
}
}
public static void process_update(byte[] data)
{
int writeSize = 0;
Buffer.BlockCopy(data, 0, update_received, writeSize, data.Length);
writeSize += data.Length;
if (update_received.Length == update_size)
{
using (FileStream fs = File.Create("the path to where the file shloud go"))
{
byte[] info = update_received;
fs.Write(info, 0, info.Length);
}
Array.Clear(update_received, 0, update_received.Length);
isupdate = false;
}
}
As I was writing this question I changed the buffer size in the receive info method and that seems to change stuff a bit but still, the file won't arrive fully..
Try this for the client:
private void SendFile(String FileName,String IPAddress,int Port )
{
System.Net.Sockets.TcpClient TcpClient = new System.Net.Sockets.TcpClient(IPAddress, Port);
System.Net.Sockets.NetworkStream NetworkStream = TcpClient.GetStream();
System.IO.Stream FileStream = System.IO.File.OpenRead(FileName);
byte[] FileBuffer = new byte[FileStream.Length];
FileStream.Read(FileBuffer, 0, (int)FileStream.Length);
NetworkStream.Write(FileBuffer, 0, FileBuffer.GetLength(0));
NetworkStream.Close();
}
and this is the code for the server:
private void ReceiveFile(String FilePath, int Port)
{
System.Threading.Thread WorkerThread = new System.Threading.Thread(() =>
{
System.Net.Sockets.TcpListener TcpListener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Any, 60000);
TcpListener.Start();
System.Net.Sockets.Socket HandlerSocket = TcpListener.AcceptSocket();
System.Net.Sockets.NetworkStream NetworkStream = new System.Net.Sockets.NetworkStream(HandlerSocket);
int BlockSize = 1024;
int DataRead = 0;
Byte[] DataByte = new Byte[BlockSize];
lock (this)
{
System.IO.Stream FileStream = System.IO.File.OpenWrite(FilePath);
while (true)
{
DataRead = NetworkStream.Read(DataByte, 0, BlockSize);
FileStream.Write(DataByte, 0, DataRead);
if (DataRead == 0)
{
break;
}
}
FileStream.Close();
}
});
WorkerThread.Start();
}
This will only transfer one file.
Server(GUI).png
Server ( GUI )
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace Server_ProfiChat
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
static readonly object _lock = new object();
static readonly Dictionary<int, TcpClient> list_clients = new Dictionary<int, TcpClient>();
public static void handle_clients(object o)
{
int id = (int)o;
TcpClient client;
lock (_lock) client = list_clients[id];
while (true)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int byte_count = stream.Read(buffer, 0, buffer.Length);
if (byte_count == 0)
{
break;
}
string data = Encoding.ASCII.GetString(buffer, 0, byte_count);
broadcast(data);
Console.WriteLine(data);
//var chatline = txtChat.Text;
Form1 formObj = new Form1();
formObj.txtChat.Text += data;
}
lock (_lock) list_clients.Remove(id);
client.Client.Shutdown(SocketShutdown.Both);
client.Close();
}
public static void broadcast(string data)
{
byte[] buffer = Encoding.ASCII.GetBytes(data + Environment.NewLine);
lock (_lock)
{
foreach (TcpClient c in list_clients.Values)
{
NetworkStream stream = c.GetStream();
stream.Write(buffer, 0, buffer.Length);
}
}
}
private void btnConnect_Click(object sender, EventArgs e)
{
int count = 1;
string serverIP = txtServerIP.Text;
int serverPort = Int32.Parse(txtServerPort.Text);
TcpListener ServerSocket = new TcpListener(IPAddress.Parse(serverIP), serverPort);
ServerSocket.Start();
while (true)
{
TcpClient client = ServerSocket.AcceptTcpClient();
lock (_lock) list_clients.Add(count, client);
Console.WriteLine("Someone connected!!");
Thread t = new Thread(handle_clients);
t.Start(count);
count++;
}
}
}
}
Server ( Console )
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
namespace testServ
{
class Program
{
static readonly object _lock = new object();
static readonly Dictionary<int, TcpClient> list_clients = new Dictionary<int, TcpClient>();
static void Main(string[] args)
{
int count = 1;
TcpListener ServerSocket = new TcpListener(IPAddress.Parse("192.168.0.169"), 123);
ServerSocket.Start();
while (true)
{
TcpClient client = ServerSocket.AcceptTcpClient();
lock (_lock) list_clients.Add(count, client);
Console.WriteLine("Someone connected!!");
Thread t = new Thread(handle_clients);
t.Start(count);
count++;
}
}
public static void handle_clients(object o)
{
int id = (int)o;
TcpClient client;
lock (_lock) client = list_clients[id];
while (true)
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
int byte_count = stream.Read(buffer, 0, buffer.Length);
if (byte_count == 0)
{
break;
}
string data = Encoding.ASCII.GetString(buffer, 0, byte_count);
broadcast(data);
Console.WriteLine(data);
}
lock (_lock) list_clients.Remove(id);
client.Client.Shutdown(SocketShutdown.Both);
client.Close();
}
public static void broadcast(string data)
{
byte[] buffer = Encoding.ASCII.GetBytes(data + Environment.NewLine);
lock (_lock)
{
foreach (TcpClient c in list_clients.Values)
{
NetworkStream stream = c.GetStream();
stream.Write(buffer, 0, buffer.Length);
}
}
}
}
}
Client (console)
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace test_Clie
{
class Program
{
static void Main(string[] args)
{
IPAddress ip = IPAddress.Parse("192.168.0.166");
int port = 123;
TcpClient client = new TcpClient();
client.Connect(ip, port);
Console.WriteLine("client connected!!");
NetworkStream ns = client.GetStream();
Thread thread = new Thread(o => ReceiveData((TcpClient)o));
thread.Start(client);
string s;
while (!string.IsNullOrEmpty((s = Console.ReadLine())))
{
byte[] buffer = Encoding.ASCII.GetBytes(s);
ns.Write(buffer, 0, buffer.Length);
}
client.Client.Shutdown(SocketShutdown.Send);
thread.Join();
ns.Close();
client.Close();
Console.WriteLine("disconnect from server!!");
Console.ReadKey();
}
static void ReceiveData(TcpClient client)
{
NetworkStream ns = client.GetStream();
byte[] receivedBytes = new byte[1024];
int byte_count;
while ((byte_count = ns.Read(receivedBytes, 0, receivedBytes.Length)) > 0)
{
Console.Write(Encoding.ASCII.GetString(receivedBytes, 0, byte_count));
Console.Write(ns);
}
}
}
}
Im a beginner but to me it seems to have to do with the threading?
Also because it freezes I cant see if the rest is working, any ideas on how to improve workflow, as in searching for the exact point the mistakes are? Im wasting alot of time searching anything because I dont know how to properly test, debug and pinpoint the mistakes I made.
Appreciate any help =))
Streams generally do exactly what you ask of them. If you ask for 10 bytes, then a Read will not return until there are 10 bytes to return. What stream.Read will not do, is return fewer bytes just because that is how many have been received.
This code will block until 1024 bytes have been read.
byte[] buffer = new byte[1024];
int byte_count = stream.Read(buffer, 0, buffer.Length);
if (byte_count == 0)
{
break;
}
Is that what you are expecting?
Generally, byte based protocols have a header, which contains how many bytes are in the payload.
Imagine the protocol below:
<stx><soh><len><eoh><payload><etx>
stx = Start of transmission
soh = Start of header
len = length of payload
eoh = End of header
etx = End of transmission
To read a complete packet you MUST read 4 bytes, and then you MUST read enough bytes to complete the payload, then the last byte should be ETX.
byte[5] header;
stream.Read(buffer, 0, 4);
int payloadLength = header[2];
byte[] payload = new byte[payloadLength];
stream.Read(payload, 0, payloadLength);
stream.Read() == ETX;
The TcpListener.AcceptTcpClient method is blocking. You could try using its asynchronous counterpart instead, combined with async/await:
private async void btnConnect_Click(object sender, EventArgs e)
{
/* ... */
TcpClient client = await ServerSocket.AcceptTcpClientAsync();
/* ... */
}
I am attempting to stream audio using Naudio over a TCP connection. The problem is the audio sounds choppy. I believe this is because I am getting an exception saying the buffer is full when I try to add samples to the bufferedwaveprovider.
I have tried increasing the buffer size however the result remains unchanged.
-----CLIENT CODE-----
public TcpClient client;
public WaveOut waveplayer = new WaveOut();
public BufferedWaveProvider bwp = new BufferedWaveProvider(new WaveFormat(8000, 16, 1));
public byte[] buffer = new byte[1024 * 16];
public Form1()
{
bwp.BufferLength = 1024 * 16;
waveplayer.Init(bwp);
waveplayer.Play();
}
public void audio()
{
try
{
client = new TcpClient(textBox1.Text.ToString(), 8001);
NetworkStream ns = client.GetStream();
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
while (true)
{
try
{
ns.Read(buffer, 0, buffer.Length);
bwp.AddSamples(buffer, 0, buffer.Length);
}
catch(Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
-----SERVER CODE------
public NAudio.Wave.WaveInEvent sourcestream = null;
public TcpListener listener = new TcpListener(IPAddress.Any, 8001);
public TcpClient client;
public NetworkStream ns;
public Form1()
{
InitializeComponent();
sourcestream = new NAudio.Wave.WaveInEvent();
sourcestream.DeviceNumber = 0;
sourcestream.WaveFormat = new NAudio.Wave.WaveFormat(8000, 16, 1);
sourcestream.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(audioDataAvailable);
sourcestream.StartRecording();
}
public void acceptclients()
{
listener = new TcpListener(IPAddress.Any, 8001);
listener.Start();
client = listener.AcceptTcpClient();
ns = client.GetStream();
}
void audioDataAvailable(object sender, NAudio.Wave.WaveInEventArgs e)
{
try
{
if (client.Connected)
{
ns.Write(e.Buffer, 0, e.Buffer.Length);
ns.Flush();
}
}
catch(Exception ex)
{
Here is the exact error I recieve
"System.InvalidOperationException: Buffer full at NAudio.Wave.BufferedWaveProvider.AddSamples(Byte[] buffer, Int32 offset, Int32 count
If you're getting a buffer full exception, that means audio is arriving faster than you are playing it. You either need a larger buffer size, or to throttle audio before downloading it.
You also should use e.BytesRecorded, not e.Buffer.Length on the server side. That might also account for the issue you are seeing.
Another bug is that you should examine the number of bytes read from ns.Read and use that number when calling bwp.AddSamples
I currently have an application that uses the .NET C# .SendFile() method.
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(endPoint);
client.SendFile(filePath);
And I have a very simple receive file solution that goes something like this. Takes the the bits read and append it to a file basically:
ReadCallback(IAsyncResult ar) {
StateObject tempState = (StateObject)ar.AsyncState;
Socket handler = tempState.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead <= 0)
{
return;
}
BinaryWriter writer = null;
try
{
writer = new BinaryWriter(File.Open(receivePath, FileMode.Append));
writer.Write(tempState.buffer, 0, bytesRead);
}
catch (Exception error)
{
Console.WriteLine(error.Message);
Thread.Sleep(30);
}
finally
{
if (writer != null)
{
writer.Close();
}
// this method starts a new AsyncCallback(ReadCallback)
// and this method is ReadCallback so it works as a recursive method
handler.BeginReceive(tempState.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), tempState);
}
Now I want to add additonal info like: filename, file size, md5 hash etc to the transfer.
So I wonder how if I send this for example:
byte[] preBuffer = { 1, 2, 3, 4 };
byte[] postBuffer ={ 5, 6, 7, 8 };
.SendFile(filePath, preBuffer, postBuffer, TransmitFileOptions.None)
How can I receive this information and separate it from the binary file that I write? How should I change my receive
Unless you want to append data to the file received on the other end, don't use postBuffer. i.e. if you want to send meta information about the file, only use preBuffer. In this case I recommend serializing the data to an in memory buffer then sending that buffer as the preBuffer and deserializing it on the other end. For example:
byte[] preBuffer;
using (var memoryStream = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(memoryStream))
{
writer.Write(filePath);
writer.Write(fileLength);
writer.Write(md5Hash);
}
preBuffer = memoryStream.ToArray();
}
mySocket.SendFile(filePath, preBuffer, null,
TransmitFileOptions.UseDefaultWorkerThread);
Then, when you want to read the meta information and file, read the pre-data first, and the remaining will be the file. For example:
private void ReadCallback(IAsyncResult ar)
{
StateObject tempState = (StateObject) ar.AsyncState;
Socket handler = tempState.workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead <= 0)
{
return;
}
using (var memoryStream = new MemoryStream(tempState.buffer))
{
using (BinaryReader reader = new BinaryReader(memoryStream))
{
var filename = reader.ReadString();
var length = reader.ReadInt32();
var md5Hash = reader.ReadString();
var fileData = new byte[memoryStream.Length - memoryStream.Position];
reader.Read(fileData, 0, fileData.Length);
try
{
using (var writer = new BinaryWriter(
File.Open(receivePath, FileMode.Append)))
{
writer.Write(tempState.buffer, 0, bytesRead);
}
}
catch (Exception error)
{
Console.WriteLine(error.Message);
Thread.Sleep(30);
}
finally
{
// this method starts a new AsyncCallback(ReadCallback)
// and this method is ReadCallback so it works as a recursive method
handler.BeginReceive(tempState.buffer,
0,
StateObject.BufferSize,
0,
new AsyncCallback(ReadCallback),
tempState);
}
}
}
}
Receiving bytes here in this code(server)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Drawing;
namespace ByteLengthReading
{
class Program
{
static void Main(string[] args)
{
StartServer();
}
private static TcpListener _listener;
public static void StartServer()
{
IPAddress localIPAddress = IPAddress.Parse("119.43.29.182");
IPEndPoint ipLocal = new IPEndPoint(localIPAddress, 8001);
_listener = new TcpListener(ipLocal);
_listener.Start();
WaitForClientConnect();
}
private static void WaitForClientConnect()
{
object obj = new object();
_listener.BeginAcceptTcpClient(new System.AsyncCallback(OnClientConnect), obj);
Console.In.ReadLine();
}
private static void OnClientConnect(IAsyncResult asyn)
{
try
{
TcpClient clientSocket = default(TcpClient);
clientSocket = _listener.EndAcceptTcpClient(asyn);
HandleClientRequest clientReq = new HandleClientRequest(clientSocket);
clientReq.StartClient();
}
catch (Exception ex)
{
throw ex;
}
WaitForClientConnect();
}
public class HandleClientRequest
{
TcpClient _clientSocket;
NetworkStream _networkStream = null;
public HandleClientRequest(TcpClient clientConnected)
{
this._clientSocket = clientConnected;
}
public void StartClient()
{
_networkStream = _clientSocket.GetStream();
WaitForRequest();
}
public void WaitForRequest()
{
byte[] buffer = new byte[_clientSocket.ReceiveBufferSize];
_networkStream.BeginRead(buffer, 0, buffer.Length, ReadCallback, buffer);
}
private void ReadCallback(IAsyncResult result)
{
NetworkStream networkStream = _clientSocket.GetStream();
byte[] buffer = new byte[16384];
int read = -1;
int totRead = 0;
using (FileStream fileStream = new FileStream(#"C:\Foo" + Guid.NewGuid().ToString("N") + ".txt", FileMode.Create))
{
while ((read = networkStream.Read(buffer, 0, buffer.Length)) > 0)
{
totRead += read;
fileStream.Write(buffer, 0, read);
Console.WriteLine("Total Read" + totRead);
//fileStream.Write(buffer, 0, totRead);
//fileStream.Close();
}
fileStream.Close();
}
}
}
}
Sending bytes (Client), Sending bytes of length 4047810. But the abover server code is recieving only 4039618 bytes. Please help someone. Don't know y? At the time of reading last set of data it is coming out of the while loop. Please test this code and tell me where the problem lies.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Drawing;
using System.Threading;
namespace ByteLengthSending
{
class Program
{
static void Main(string[] args)
{
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.Connect(IPAddress.Parse("119.43.29.182"), 8001);
//IPAddress ipAd = IPAddress.Parse("119.43.29.182");
//TcpClient client = new TcpClient(ipAd.ToString(), 8001);
//NetworkStream stream = client.GetStream();
int totread = 0;
byte[] longBuffer = new byte[3824726];
byte[] buffer = new byte[4096];
using (var fileStream = File.OpenRead("C:/Foo.txt"))
{
while (true)
{
int read = fileStream.Read(buffer, 0, buffer.Length);
totread += read;
if (read <= 0)
{
break;
}
for (int sendBytes = 0; sendBytes < read; sendBytes += client.Send(buffer, sendBytes, read - sendBytes, SocketFlags.None))
{
}
}
}
client.Close();
Console.WriteLine("Total Read" + totread);
Console.In.ReadLine();
}
}
}
Here is a sample which uses my library Griffin.Framework to transmit a file (Apache license).
All you need to do is to install the nuget package "griffin.framework" and then create a console application and replace Program class with the following:
class Program
{
static void Main(string[] args)
{
var server = new ChannelTcpListener();
server.MessageReceived = OnServerReceivedMessage;
server.Start(IPAddress.Any, 0);
var client = new ChannelTcpClient<object>(new MicroMessageEncoder(new DataContractMessageSerializer()),
new MicroMessageDecoder(new DataContractMessageSerializer()));
client.ConnectAsync(IPAddress.Loopback, server.LocalPort).Wait();
client.SendAsync(new FileStream("TextSample.txt", FileMode.Open)).Wait();
Console.ReadLine();
}
private static void OnServerReceivedMessage(ITcpChannel channel, object message)
{
var file = (Stream) message;
var reader = new StreamReader(file);
var fileContents = reader.ReadToEnd();
Console.WriteLine(fileContents);
}
}
The library can send/receive any type of stream of any size (as long as the size is known). The client will automatically create a MemoryStream or FileStream depending on the stream size.