Reputation: 1355
I have multiple servers created.Each one has to send some data to its own client. I am using TCP/IP protocol. To prevent any data loss due to client getting disconnected, I am using a text file as a buffer. So in the program , there is a thread for each server which keeps on checking if the client is connected or not. If it is connected then it reads from the buffer and sends it to client. whenever some new data has to be send to client , I am first checking if client is connected.If client isn't connected then I am writing data to the same buffer text file. The problem I am facing is that I am unable to write to the file while thread is reading from it.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace WindowsFormsApplication1
{
public class TcpIp
{
public int machinePort;
public static int port1 = 1024;
public static int count = 0;
public string bufferName;
FileStream buffer;
Socket client;
public IPAddress localIp;
public TcpListener sender;
StreamReader reader ;
FileStream iStream;
//this.get
public TcpIp(string id)
{
this.machinePort = port1 + count;
while (!isAvailable(this.machinePort))
{
count++;
this.machinePort = port1 + count;
}
this.bufferName = WindowsFormsApplication1.Program.path + "machine_" + id + ".txt";
buffer = new FileStream(this.bufferName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
localIp = IPAddress.Parse(WindowsFormsApplication1.Program.ip);
sender = new TcpListener(localIp, this.machinePort);
// this.oStream = new FileStream(this.bufferName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
//this.iStream = new FileStream(this.bufferName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
// reader = new StreamReader(this.iStream);
}
bool isAvailable(int port)
{
bool isAvailable = true;
IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();
foreach (TcpConnectionInformation tcpi in tcpConnInfoArray)
{
if (tcpi.LocalEndPoint.Port == port)
{
isAvailable = false;
break;
}
}
return isAvailable;
}
public void createServer()
{
this.sender.Start();
string line;
reader = new StreamReader(buffer);
//client = sender.AcceptSocket();
while (true)
{
line = reader.ReadLine();
if (!connected())
{
client = sender.AcceptSocket();
}
while (reader.EndOfStream && line != null)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(line);
client.Send(bytes, 0, bytes.Length, SocketFlags.None);
line = reader.ReadLine();
}
// iStream.Flush();
Thread.Sleep(3000);
//reader = new StreamReader(iStream);
}
}
public void writeToClient(string data)
{
if (connected())
{
//send data to client
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
//System.Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
this.client.Send(bytes, 0, bytes.Length, SocketFlags.None);
}
else
{
//write to file
while (true)
{
try
{
StreamWriter sw = File.AppendText(this.bufferName);
sw.WriteLine(data);
sw.Close();
break;
}
catch (Exception ex)
{
Console.WriteLine("WaitForFile {0} failed to get an exclusive lock: "+ex.Message );
// Wait for the lock to be released
System.Threading.Thread.Sleep(500);
}
}
}
}
bool connected()
{
if (client == null)
return false;
else
return client.Connected;
}
}
}
Any enlightenment would be appreciated. Thank you :)
Upvotes: 0
Views: 1705
Reputation: 11216
The actual problem is that you mix up access to the file.
You open a stream by buffer = new FileStream(this.bufferName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
in order to create a StreamReader
from the opened stream for later reading by reader = new StreamReader(buffer);
.
OTOH, you want get a StreamWriter
for writing to the file by StreamWriter sw = File.AppendText(this.bufferName);
. This tries to open the file again which fails because of mismatching file sharing mode.
So you need to access the file for writing and reading via the very same "handle" (here FileStream). Furthermore, don't forget to serialize access by some locking mechanism in order to make it thread-safe. Otherwise, you'll get corrupted data. You'll probably need to maintain the read/write pointer (Stream.Position).
Upvotes: 2
Reputation: 104
You can't read and write from the same text file at the same time.
If you really want to use text files, why not using 2 of them: One to read from and one to write into. Once the read file is empty -> the read file is your new write file and vice versa.
Upvotes: 1
Reputation: 3360
You will have to create Threads like this
Thread thread = new Thread(yourMethod());
When you have multiple threads then what you need is ReaderWriterLockSlim. Here is the link.
It lets you to read a file by multiple threads but write the file with one Thread. I think this will solve your problem.
Upvotes: 0
Reputation: 5830
That sounds logic, it's not possible for 2 threads to access a file as the same time, because the file is in use.
Imagine that it's even possible, you will have some very strange behaviour.
Why are you not using a lock() to make sure that only a single thread can access the file at a given time?
And with async programming, you don't need to wait until the lock is released before continueing your program.
Upvotes: 0