How do I make the client read from the stream (for messages of other clients sent to the stream) at the same time as being able to write to them?
I tried creating different threads on the client side (did I even do it right?), however I can still only write to the server, without any response. This is what I'm getting right now:
(Server-Client-Client)
Client:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace Klientas
{
class Klientas
{
public static void Write()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
string str = Console.ReadLine();
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
writer.Write(str);
}
}
public static void Read()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
Console.WriteLine(reader.ReadString());
}
}
static void Main(string[] args){
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
}
}
}
Server:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;
namespace MultiServeris
{
class Multiserveris
{
static void Main(string[] args)
{
TcpListener ServerSocket = new TcpListener(1000);
ServerSocket.Start();
Console.WriteLine("Server started.");
while (true)
{
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
handleClient client = new handleClient();
client.startClient(clientSocket);
}
}
}
public class handleClient
{
TcpClient clientSocket;
public void startClient(TcpClient inClientSocket)
{
this.clientSocket = inClientSocket;
Thread ctThread = new Thread(Chat);
ctThread.Start();
}
private void Chat()
{
while (true)
{
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
Console.WriteLine(reader.ReadString());
}
}
}
}
It seems like you do not have any code on the server to send messages to the client. You need to maintain a list of connected clients and make the server send a message to the eligible clients when it receives a message. Also do not make the client a console app. Unlike most projects for a chat client it is actually harder to do it as a console app.
To keep a list of clients you declare a list of TCP Clients like this
static List<TcpClient> clients = new List<TcpClient>();
Then when a client connects you add it to the list
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
clients.Add(clientSocket);
Then when you receive a message you send it to all clients
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
while(true)
{
string message = reader.ReadString();
foreach(var client in clients)
{
//send message to client
}
}
Now remember that in practice you should handle things like disconnects and adding and removing clients from the list should be thread safe (locks and all).
Good starting point for socket client-server communication: demo application and library. Support for reconnecting, sudden client disconnect catch, message broadcasting and many more.
Related
I have a simple TCP server written in C# with Visual Studio. Everything works fine unless ran as a Windows Service in which I get the 1053 error. I narrowed it down to the TCP listener parts because when I comment them out the service starts just fine.
Here is the code I keep in Service1.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.IO;
using System.Text.RegularExpressions;
namespace Vegas_Remote_Render_Server
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
var defaultwaittime = "20";
int defaultport = 1302;
TcpListener listener = new TcpListener(System.Net.IPAddress.Any, defaultport);
listener.Start();
while (true)
{
Console.WriteLine("Waiting for a connection.");
TcpClient client = listener.AcceptTcpClient();
Console.WriteLine("Client accepted.");
NetworkStream stream = client.GetStream();
StreamReader sr = new StreamReader(client.GetStream());
StreamWriter sw = new StreamWriter(client.GetStream());
try
{
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
int recv = 0;
foreach (byte b in buffer)
{
if (b != 0)
{
recv++;
}
}
string request = Encoding.UTF8.GetString(buffer, 0, recv);
string formattedrequest = Regex.Replace(request, #"^\s*$\n|\r", string.Empty, RegexOptions.Multiline).TrimEnd();
Console.WriteLine(request);
Console.WriteLine("request received");
sw.WriteLine("request received");
string[] splitrequest = request.Split('|');
var projectpath = "";
var parameternumber = 0;
var waittime = defaultwaittime;
foreach (string value in splitrequest)
{
parameternumber = parameternumber + 1;
if (parameternumber == 1)
{
projectpath = value;
}
if (parameternumber == 2)
{
waittime = value;
}
}
if (formattedrequest != "killrender")
{
Console.WriteLine(projectpath);
Console.WriteLine(waittime);
System.Diagnostics.Process.Start(#"render.bat", $"\"{projectpath}\" \"{waittime}\"");
sw.Flush();
}
if (formattedrequest == "killrender")
{
Console.WriteLine("killing instances");
System.Diagnostics.Process.Start(#"kill.bat");
}
}
catch (Exception e)
{
Console.WriteLine("Something went wrong.");
sw.WriteLine(e.ToString());
}
}
}
protected override void OnStop()
{
}
}
}
To simplify the script, here is only the TCP listener parts:
static void Main(string[] args)
{
TcpListener listener = new TcpListener(System.Net.IPAddress.Any, defaultport);
listener.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
NetworkStream stream = client.GetStream();
StreamReader sr = new StreamReader(client.GetStream());
StreamWriter sw = new StreamWriter(client.GetStream());
}
}
I've also tried many solutions in other posts about this issue with the service. This includes exporting the .exe from visual studio as Release instead of Debug, commenting the console.WriteLine commands that I used while debugging, and removing the while(true). My Visual Studio Project also uses the project template "Windows Service C#"
I have heard that you can make registry edits to give the service more time, but I would rather not use that solution.
The service works for the amount of time before it is deemed "unresponsive".
I am stuck trying to find what part of the TCP listener is doing this.
Thanks in advance, I'm still kind of a Newbie with C#
1053 error states:
The service did not respond to the start or control request in a timely fashion.
This error message is the cause of a timeout that occurs after a request was initiated to start a service but it did not respond in the time window.
Response in your case is exiting OnStart() method. You use while(true) which is in many cases quite error prone. I suspect your service just enters the loop and ever exits because AcceptTcpClient is !blocking! operantion:
AcceptTcpClient is a blocking method that returns a TcpClient that you can use to send and receive data. Use the Pending method to determine if connection requests are available in the incoming connection queue if you want to avoid blocking.
You need to move your routine of handling clients out of the OnStart method to do not block it, just move it to the separate thread.
Don't forget to Stop() TcpListener on service stop.
C# Form .Net 4.7
A client app should receive XML data from an external payment machine. The XML is automatically sent after each deposit. It's about receiving it at any time and displaying it in a TextBox.
Sending takes place via TCP
The machine opens the connection to the external system at the specified port
Sends the data
Waits for confirmation on the same connection if necessary
Closes the connection
Since the client has to be able to receive the data at any time, I thought about a listener. But I'm not sure if this approach is the right one. What I did here doesn't work. I wanted to test that with a localhost. There is sure to be a simple solution. But since I'm not a network specialist, I can't find it. Does anyone know how to do it best?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace TCPListener
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
TcpListener Listener = null;
Int32 port = 8080;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
private void button1_Click(object sender, EventArgs e)
{
Listener = new TcpListener(localAddr, port);
byte[] receiveBuffer = new byte[10025];
while (true)
{
int requestCount = 0;
Listener.Start();
MessageBox.Show(" >> Listener Started");
using (var tcpClient = Listener.AcceptTcpClient())
{
MessageBox.Show(" >> Accepted connection from client");
using (var networkStream = tcpClient.GetStream())
{
while (true)
{
try
{
requestCount = requestCount++;
var bytesRead = networkStream.Read(receiveBuffer, 0, (int)tcpClient.ReceiveBufferSize);
if (bytesRead == 0)
{
// Read returns 0 if the client closes the connection
break;
}
string dataFromClient = System.Text.Encoding.ASCII.GetString(receiveBuffer, 0, bytesRead);
XmlDocument xm = new XmlDocument();
xm.LoadXml(string.Format("<root>{0}</root>", dataFromClient));
XmlElement root = xm.DocumentElement;
string rootName = root.FirstChild.Name;
textBox1.Text = (rootName, dataFromClient);
}
catch (Exception ex)
{
MessageBox.Show("ReceivePortMessages: " + ex.ToString());
break;
}
}
}
MessageBox.Show(" >> stopped read loop");
}
Listener.Stop();
}
}
}
}
I'm having a hard time naming this question and solving my problem.
I'm challenging myself in creating a chat server with multiple chat clients.
I've created a server that instantiates a new ClientManager object upon receiving a new connection via TCP. I'm missing various bits of management currently so ignore that as I'm focusing on figuring this bit out first:
Once the connection is established the ClientManager creates a new background worker for receiving streams. What I'm having trouble is once the stream is read, how can I forward that information back to my main server class to distribute amounts to the other clients (chat clients).
Once again, I'm missing a few management options such as threading, semaphores, etc but that will come as I build it out.
Here is how my server established a connection:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;
namespace ChatServer
{
class Program
{
private IPAddress localAddr;
private int serverPort;
private BackgroundWorker bwListener;
private List<ClientManager> clients;
private TcpListener server;
static void Main(string[] args)
{
Program progInstance = new Program();
progInstance.clients = new List<ClientManager>();
progInstance.server = null;
try
{
// Set the TcpListener on port 13000.
progInstance.serverPort = 13000;
progInstance.localAddr = IPAddress.Parse("127.0.0.1");
// Start to listen
progInstance.bwListener = new BackgroundWorker();
progInstance.bwListener.WorkerSupportsCancellation = true;
progInstance.bwListener.DoWork += new DoWorkEventHandler(progInstance.ServerListen);
progInstance.bwListener.RunWorkerAsync();
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
finally
{
// Stop listening for new clients.
//progInstance.server.Stop();
}
Console.WriteLine("\nHit enter to continue...");
Console.Read();
}
private void ServerListen(object sender, DoWorkEventArgs e) {
this.server = new TcpListener(this.localAddr, this.serverPort);
this.server.Start();
// Enter the listening loop.
while (true)
{
Console.Write("Waiting for a connection... \n");
Socket client = server.AcceptSocket();
this.AcceptClientConnection(client);
}
}
private void AcceptClientConnection(Socket client) {
ClientManager newClient = new ClientManager(client);
this.clients.Add(newClient);
}
private void DisplayClientConnections() {
}
}
}
And this is the ClientManager
using System;
using System.Net.Sockets;
using System.ComponentModel;
namespace ChatServer
{
class ClientManager
{
NetworkStream stream;
private Socket socket;
private BackgroundWorker bwReceiver;
//data = null;
public ClientManager(Socket socket)
{
this.socket = socket;
this.stream = new NetworkStream(this.socket);
this.bwReceiver = new BackgroundWorker();
this.bwReceiver.DoWork += new DoWorkEventHandler(StartReceive);
this.bwReceiver.RunWorkerAsync();
}
private void StartReceive(object sender, DoWorkEventArgs e) {
String data = null;
Byte[] bytes = new Byte[256];
int i;
while (this.socket.Connected) {
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine("Received: {0}", data);
}
}
}
}
}
I was thinking that the server should somehow retrieve this information and then write it to the stream, or would I just do that would I just write back to the stream from the ClientManager and then have my actual clients listen and receive? I'm just super lost how to handle this portion of the chat server.
Sorry for asking about the 'same' thing over and over again. It's yet another edition of the chat. With a lot of searching around I've finally found out how to pass the client list (or at least I hope so) to the Chat function.
However I don't know if this is even supposed to work:
Everytime a client connects :
clients.Add(clientSocket);
var ctThread = new System.Threading.Thread(() => Chat(clients));
where the Chat function hopefully correctly receives the clients via
public void Chat(List<TcpClient> clients)
and then writes this out
foreach (var client in clients)
{
writer.Write(message);
}
With the client having 2 threads (not sure if they can actually read/write at the same time)
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
Did I pass the client list to the function properly? and can it actually correctly send the messages? Because right now the server is not responding to anything that I type on the client.
Full code:
Server
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.IO;
namespace MultiServeris
{
class Multiserveris
{
static void Main(string[] args)
{
TcpListener ServerSocket = new TcpListener(1000);
ServerSocket.Start();
List<TcpClient> clients = new List<TcpClient>();
Console.WriteLine("Server started.");
while (true)
{
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
handleClient client = new handleClient();
clients.Add(clientSocket);
client.startClient(clientSocket,clients);
}
}
}
public class handleClient
{
TcpClient clientSocket;
public void startClient(TcpClient inClientSocket, List<TcpClient> clients)
{
this.clientSocket = inClientSocket;
var ctThread = new System.Threading.Thread(() => Chat(clients));
}
public void Chat(List<TcpClient> clients)
{
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
while (true)
{
string message = reader.ReadString();
foreach (var client in clients)
{
writer.Write(message);
}
}
}
}
}
Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.IO;
using System.Threading;
namespace Klientas
{
class Klientas
{
public static void Write()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
string str = Console.ReadLine();
BinaryWriter writer = new BinaryWriter(clientSocket.GetStream());
writer.Write(str);
}
}
public static void Read()
{
while (true)
{
TcpClient clientSocket = new TcpClient("localhost", 1000);
BinaryReader reader = new BinaryReader(clientSocket.GetStream());
string message = reader.ReadString();
Console.WriteLine(message);
}
}
static void Main(string[] args){
Thread ctThread = new Thread(Write);
Thread ctThread2 = new Thread(Read);
ctThread2.Start();
ctThread.Start();
}
}
}
TCP is not design for broadcasting which is why you're having to loop through all your clients. A better approach would be to use a protocol that supports brodcast use the SignalR Framework or if you want baremetal access use UDP. Here's a great SignalR chat example.
https://dhavalupadhyaya.wordpress.com/tag/signalr-chat-example/
hello guyz i have made a bare bone program in C# that simply sends a message from server to client.
Now i have successfully tested both programs running on the same machines. However when i attempt to connect 2 different computers on different networks it sends me the unable to connect message.
Here the server code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Net.Sockets;
namespace chat_client_console
{
class Program
{
static TcpListener listener;
static void Main(string[] args)
{
/*
string name = Dns.GetHostName();
IPAddress[] address = Dns.GetHostAddresses(name);
foreach(IPAddress addr in address)
{
Console.WriteLine(addr);
}
Console.WriteLine(address[2].ToString());*/
Console.WriteLine("server");
listener = new TcpListener(IPAddress.Any, 2055);
listener.Start();
Socket soc = listener.AcceptSocket();
Console.WriteLine("Connection successful");
Stream s = new NetworkStream(soc);
StreamReader sr = new StreamReader(s);
StreamWriter sw = new StreamWriter(s);
sw.AutoFlush = true;
sw.WriteLine("A test message");
sw.WriteLine("\n");
Console.WriteLine("Test message delivered. Now ending the program");
/*
string name = Dns.GetHostName();
Console.WriteLine(name);
//IPHostEntry ip = Dns.GetHostEntry(name);
//Console.WriteLine(ip.AddressList[0].ToString());
IPAddress[] adr=Dns.GetHostAddresses(name);
foreach (IPAddress adress in adr)
{
Console.WriteLine(adress);
}
*/
Console.ReadLine();
}
}
}
and here the client code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net.Sockets;
namespace chat_client_console_client
{
class Program
{
static void Main(string[] args)
{
string host_ip_address;
Console.WriteLine("Enter server ip address");
host_ip_address=Console.ReadLine();
string display;
TcpClient client = new TcpClient(host_ip_address, 2055);
Stream s = client.GetStream();
Console.WriteLine("Connection successfully received");
StreamWriter sw = new StreamWriter(s);
StreamReader sr = new StreamReader(s);
sw.AutoFlush = true;
/*while (true)
{
display = sr.ReadLine();
if (display == "")
{
Console.WriteLine("breaking stream");
break;
}
}*/
display = sr.ReadLine();
Console.WriteLine(display);
Console.ReadLine();
}
}
}
now when i enter the 127.0.0.1 in the client program it successfully connects to the server and the message is received.
However when i enter my external ip address in the client program running on another computer i am unable to connect.
Suggestions are required in this matter.
Thank you.
You can use Wireshark in the client computer and look up any tcp packet to make sure the message sent to server.