• 0

[C#] Simple chat program


Question

Im attempting put some sort of simple chat functionality into my C# program but need clarification on a few issues before I start...

Sockets are the main way of connecting users together if i understand correctly. A server, with a known IP address is contacted and is used to record the IP address and port number of each client. Once the clients have the necessary IP addresses they can communicate directly. I can understand how this would work on a local network but would ports need to be forwarded for this to work over the internet?

If that is the case then I would need to find another way of implementing a chat feature.

The program is already consuming a webservice, could that be modified to facilitate chat in an efficient fashion?

Thanks

Link to comment
Share on other sites

21 answers to this question

Recommended Posts

  • 0

You would most likely need a combination of Firewall rules to allow traffic on your desired ports, and possibly also Port-forwarding on any router that sits between the client and the outside world.

If you wanted to implement the chat system as a webservice, you'd need to provision a data store (such as Sql Server) to store messages until they are retrieved by the client. The main difference with this setup, is it would become a pull-service, where the client will have to poll the webservice to determine if they have any pending messages.

Link to comment
Share on other sites

  • 0
You would most likely need a combination of Firewall rules to allow traffic on your desired ports, and possibly also Port-forwarding on any router that sits between the client and the outside world.

Yea, I was thinking that was the case. Didn't know that every router on the path needed to be configured aswell... thats insane.

How do programs like AIM or MSN implement chat?

If you wanted to implement the chat system as a webservice, you'd need to provision a data store (such as Sql Server) to store messages until they are retrieved by the client. The main difference with this setup, is it would become a pull-service, where the client will have to poll the webservice to determine if they have any pending messages.

Thats exactly what I was thinking. I have a MySQL database that i could act as a buffer for the messages, Have each client poll for updates every 2 seconds or so. If i was going to allow the client to upload images and files though it could be awkward as the first client would have to upload the file to the server and the second client would have to wait for the file to upload before they could download the file.

I'll use the webservice as a last resort, seems like a bit of a hack-job. At the moment i'm trying to learn how to correctly setup a simple chat program.

Link to comment
Share on other sites

  • 0

If you stuck with sockets, or even try Windows Communication Foundation, there are a variety of tutorials around, Bing is your friend!

Link to comment
Share on other sites

  • 0

won't need to configure every router

basically all users will connect to your server on some port

now for direct communication one user will have to host on an open port while the other connects to it

sometimes this may not be possible so a server will be used instead

the way msn works is all messages are sent through their server and transmitted to the other user e.g.

User A connects to the Server

User B connects to the Server

User A wants to send a message to User B so it will pass a message to the server saying "send this to User B"

the server will grab the message then on the socket thats connected to User B it will send the message from User A along

If you need to use the polling approach for a web service you can queue up the messages as for file transfer you can accept a request from the user to send a file

pass the request onto the other user (so next time it refreshes they will see it) then they can accept it (pass that back to the other user)

then the user can upload 8KB to the server - other user downloads 8KB; user sends another 8KB and so forth

sorry if that was difficult to read/understand goodluck

Link to comment
Share on other sites

  • 0
If you stuck with sockets, or even try Windows Communication Foundation, there are a variety of tutorials around, Bing is your friend!

Trust me, i've been binging all morning. Just want answers to these relatively trivial questions before i get in too deep. :)

User A wants to send a message to User B so it will pass a message to the server saying "send this to User B"

the server will grab the message then on the socket thats connected to User B it will send the message from User A along

Thanks for replying. Most of your post makes sense... however, the server wont be able to pass the message onto the receiver unless the receiver has port forwarding enabled also. Right?

My main concern is simplicity. I dont want every user to have to forward ports just to facilitate chat.

If you need to use the polling approach for a web service you can queue up the messages as for file transfer you can accept a request from the user to send a file

pass the request onto the other user (so next time it refreshes they will see it) then they can accept it (pass that back to the other user)

then the user can upload 8KB to the server - other user downloads 8KB; user sends another 8KB and so forth

sorry if that was difficult to read/understand goodluck

Never though of that actually, guess the 8KB of data could be stored in a database?

Seems like a lot of overhead involved... would performance be poor?

Link to comment
Share on other sites

  • 0
Thanks for replying. Most of your post makes sense... however, the server wont be able to pass the message onto the receiver unless the receiver has port forwarding enabled also. Right?

If a user can connect to you in the first place then there ports are already setup correctly (once the connection is established you can freely send/receieve data without worrying about router/firewalls)

a firewall will rarely filter an outgoing connection without prompting the user and the user will usually accept it if anything once the program connects it can do as it pleases the issue is when the server tries to connect to your pc which is really a big no no :p

for example its like when you visit neowin you initiate the connection and begin talking to neowin and then neowin can talk back to you

however neowin cannot connect to your pc and start talking to you without their being issues (firewalls and what not)

My main concern is simplicity. I dont want every user to have to forward ports just to facilitate chat.
Never though of that actually, guess the 8KB of data could be stored in a database?

Seems like a lot of overhead involved... would performance be poor?

Not sure a more efficient method would be to store it on disk but you have to be careful with that however what you can do is try this

{Note: (even though your using c# you'd have to simulate this if you want to use the web service)}

User A requests to send a file....User B Accepts

User A Initiates a "browser-based upload" {Note} The user begins uploading data and the server grabs this data and just sends it to User B as the data arrives e.g. streaming the data down the line

so with that approach you pretty much won't be storing anything on the server which will reduce any overhead involved with disk access

Link to comment
Share on other sites

  • 0
If a user can connect to you in the first place then there ports are already setup correctly (once the connection is established you can freely send/receieve data without worrying about router/firewalls)

a firewall will rarely filter an outgoing connection without prompting the user and the user will usually accept it if anything once the program connects it can do as it pleases the issue is when the server tries to connect to your pc which is really a big no no :p

Ohh cool, didn't know that... A permanent tunnel between the client and server machine is maintained until the connection is terminated then?

The client router must be constantly expecting a response from the server then to get past the NAT.

If thats the case it shouldn't be too difficult to implement, just a matter of setting up the server and configuring the ports at the server side.

Great... Thanks for all the help, both of you. I feel much better prepared now :)

Edited by Guest
Link to comment
Share on other sites

  • 0

Yep that would be the tcp protocol

an issue when 2 people can't connect together because they are both behind firewalls is usually resolved by introducing some server

theres a much better chance they can connect to the server and once they can connect they can freely transmit data between each other

or if one user can host but the other can't once a connection is established they can freely talk to each other back and forth

No problem if you have any more issues keep us informed i'd like to see how this turns out :D good luck :)

Link to comment
Share on other sites

  • 0

back again :p

I've been working with sockets for a few hours this afternoon.

I managed to build the server, register multiple clients and accept messages from each of them.

My main problem at the moment is message segmentation. If client A sends 3 strings to the server, how can the server distinguish one from the other?

I could set the buffer size at the client and server to a ridiculously high number but i'd prefer to avoid doing that.

This is what i have at the moment...

Byte[] message = new Byte[1024];
client_Socket.Receive(message);
MessageBox.Show("String received");

Imagine i wanted to send the following strings to the server:

String A: "aaaaaaaaaaaaaaaaaa"

String B: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"

String C: "aaaaaaaaaaaaaaaaaa"

Depending on the buffer size i might get the following:

String received (for string A)

String received (for the first few characters string B)

String received (for the remaining characters of string B)

String received (for string C)

Should i be sending delimiting bytes to indicate the start and end of a string so that the server can construct the whole string correctly?

Link to comment
Share on other sites

  • 0

I've had exactly this problem a few days ago. Either send fixed-length messages (filling unused bytes with null characters, something like that); prefix each message with a fixed-size header containing the length of the following message (that's what I did); or, as you proposed, use a delimiter. The delimiter seems like the most efficient solution, but you have to make sure that it can never be used for any other purpose.

Link to comment
Share on other sites

  • 0
I've had exactly this problem a few days ago. Either send fixed-length messages (filling unused bytes with null characters, something like that); prefix each message with a fixed-size header containing the length of the following message (that's what I did); or, as you proposed, use a delimiter. The delimiter seems like the most efficient solution, but you have to make sure that it can never be used for any other purpose.

At least i'm not the only one with these issues. :p

Strange that we need to delimit the information. I expected this type of behaviour with UDP but i though TCP would have a built-in mechanism to indicate the beginning and the end of the message sent.

Link to comment
Share on other sites

  • 0

TCP does a lot:

  • There are a few key features that set TCP apart from User Datagram Protocol:
  • Ordered data transfer - the destination host rearranges according to sequence number[2]
  • Retransmission of lost packets - any cumulative stream not acknowledged will be retransmitted[2]
  • Discarding duplicate packets
  • Error-free data transfer
  • Flow control - limits the rate a sender transfers data to guarantee reliable delivery. When the receiving host's buffer fills, the next acknowledgement contains a 0 in the window size, to stop transfer and allow the data in the buffer to be processed.[2]
  • Congestion control - sliding window[2]

http://en.wikipedia.org/wiki/Transmission_...l#Data_transfer

However, guaranteeing that one send = one recv is not on the list. Luckily, it's pretty simple to solve given all the guarantees already offered.

Link to comment
Share on other sites

  • 0

Good... I have a simple server built now, but i need to make a few architectual changes before i continue.

At the moment the server has one dedicated thread for accepting new connections. This creates a TCP_Client struct which stores the clients socket and thread.

		public struct TCP_Client
		{
			public String friendly_Name;
			public Thread listener;
			public Socket socket;
		}		

		private void accept_New_Clients()
		{
			while (true)
			{
				try
				{
					TCP_Client client = new TCP_Client();
					client.socket = tcp_Listener.AcceptSocket();

					client.listener = new Thread(new ParameterizedThreadStart(get_Client_Input));
					client.listener.Start(client);

					this.tcp_Clients.Add(client);
				}
				catch (Exception) { /* do nothing */ }
			}
		}

Each client spawns a new thread to pick up messages from the client. The client struct object is passed to the new thread.

		public struct TCP_Message
		{
			public String type;
			public String from;
			public byte[] information;
		}

		private void get_Client_Input(object client_Object)
		{
			while (true)
			{
				try
				{
					TCP_Message message = new TCP_Message();
					TCP_Client client = (TCP_Client)client_Object;

					String[] header_Data = null;
					Boolean more_Data_Available = true;

					String TCP_Message = null;
					String message_Body = null;

					while (more_Data_Available == true)
					{
						byte[] bytes = new byte[1024];
						int bytes_Received = client.socket.Receive(bytes);

						TCP_Message += Encoding.UTF8.GetString(bytes, 0, bytes_Received);

						if (bytes_Received == 0 || TCP_Message.IndexOf("</EOF>") > -1)
						{
							more_Data_Available = false;
						}
					}

					// isolate header information
					String header_Delimiter = "</HEADER>";
					String header_String = TCP_Message.Substring(0, TCP_Message.IndexOf(header_Delimiter));
					header_Data = header_String.Split(';');

					if (header_Data.Length > 0) { message.type = header_Data[0]; }
					if (header_Data.Length > 1) { message.from = header_Data[1]; }

					// isolate message payload
					message_Body = TCP_Message.Remove(0, header_String.Length + header_Delimiter.Length);
					message_Body = message_Body.Replace("</EOF>", "");

					message.information = Encoding.UTF8.GetBytes(message_Body);

					// decision engine
					this.process_TCP_Message(message);

				}
				catch (Exception) { /* do nothing */ }
			}
		}

This seems to work alright but it's already starting to drop a few messages.

Its poor architecture, i understand that... but this has been my first venture into socket programming and threads. :p

I have read that the server should only have

  • one thread to pick up new messages from all the clients and add them to a queue
  • one worker thread to process the queue

Any idea how i could manage this? I cant seem to find any examples on the internet.

Thanks

Link to comment
Share on other sites

  • 0

Nevermind... I'm looking at the asynchronous client/server socket examples on MSDN at the moment.

Think i'll just scrap what im doing at the moment and try them out...

Link to comment
Share on other sites

  • 0
Yea, I was thinking that was the case. Didn't know that every router on the path needed to be configured aswell... thats insane.

How do programs like AIM or MSN implement chat?

For simple text chat, these services use a server that just relays messages. Simple, works in all cases, but has a higher latency and consumes bandwidth of the chat provider.

The bandwidth cost makes this impractical for file transfers and voice/video (which would also suffer in quality from the latency), so a direct connection between clients is necessary. In these cases, both clients make a UDP connection to the server, and then the server acts as a connection bootstrap of sorts and and "joins" these two connections together (and the server steps out of the picture afterwards). This works for virtually all consumer NAT setups, but necessarily requires the use of a stateless protocol (i.e., UDP, not TCP). (plus, the various guarantees and requirements imposed by TCP would be inappropriate for real-time data like voice/video, where it's desirable to have the option to just skip a chunk of data instead of waiting to have it retransmitted; for file transfers, the client has to implement the various transmission guarantees that it requires since it can't just outsource that work to TCP).

No mainstream chat application (except IRC file transfers, but IRC isn't exactly mainstream any more these days) requires configuration of the router because that is simply impractical, and yes, quite "insane" (not to mention, many routers, esp. older ones, don't support uPnP configuration).

Link to comment
Share on other sites

  • 0
For simple text chat, these services use a server that just relays messages. Simple, works in all cases, but has a higher latency and consumes bandwidth of the chat provider.

The bandwidth cost makes this impractical for file transfers and voice/video (which would also suffer in quality from the latency), so a direct connection between clients is necessary. In these cases, both clients make a UDP connection to the server, and then the server acts as a connection bootstrap of sorts and and "joins" these two connections together (and the server steps out of the picture afterwards). This works for virtually all consumer NAT setups, but necessarily requires the use of a stateless protocol (i.e., UDP, not TCP). (plus, the various guarantees and requirements imposed by TCP would be inappropriate for real-time data like voice/video, where it's desirable to have the option to just skip a chunk of data instead of waiting to have it retransmitted; for file transfers, the client has to implement the various transmission guarantees that it requires since it can't just outsource that work to TCP).

No mainstream chat application (except IRC file transfers, but IRC isn't exactly mainstream any more these days) requires configuration of the router because that is simply impractical, and yes, quite "insane" (not to mention, many routers, esp. older ones, don't support uPnP configuration).

Yea, i read something about that a few days ago. I think it was the kazaa that first program to implemented that idea. The clients wouldn't be able to find each other without creating the initial connection to the centralised kazaa server. Its a nice idea but i dont think i'll attempt anything like that :D

Link to comment
Share on other sites

  • 0

Ok, so final question regarding this issue... I promise.

I started from scratch yesterday and followed the MSDN asynchronous sockets client / server examples very carefully and for the most part things are working great.

Messages are encapsulated as follows

message_type;send_to<HEADER>message<EOF>

I dont lose any messages anymore but sometimes the messages arrive together. eg... after send a REALLY long string

message_type;send_to<HEADER>message1<EOF>message_type;send_to<HEADER>messag2<EOF>

Im using beginConnect, beginSend and beginReceive as well as ManualResetEvents but i cant seem to find out whats going wrong

I've included the client / server code... I would really appreciate if someone could have a quick look at it and point out what i'm doing wrong.

Thanks :)

code.zip

Link to comment
Share on other sites

  • 0

I don't think you can have any guarantees that messages will arrive in whole pieces. You could very receive:

(1st receive): "message_"

(2nd receive): "type;send_to<HEADER>message1<EOF>message_type;send_to<H"

(3rd receive): "EADER>messag2<EOF>"

How I would deal with that is to create a buffer with logic similar to the following (pseudocode):

while (newRecv = socket.recv) != ""
	buffer += newRecv
	while (Process(buffer));

Process(buffer):
	// try to find first begin-end pair: if success, remove from buffer, return true; otherwise return false

Link to comment
Share on other sites

  • 0
Nevermind... I'm looking at the asynchronous client/server socket examples on MSDN at the moment.

Think i'll just scrap what im doing at the moment and try them out...

What are the advantages of using Asynchronous Sockets over just using threads? I've got some code that appears to work based off of this,

http://www.switchonthecode.com/tutorials/c...aded-tcp-server

and it seems to work ok. :)

Link to comment
Share on other sites

  • 0

I think i've found the solution. Ive accepted the fact that messages may come together, they just have to be isolated at the client side.

When a block of messages are now received i use the <EOF> delimiter to split the group of messages into single messages.

if (content.EndsWith("&lt;EOF&gt;") == true)
{
	String[] end_of_file = {"&lt;EOF&gt;"};
	String[] content_Array = content.Split(end_of_file, StringSplitOptions.RemoveEmptyEntries);

	foreach (String single_Message in content_Array)
	{
		Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate()
		{
			this.conversation_ListBox.Items.Add(single_Message);
		});
	}

	// clear the previous content
	this.state.sb = new StringBuilder();

	receive_Message_Done.Set();

	// pick up new messages sent by the server
	receive_Message_Done.Reset();
	this.state.work_Socket.BeginReceive(this.state.buffer, 0, StateObject.Buffer_Size, SocketFlags.None, new AsyncCallback(receive_Data), this.state.work_Socket);
	receive_Message_Done.WaitOne();
}

Seems to work... Just need to test it out a bit more :D

Unless there is a correct way of isolating the messages i'll keep using this method.

What are the advantages of using Asynchronous Sockets over just using threads? I've got some code that appears to work based off of this,

http://www.switchonthecode.com/tutorials/c...aded-tcp-server

and it seems to work ok. :)

Settng up threaded synchronous sockets was a lot easier alright. I checked out a few sites and noticed that they suggested against it. Apparently it doesn't scale up well. 100 or more threads all fighting for CPU + the memory usage associated with every new thread.

This was the first time i built a client/server framework, i just wanted to do it right... and asynchronous socket seemed to fit the bill.

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.