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
Related
I wrote this method using UDP socket with ReceiveTimeout = 1000. And sometimes it loses second packet:
private static byte[] ReceivePlainData(Socket socket) {
var recievedData = new List<byte>();
var buffer = new byte[1024];
do {
int recievedAmount;
while (true) {
try {
recievedAmount = socket.Receive(buffer);
}
catch (SocketException) {
continue;
}
break;
}
recievedData.AddRange(buffer.Take(recievedAmount));
} while (socket.Available > 0);
return recievedData.ToArray();
}
Socket initialization:
using (var socket = new Socket(SocketType.Dgram, ProtocolType.Udp) {
ReceiveTimeout = 1000
})
What do i need to change to make this method work correctly?
Socket.Available only tells you if data has been received and available for immediate reading. It doesn't tell you if the stream is complete or not. The sender may still be in the process of generating or transmitting more data. If you intend to consume the entire response and are relying on the socket to be closed by the sender when there is no more data, continue reading from the socket until the number of bytes returned is 0 (indicating the socket was closed normally) or an exception occurs (indicating abnormal termination).
MemoryStream stream = new MemoryStream();
int received;
var buffer = new byte[8096];
do {
received = socket.Receive(buffer);
stream.Write(buffer, 0, received);
} while (received > 0);
// Use the stream
// byte[] bytes = stream.ToArray();
Set your socket's ReceiveTimeout to the maximum amount of time you're willing to wait between bytes before aborting.
i have an issue where im not receiving any bytes from the connected TcpClient!
Server: (i attempt to add the received message into a listbox, but nothing comes out.
//tl is a TcpListener
//I set this up in a 500 milisecond loop. No worries about that.
if (tl.Pending())
{
TcpClient tcp = tl.AcceptTcpClient();
var s = tcp.GetStream();
int bytesRead = s.Read(new byte[tcp.ReceiveBufferSize], 0,
tcp.ReceiveBufferSize);
string dataReceived = Encoding.ASCII.GetString(new
byte[tcp.ReceiveBufferSize], 0, bytesRead);
listBox1.Items.Add(dataReceived);
tcp.Close();
oac++; //Overall connection count
label3.Text = "Overall Connections: " + oac; //logging connections
}
Client:
void Send(){
TcpClient c = new TcpClient(Im not including my ip., 4444);
System.IO.StreamWriter w = new System.IO.StreamWriter(c.GetStream());
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes($"Username: \"
{textBox1.Text}\" | Password: \"{textBox2.Text}\"");
NetworkStream nwStream = c.GetStream();
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
nwStream.Flush();
}
I The connection works, but theres some issues receiving data. it just comes out blank
On serverside, your problem is that you renew toy byte array new byte[tcp.ReceiveBufferSize]. You could also do something like this:
using( var inputStream = new MemoryStream() )
{
tcp.GetStream().CopyTo(inputStream);
Encoding.ASCII.GetString(inputStream.ToArray());
listBox1.Items.Add(dataReceived);
...
}
Remember using on all IDisposable's, else you will run out of resources.
I'm trying to send files after sending their's information via TCP connection. At the end of receiver host, the data packets are received. I used Wireshark to confirm it. However the data couldn't be received in NetworkStream.
public class FileTransporter
{
public void ReceiveFiles(IPAddress IP, int port)
{
TcpListener tcpListener = new TcpListener(IP, port);
tcpListener.Start();
using (TcpClient tcpClient = tcpListener.AcceptTcpClient())
{
if (tcpClient.Connected)
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
int pointer = 0;
byte[] fileNameLengthBytes = new byte[sizeof(int)];
networkStream.Read(fileNameLengthBytes, pointer, fileNameLengthBytes.Length);
int fileNameLength = BitConverter.ToInt32(fileNameLengthBytes, pointer);
// code to read fileName and it's size
networkStream.Close();
}
}
tcpClient.Close();
}
tcpListener.Stop();
}
public void SendFiles(IPAddress IP, int port, string[] paths)
{
for(int i=0; i<paths.Length; i++)
{
FilePackage filePackage = new FilePackage(paths[i]);
byte[] infoBytes = filePackage.EncodeInfoToByte();
using (TcpClient tcpClient = new TcpClient())
{
tcpClient.Connect(IP, port);
using (NetworkStream networkStream = tcpClient.GetStream())
{
networkStream.Write(infoBytes, 0, infoBytes.Length);
networkStream.Close();
}
tcpClient.Close();
}
}
}
}
public class FilePackage
{
public FilePackage(string fileName)
{
this.Info = new FileInfo(fileName);
}
public byte[] EncodeInfoToByte()
{
List<byte> infoByte = new List<byte>();
infoByte.AddRange(BitConverter.GetBytes(this.Info.Name.Length));
infoByte.AddRange(Encoding.UTF8.GetBytes(this.Info.Name));
infoByte.AddRange(BitConverter.GetBytes(this.Info.Length));
return infoByte.ToArray();
}
I am going to Assume that ReadFiles is invoked somehow / somewhere else.
I have a very similar implementation, however my "Read" of the stream is much smaller:
// Read the first batch of the TcpServer response bytes.
var bytes = new byte[512];
// Loop to receive all the data sent by the Server.
var sb = new StringBuilder();
do
{
var i = stream.Read(bytes, 0, bytes.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(bytes, 0, i));
} while (stream.DataAvailable);
Perhaps using "stream.DataAvailable" will allow you to test for data existence and that way you can make sure that you only process when data is available.
Also I don't see where "pointer" is being initialized in your code.
If it is not set to 0 each time, you will not be processing from the beginning of the data packet.
Your problem is probably being caused by the nagle algorithm, prevent small amounts of data being sent to reduce congestion. You could try disabling it by setting the following properties on your tcpClient
tcpClient.NoDelay = true;
tcpClient.Client.NoDelay = true
This is the the function for receiving the values from the client. But the problem is I'm receiving it only once. No matter how many time I send some data, the label on the MainWindows is being changed only once.
What am I doing wrong here?
private void HandleClientComm(object client)
{
tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
break;
}
if (bytesRead == 0)
{
break;
}
if (String.IsNullOrWhiteSpace(data))
{
ASCIIEncoding encoder = new ASCIIEncoding();
data = encoder.GetString(message, 0, bytesRead);
MainWindow.Change(data);
tcpClient.Close();
}
tcpClient.Close();
}
On the client side I have this following example:
try
{
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("127.0.0.1", 8001);
Console.WriteLine("Connected");
while (true)
{
Console.Write("Enter the string to be transmitted : ");
String str = Console.ReadLine();
Stream stm = tcpclnt.GetStream();
ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);
Console.WriteLine("Transmitting.....");
stm.Write(ba, 0, ba.Length);
stm.Flush();
Console.WriteLine("Sent.....");
}
tcpclnt.Close();
Console.Read();
}
catch (Exception e)
{
Console.WriteLine("Error..... " + e.StackTrace);
}
Just a fast example, so when I enter the string name, the value changes the first time but when I enter a second time or if i exit the client program and reenter nothing changes, the value of the label content is equal to the first sent value.
You must use asynchronous server socket... see this sample from MSDN
Link
It looks like after the first successful read you are calling:
tcpClient.Close();
This will prevent any future reads from being successful.
If you would like avoid these sort of socket problems have you considered using the network library instead? I'm a developer for the library networkComms.net and you can find a short example or what I mean here.
If you use clientStream.Read() after tcpClient.Close(), it will throw an exception, which will run your catch block and break you out of the loop. (And you are calling tcpClient.Close() in two places within the loop so just getting rid of just one will not help. Both of those are wrong.)
tcpClient.Close() should not be called unless you're completely done with the connection (moreover it should only be called once.)
Also,
"You must close the NetworkStream when you are through sending and receiving data. Closing TcpClient does not release the NetworkStream."
So make sure you close the TcpClient and the NetworkStream, but only when you're completely finished processing in the loop.
I'm creating a socket server that needs to continuously listen for incoming messages from the connected clients. Those messages will be sent in a byte[] array. I had the server working great with a StreamReader but StreamReader only works with textual represenations of the data being sent...not byte[] arrays.
Here's what I had:
StreamReader reader = new StreamReader(Client.GetStream());
string line = "";
while (true)
{
line = reader.ReadLine();
if (!string.IsNullOrEmpty(line))
{
parentForm.ApplyText(line + "\r\n");
SocketServer.SendBroadcast(line);
}
}
I need to now convert that into a raw stream somehow that will convert the stream contents into a byte[] array but I can't seem to get a handle on it.
I tried this:
while (true)
{
var bytes = default(byte[]);
using (var memstream = new MemoryStream())
{
var buffer = new byte[512];
var bytesRead = default(int);
while ((bytesRead = reader.BaseStream.Read(buffer, 0, buffer.Length)) > 0)
memstream.Write(buffer, 0, bytesRead);
bytes = memstream.ToArray();
}
//parentForm.ApplyText(bytes.Length + "\r\n");
}
but as you might guess, the while(true) loop doesn't quite work how I need it to. Can anyone help me with some code adjustment to make this work as I need it to. It needs to continuously listen for incoming messages, then when a message is received, it needs to do something with that message (the byte[] array) then go back to listening again.
TIA
I guess "listening continuously" is not task of reader its a task of listener. I ran into same problem when i was writing server using TcpListener. I am not sure what you want to do but i am posting solution for your "listening continuous" and reading into byte[] problem. I guess this code might help you:
TcpListener t = new TcpListener(IPAddress.Loopback, _port);
t.Start();
Console.WriteLine("Server is started and waiting for client\n\n");
byte[] buff = new byte[255];
NetworkStream stream;
TcpClient client;
while(true)
{
client = t.AcceptTcpClient();
if (!client.Connected)
return;
stream = client.GetStream();
while ((stream.Read(buff, 0, buff.Length)) != 0)
{
break;
}
if (0 != buff.Length)
break;
}
There's no need to convert anything. GetStream() returns a NetworkStream. See the sample Microsoft includes in the NetworkStream.Read Method. All you have to do is replace the myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead)); line with an appropriate storage mechanism.