I've been trying my hand at a minimalistic IRC bot, but I can never get a connection to work.
I'm doing this via a TcpClient object, which I've seen used in other such projects and those reportedly work.
Here's the code.
private string server = "irc.freenode.net";
private int port = 6667;
private string nick = "testingsharpbot";
private string channel = "testblablabla";
private TcpClient irc;
public ConfigForm() {
InitializeComponent();
}
private void ConnectButton_Click(object sender, EventArgs e) {
this.irc = new TcpClient(this.server, this.port);
using(NetworkStream stream = irc.GetStream()){
using(StreamReader sr = new StreamReader(stream)) {
using(StreamWriter sw = new StreamWriter(stream) {NewLine = "\r\n", AutoFlush = true}) {
sw.WriteLine("NICK " + this.nick);
sw.WriteLine("JOIN " + this.channel);
}
}
}
}
So I wait a bit and then do a /whois on the nickname, but I always get the same reply: user does not exist.
As far as I'm aware, the TcpClient makes the connection and I can then use the NetWorkStream instance to read and write to that connection.
What else do I need to do?
At first, I suggest you taking a look in the appropiate RFC:
http://www.faqs.org/rfcs/rfc2812.html
Look at Connection Registration. You need to follow this steps to get a connection:
Pass message
Nick message
User message
You're missing the USER command.
Related
I am creating a tcpClient on my main form and i am reading and writing to an irc server.
tcpClient = new TcpClient(serverName, 6667);
reader = new StreamReader(tcpClient.GetStream());
writer = new StreamWriter(tcpClient.GetStream());
writer.AutoFlush = true;
at some point my app opens a second form with a listbox of some options and i want to double click on one of these options and write something to the initial stream. I have tried creating a new tcpClient on the same port with a new reader and writer but this does not seem to work.
private void listBox_MouseDoubleClick(object sender, MouseEventArgs e)
{
SendMessage("some message");
}
private void SendMessage(string message)
{
TcpClient tcpClient;
StreamReader reader;
StreamWriter writer;
string serverName = "chicago.il.us.undernet.org";
tcpClient = new TcpClient(serverName, 6667);
reader = new StreamReader(tcpClient.GetStream());
writer = new StreamWriter(tcpClient.GetStream());
writer.AutoFlush = true;
writer.WriteLine("PRIVMSG #chan :" + message + "\r\n");
}
Could anybody point me to the right direction please?
Just like you I am not an expert in C#, I only program as a hobby and I'm self taught. So maybe there is a better way to do this, but this is how I would approach it myself.
Add a new class to your project like this:
namespace Test //If you copy this, remember to change the namespace to match your application
{
class Shared
{
public static TcpClient client = new TcpClient(“server”, 6667);
//OR do this and use the Connect method to connect later (see below)
public static TcpClient client = new TcpClient();
}
}
If you use the second declaration you could then connect later from ANY form (you would only need to connect once) when the form loads or on a button click etc. using this:
Shared.client.Connect(serverName, 6667);
Then in both of your forms you can do the following. Or if the SendMessage() function would be the same for both forms you could always make it a static function in the Shared class and call Shared.SendMessage("your message");.
private void SendMessage(string message)
{
if (Shared.client.Connected)
{
StreamReader reader;
StreamWriter writer;
reader = new StreamReader(Shared.client.GetStream());
writer = new StreamWriter(Shared.client.GetStream());
writer.AutoFlush = true;
writer.WriteLine("PRIVMSG #chan :" + message + "\r\n");
}
}
Using static to declare client in the Shared class means that it is not referenced by an instance of the class. So you don't have to do Shared sharedClass = new Shared(); before you can access it, and if you made 2 instances of the Shared class client would only have one instance which is accessible to both instances of the class.
There are two C# projects: one project is for the client, the other one is for the server. First step is to run the server , then to choose a target folder, after that to run the client project, to choose some text.txt to send to the server's target folder.
Only client can send files to the server
Demo:
1.choosing file target 2.client sends
+------------+
| tar folder | <---------------- text.txt
+------------+
My problem: there isn't compile errors or syntax errors in both projects, the only problem is that the server doesn't receives the .txt file.
Client:
First I designed a form for the client such as:
And placed an OpenFileDialog from the ToolBox-> Dialogs-> OpenFileDialog control.
Full code:
namespace SFileTransfer
{
public partial class Form1 : Form
{
string n;
byte[] b1;
OpenFileDialog op;
private void button1_Click(object sender, EventArgs e) //browse btn
{
op = new OpenFileDialog();
if (op.ShowDialog() == DialogResult.OK)
{
string t = textBox1.Text;
t = op.FileName;
FileInfo fi = new FileInfo(textBox1.Text = op.FileName);
n = fi.Name + "." + fi.Length;
TcpClient client = new TcpClient("127.0.0.1", 8100);//"127.0.0.1", 5055
StreamWriter sw = new StreamWriter(client.GetStream());
sw.WriteLine(n);
sw.Flush();
// label2.Text = "File Transferred....";
}
}
private void button2_Click(object sender, EventArgs e) //send btn
{
TcpClient client = new TcpClient("127.0.0.1", 8100);//5050
Stream s = client.GetStream();
b1 = File.ReadAllBytes(op.FileName);
s.Write(b1, 0, b1.Length);
client.Close();
// label2.Text = "File Transferred....";
}
}
}
Server:
Created and designed a Form for Server like:
Then Placed a folderBrowserDialog from the ToolBox->Dialogs-> folderBrowserDialog.
Full code:
namespace SFileTransferServer
{
public partial class Form1 : Form
{
string rd;
byte[] b1;
string v;
int m=1;
TcpListener list;
TcpClient client;
Int32 port = 8100;//5050
Int32 port1 = 8100;//5055
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
private void button1_Click(object sender, EventArgs e) //browse button
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
textBox1.Text = folderBrowserDialog1.SelectedPath;
list = new TcpListener(localAddr, port1);
list.Start();
client = list.AcceptTcpClient();
MessageBox.Show("ggggggg" + m.ToString());
Stream s = client.GetStream();
b1 = new byte[m];
s.Read(b1, 0, b1.Length);
MessageBox.Show(textBox1.Text);
File.WriteAllBytes(textBox1.Text+ "\\" + rd.Substring(0, rd.LastIndexOf('.')), b1);
list.Stop();
client.Close();
label1.Text = "File Received......";
}
}
private void Form2_Load(object sender, EventArgs e)
{
TcpListener list = new TcpListener(localAddr, port);
list.Start();
TcpClient client = list.AcceptTcpClient();
MessageBox.Show("Client trying to connect");
StreamReader sr = new StreamReader(client.GetStream());
rd = sr.ReadLine();
v = rd.Substring(rd.LastIndexOf('.') + 1);
m = int.Parse(v);
list.Stop();
client.Close();
}
}
}
Based on this page
I'm assuming you are a new coder because I'm seeing a lot of inefficient code.
On the Client App: Don't redeclare the TcpClient, instead declare it in the global scope and just reuse it.
Then On the server side: Always use the correct datatype IF AT ALL possible
Int16 port = 8100;
//same as
int port = 8100;
Then on to the question: First you are only reading a single byte from the received data
int m=1;
byte[] b1 = new byte[m];
s.Read(b1, 0, b1.Length);
//Then if you know the length of the byte, why do the computation of b1.Length, just use
s.Read(b1, 0, 1);
I see now you are also opeing the connection on the Load event. But that variable is not in the global scope so you are essentially creating a variable in the Load event then after the Load event finishes, sending it to the garbage collection and then declaring a variable with the same name in the button click event.
So try declaring the TcpListener object in the global scope then assign it in the load event and start listening.
Now the AcceptTcpClient() method is a blocking method so it will block your thead until it receives a connection attempt at which point it will return the client object. So try to use this instead:
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.accepttcpclient(v=vs.110).aspx
using System.Threading;
TcpClient client;
TcpListener server = new TcpListener("127.0.0.1",8100)
Thread incoming_connection = new Thread(ic);
incoming_connection.Start();
private void ic() {
client = server.AcceptTcpClient();
Stream s = client.GetStream();
//And then all the other code
}
Or you can use the Pending method as suggested by MSDN (on the ref page).
EDIT: using threading like this is 'not safe', so if you don't understand threading go read up on it first, this post doesn't cover how to use multi-threading.
So my server and chat client are made from 2 different C# TCP tutorials.You may recognize 1 if not both of them and I have made my own modifications to them to fit my own style. When I tried both they worked perfectly fine with 0 loss, but my version has exactly a 50% loss rate.
For instance:
1. A client connects: Data received
2. A client sends text: No Data
3. A client sends text: Data received
4. A client sends text: No Data
The server code is as follows:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
namespace WindowsFormsApplication2
{
class Server
{
private TcpListener tcpListener;
private Thread listenThread;
public Hashtable clientsList = new Hashtable();
private System.Windows.Forms.TextBox output;
private delegate void ObjectDelegate(String text);
private ObjectDelegate del;
public Server(System.Windows.Forms.TextBox setOut)
{
this.tcpListener = new TcpListener(IPAddress.Any, 8888);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.IsBackground = true;
this.listenThread.Start();
output = setOut;
del = new ObjectDelegate(outputTextToServer);
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
addClient(client);
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.IsBackground = true;
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
String text = getData(clientStream);
del.Invoke(text); //Used for Cross Threading & sending text to server output
//if filter(text)
sendMessage(tcpClient);
//System.Diagnostics.Debug.WriteLine(text); //Spit it out in the console
}
tcpClient.Close();
}
private void outputTextToServer(String text)
{
if (output.InvokeRequired)
{
// we then create the delegate again
// if you've made it global then you won't need to do this
ObjectDelegate method = new ObjectDelegate(outputTextToServer);
// we then simply invoke it and return
output.Invoke(method, text);
return;
}
output.AppendText(Environment.NewLine + " >> " + text);
}
private String getData(NetworkStream stream)
{
int newData;
byte[] message = new byte[4096];
ASCIIEncoding encoder = new ASCIIEncoding();
newData = stream.Read(message, 0, 4096);
String text = encoder.GetString(message, 0, newData); //Translate it into text
text = text.Substring(0, text.IndexOf("$")); //Here comes the money
return text;
}
private void addClient(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
String dataFromClient = getData(clientStream);
if (clientsList.Contains(dataFromClient))
{
Console.WriteLine(dataFromClient + " Tried to join chat room, but " + dataFromClient + " is already in use");
//broadcast("A doppleganger of " + dataFromClient + " has attempted to join!", dataFromClient, false);
}
else
{
clientsList.Add(dataFromClient, tcpClient);
//broadcast(dataFromClient + " Joined ", dataFromClient, false);
del.Invoke(dataFromClient + " Joined chat room ");
//handleClinet client = new handleClinet();
//client.startClient(clientSocket, dataFromClient, clientsList);
}
}
private Boolean connectionAlive(NetworkStream stream)
{
byte[] message = new byte[4096];
int bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = stream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
return false;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
//clientsList.Remove
return false;
}
return true;
}
private void sendMessage(TcpClient client)
{
NetworkStream clientStream = client.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Client!");
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
}
}
And here's my client code
using System;
using System.Windows.Forms;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public delegate void newDelegate();
public newDelegate myDelegate;
System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
NetworkStream serverStream = default(NetworkStream);
string readData = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
newMsg();
}
private void newMsg()
{
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(textBox2.Text + "$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
textBox2.Text = "";
}
private void button2_Click(object sender, EventArgs e)
{
readData = "Connecting to Chat Server ...";
msg();
clientSocket.Connect(txtIP.Text, int.Parse(txtPort.Text));
serverStream = clientSocket.GetStream();
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(txtName.Text + "$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
myDelegate = new newDelegate(disconnect);
Thread ctThread = new Thread(getMessage);
ctThread.IsBackground = true;
ctThread.Start();
button2.Enabled = false;
}
private void getMessage()
{
while (true)
{
serverStream = clientSocket.GetStream();
int buffSize = 0;
byte[] inStream = new byte[clientSocket.ReceiveBufferSize];
buffSize = clientSocket.ReceiveBufferSize;
try
{
serverStream.Read(inStream, 0, buffSize);
string returndata = System.Text.Encoding.ASCII.GetString(inStream);
readData = "" + returndata;
msg();
}
catch
{
Invoke(myDelegate);
return;
}
}
}
private void disconnect()
{
button2.Enabled = true;
}
private void msg()
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(msg));
else
textBox1.AppendText(Environment.NewLine + " >> " + readData);
//textBox1.Text = textBox1.Text + Environment.NewLine + " >> " + readData;
}
private void textBox2_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
newMsg();
}
}
private void cmdHost_Click(object sender, EventArgs e)
{
Server serv = new Server(txtLog);
}
}
}
This code is obviously a work in progress and sorry in advance for messiness. Any other suggestions to the code are also welcome.
Okay, this is starting to get a bit long.
There's multiple errors in your code. Starting with the server code:
As Damien noted, you're trying to read each "message" twice - first in HandleClientComm, then again in getData. The stream no longer has the original data, so you're just throwing one of the reads away completely (thus the suspicious 50% "packet" loss)
Later, in getData, you discard all the data in the stream after the first $. While this is obviously an attempt at handling message framing (since TCP is a stream-based protocol, not a message-based protocol), it's a silly one - you're throwing the data away. The reason this didn't show in your testing is that 1) Windows treats local TCP very differently from remote TCP, 2) You'd need to be actually able to send two messages fast enough to make them "blend" together in the stream. That means either sending two messages in about 200ms (default TCP buffering) or blocking on reads.
You keep Flushing the network stream. This doesn't actually do anything, and even if it did, you wouldn't want to do that.
connectionAlive reads from the shared socket - this is always a bad idea. Never have more than one reader - multiple readers don't work with stream-based protocols. It doesn't seem you're using it in your sample code, but beware of trying to.
The commented out clientList.Remove would of course be a cross-thread access of a shared field. If you want to do it like this, you'll have to ensure the concurrent access is safe - either by using ConcurrentDictionary instead of HashSet, or by locking around each write and read of clientList.
You're expecting to get the whole message in one Read. That may be fine for a simple chat client, but it's bad TCP anyway - you need to read until you find your message terminator. If I send a message big enough, your code will just drop dead on text.IndexOf("$").
There's a lot of "style" issues as well, although this isn't code review, so let me just list some: using ancient technology, synchronous sockets for the server, mixing multi-threaded code with GUI at will. This is mostly about maintainability and performance, though - not correctness.
Now, the client is a bit simpler:
Again, don't Flush the network stream.
Don't use background threads if you don't have to. Simply make sure to terminate the connections etc. properly.
Disconnect should actually disconnect. It's not that hard, just close the TcpClient.
What is readData = "" + returndata supposed to do? That's just silly.
You're ignoring the return value of Read. This means that you have no idea how many bytes of data you read - which means your returnData string actually contains the message followed by a few thousand \0 characters. The only reason you don't see them in output is because most of Windows uses \0 as the string terminator ("it made sense at the time"). .NET doesn't.
Again, the Read expects the whole message at once. Unlike the server, this isn't going to crash the client, but your code will behave differently (e.g. an extra \r\n >> even though it's not a separate message.
The style issues from the server also apply here.
As a side-note, I've recently made a simplified networking sample that handles a simple chat client-server system using more modern technologies - using await-based asynchronous I/O instead of multi-threading, for example. It's not production-ready code, but it should show the ideas and intent quite clearly (I also recommend having a look at the first sample, "HTTP-like TCP communication"). You can find the full source code here - Networking Part 2.
I need a socket communication between my own written java server and C# client, the problem is that the C# client can't receive any messages from my java server, but sending messages to my java server works.
my workflow:
Java: Create Server
Java: Waiting for Client connection
c#: Create Client
c#: Build connection to the server
c#: send a msg to the server
Java: msg received
java: send msg to c# client
c#: receiving msg from server <- this is the point where the client waits for a message but never get.
Java Server code:
public class Communicator {
private int m_port;
private Socket m_socket;
private ServerSocket m_serverSocket;
public Communicator(int port) {
this.m_port = port;
initConnection();
}
private void initConnection() {
try {
System.out.println("Creating Server");
m_serverSocket = new ServerSocket(m_port);
System.out.println("Waiting for client connection");
m_socket = m_serverSocket.accept();
System.out.println("Connection made");
} catch (IOException e) {
e.printStackTrace();
}
}
public String sendMsg(JSONMessage msg) {
try {
//get msg
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(m_socket.getInputStream()));
System.out.println("Waiting for msg...");
String answer = bufferedReader.readLine();
System.out.println("Received: " + answer);
//send msg
PrintWriter writer = new PrintWriter(m_socket.getOutputStream(),true);
writer.print(msg.getMsg());
System.out.println("Sending: " + msg.getMsg());
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
return "";
}
}
My C# client code:
class Communicator
{
private int m_port;
private Thread mainThread;
public Communicator(int port)
{
m_port = port;
mainThread = new Thread(new ThreadStart(this.initConnection));
mainThread.Start();
}
public void initConnection()
{
IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), m_port);
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
Console.WriteLine("Trying to build connection");
server.Connect(ip);
Console.WriteLine("Connection successful");
NetworkStream ns = new NetworkStream(server);
StreamReader sr = new StreamReader(ns);
StreamWriter sw = new StreamWriter(ns);
string data;
string welcome = "Hello";
Console.WriteLine("Sending: " + welcome);
sw.WriteLine(welcome);
sw.Flush();
Console.WriteLine("Receiving...");
data = sr.ReadLine();
// --> NEVER REACHING THIS POINT <---
Console.WriteLine("Received: " + data);
}
catch (SocketException e)
{
Console.WriteLine("Connection failed.");
return;
}
}
}
Does somebody has any idea why it never reaches my client code Console.WriteLine("Received: " + data); ?
I already tried with waits on both sides. I'm not getting any exceptions or error so I don't have really an idea where my problem is.
Thanks
If your receiver is expecting lines, your sender has to send lines. Your sender does not send lines, so the receiver waits forever until it gets one.
To avoid these kinds of problems in the future, you should always make a specification document that explains how your protocol works, ideally at the byte level. It should specify whether the protocol contains messages and if so, how the sender marks message boundaries and how the receiver identifies them.
Here, your receiver identifies message boundaries by looking for line endings. But your sender doesn't mark message boundaries with line endings. So the receiver waits forever.
If you had a protocol specification, this would have been obvious. In the future, I strongly urge you to invest the time to specify every protocol you implement.
You need to use println() which adds a new line instead of print(). In Java, readLine waits for a new line and I would expect it to do the same in C#. Also println will auto-flush, so you don't need to flush as well.
If you intend to use this connection mreo than once, you need to keep the BufferedReader and PrintWriter for the connection. (So I suggest you create these after the socket is created/accepted) Creating these multiple times for the same socket can be error prone and confusing.
I have a simple multithreaded C# server and client. When just one client is connected I can interact with it fine, but when two or more are connected, it seems I am using the last NetworkStream. What I'd like to be able to do is give an input command that specifies the stream to read and write to. So, for example, the first client is "Client 1" and the second client is "Client 2." I'd just type "Client 2" into my command textbox and it will get the stream for the second client.
The problem is, I don't know how to assign the text to the clients. Here is the relevant code from the server:
private void ClientThread(Object client)
{
NetworkStream networkStream = ((TcpClient)client).GetStream();
Dictionary<int, NetworkStream> myClients = new Dictionary<int, NetworkStream>(); // This didn't work.
myClients.Add(counter, ((TcpClient)client).GetStream()); // Wouldn't write.
counter = counter + 1;
streamReader = new StreamReader(networkStream);
streamWriter = new StreamWriter(networkStream);
strInput = new StringBuilder();
while (true)
{
try
{
strInput.Append(streamReader.ReadLine());
strInput.Append("\r\n");
}
catch (Exception error)
{
break;
}
Application.DoEvents();
DisplayMessage(strInput.ToString());
strInput.Remove(0, strInput.Length);
}
}
private void textBox2_KeyDown(object sender, KeyEventArgs e)
{
try
{
if (e.KeyCode == Keys.Enter)
{
//ListView.SelectedListViewItemCollection stuff = listView1.SelectedItems;
//ip is displayed in listView1, if I could also bind the stream for the ip
//to it and select it, that would be cool.
{
strInput.Append(textBox2.Text.ToString());
streamWriter.WriteLine(strInput);
streamWriter.Flush();
strInput.Remove(0, strInput.Length);
if (textBox2.Text == "cls") textBox1.Text = "";
textBox2.Text = "";
}
}
}
catch (Exception error) { }
}
So, how can I do this?
NetworkStream networkStream = myClients[2];
using(streamWriter = new StreamWriter(networkStream))
{
streamWriter.WriteLine("hello client 2"); // send something to Client 2
}
networkStream = myClients[4];
using(streamWriter = new StreamWriter(networkStream))
{
streamWriter.WriteLine("hello client 4"); // send something to Client 4
}
You are obviously storing all your client streams into a dictionary. Just load that stream into a StreamWriter and send your data.
Make your dictionary myClients class field and then just get your currently active stream like above.