I'm making an application server client using tcp sockets in c# ..
The application has multi tasks like file transfer .. file manager .. chat .. (voice chat later)
So I decided to create a socket to receive the commands as a string
and another socket to transfer files ..
is that a good way for programming a server-client application or should I try another way?
because user could send a message while receiving/sending a file
and how could I tell the (file-transfer-server) accept only the same client who has already connected with the main server
ex: server listen on port 8000 and accept clients .. and file transfer on port 8111
public StartSever()
{
sr = new StreamReader(networkStream);
while(connected)
{
string[] command = sr.ReadLine().split(',');
switch (Command[0])
{
case "RecFile":
StartFileTransferServer(); // creating new socket tcp listens on port 8111
Receiving();
break;
case "SendFile":
StartFileTransferServer(); // creating new socket tcp listens on port 8111
Sending();
break;
case "Chat":
chat(Command[1]);
break;
default:
break;
}
}
You could use some kind of communication framework or library which would abstract the details of TCP/IP sockets and allow you to send objects and messages and files between your client and server end and not have to worry about all the details.
Some things you could look into using instead:
WCF
ZeroMQ
RabbitMQ
Those are just some examples I thought of off of the top of my head there are tons of such frameworks that work at different levels of abstraction and offer more heavy or light weight implementations. I'm sure you could find one that meets your basic needs.
The application has multi tasks like file transfer .. file manager .. chat .. (voice chat later)
If you haven't gotten longer than using a colon separated string protocol and switch statements I recommend that you stop right there.
I strongly suggest that you use WCF with netTcpBinding and duplex binding instead: http://www.dotnetconsult.co.uk/weblog2/PermaLink,guid,b891610a-6b78-4b54-b9a6-4ec81c82b7c0.aspx
You will not succeed with a VoiceChat on your own. Buy a component for that.
When you call Accept you receive socket for new user. After this you can create NetworkStream.
Write and Read are blocking methods. You can try to use asynchronous methods: BeginRead and BeginWrite.
Also creating new thread per user is not good if you will have thousands users.
I propose such solution: One thread Accpects connections and send work to threads pool.
Something like this (note this is only draft):
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace ConsoleServer
{
class Program
{
private static void Worker(Object sockObj)
{
var mySocket = (Socket) sockObj;
using(var netStream = new NetworkStream(mySocket))
{
//Handle work;
}
}
static void Main(string[] args)
{
int port = 80;
// create the socket
Socket listenSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
// bind the listening socket to the port
IPAddress hostIP = (Dns.Resolve(IPAddress.Any.ToString())).AddressList[0];
IPEndPoint ep = new IPEndPoint(hostIP, port);
listenSocket.Bind(ep);
// start listening
listenSocket.Listen(125);
while (true)
{
Socket mySocket = listenSocket.Accept();
ThreadPool.QueueUserWorkItem(new WaitCallback(Worker), mySocket);
}
}
}
}
Related
I have an application that is reading data from a Udp server on 32 different ports that I need to process. I'm using the UdpClient.BeginReceive that is calling itself because I want to listen all the time :
private void ProcessEndpointData(IAsyncResult result)
{
UdpClient client = result.AsyncState as UdpClient;
// points towards whoever had sent the message:
IPEndPoint source = new IPEndPoint(0, 0);
// schedule the next receive operation once reading is done:
client.BeginReceive(new AsyncCallback(this.ProcessEndpointData), client);
// get the actual message and fill out the source:
this.DecodeDatagram(new DatagrammeAscb()
{
Datagramme = this.ByteArrayToStructure<Datagram>(client.EndReceive(result, ref source))
});
}
When I stop the server side, the function is waiting for data (that is normal behavior). What I would like to do is to detect when the server is disconnected and then close all my clients.
I'm asking myself if I should use sockets class to have more controls or just maybe I'm missing something here.
Anyway thanks for your help.
I had an idea, and am trying to upload data from a Netduino to a SQL DB hosted in an Azure VM. After struggling with WCF, I have just written a simple app in C# that processes requests through Sockets (code below) and does the job -works fine on my machine/LAN. Since I still don't know much about
Azure VMs, how can I expose an IP/port to the outside world in such a way that my client application reaches the server part successfully?
Currently I can't make the Azure VM to listen to the outside world. I have created an endpoint (with a name, TCP protocol, same public and private ports and checked 'enable direct server return'. To try it, I shutdown the firewall and tried it, from outside, to no avail.
Can a listener on that port be considered a secure solution?
For the sake of simplicity I thought of Sockets, but is it really a good idea ? Would you guys do differently and still keep it simple as this ?
...
Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, 3211);
server.Bind(localEndPoint);
server.Listen(Int32.MaxValue);
while (true)
{
Socket clientSocket = server.Accept();
new ProcessClientRequest(clientSocket);
}
...
public ProcessClientRequest(Socket clientSocket)
{
m_clientSocket = clientSocket;
new Thread(ProcessRequest).Start();
}
private void ProcessRequest()
{
const Int32 c_microsecondsPerSecond = 1000000;
using (m_clientSocket)
{
Byte[] buffer = new Byte[1024];
string lastRead;
if (m_clientSocket.Poll(5 * c_microsecondsPerSecond, SelectMode.SelectRead))
{
if (m_clientSocket.Available == 0)
return;
Int32 bytesRead = m_clientSocket.Receive(buffer, m_clientSocket.Available, SocketFlags.None);
// Return a key to the client.
lastRead = WriteDataToDB(System.Text.Encoding.UTF8.GetString(buffer).Replace("\0", string.Empty));
m_clientSocket.Send(Encoding.UTF8.GetBytes(lastRead));
}
}
}
Have you checked your VM's endpoint settings on https://manage.windowsazure.com/? Adding a rule there should allow anyone to connect to <vm-dns-name>.cloudapp.net:<public-port>.
That's the DNS name, not the VM name. My Meteor app didn't need the direct server return feature.
A sufficiently hard hashing and encryption layer should be secure enough, provided the customers know your public key's fingerprint because Certificate Authorities can be compromised.
The .NET has a built in HttpListener class, however I was wondering how I could roll my own Http Listener. I know there are a lot of implications to this, but I just want see the browser and my own app interact with each other.
This is the code I have written so far:
Socket servSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
servSocket.ExclusiveAddressUse = false; // Does this matter?
servSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
servSocket.Listen(10);
byte[] buffer;
do
{
try
{
Socket clientSocket = servSocket.Accept();
Console.WriteLine("Received Request at: {0}.", DateTime.Now);
EndPoint remoteEP = clientSocket.RemoteEndPoint;
buffer = new byte[1024];
clientSocket.ReceiveFrom(buffer, SocketFlags.None, ref remoteEP);
string request = Encoding.UTF8.GetString(buffer);
System.Diagnostics.Trace.Write(request);
buffer = Encoding.UTF8.GetBytes("<html><head><link rel=\"icon\" href=\"data:;base64,=\"></head><body></body></html>");
clientSocket.Send(buffer);
clientSocket.Close();
clientSocket.Dispose();
}
catch { }
}
while (this.isRunning);
This sort of works, however two issues I have noted, is that the clientSocket local end-point is on the same port as the servSocket. With the built-in HttpListener, the request gets handled by a random local end-point port. How can I mimic this?
I have set the ExclusiveAddressUse flag to false, however I still cannot bind more than one socket to that particular end-point, so what is its exact use?
Furthermore, from time to time I get a SocketException with 'An established connection was aborted by the software in your host machine'. What could be the source of the problem?
The simple answer is "you don't really care about that".
TCP works by estabilishing a two-way "virtual" persistent connection. To achieve this illusion, it uses a separate port for communication with a given client. You still connect to port 80 (for example), but a thousand clients use a thousand different ports for the actual communication with the server.
ExclusiveAddressUse allows you to bind more sockets to the same port - but each has to have its own IP address.
Networking is hard. Us as high an abstraction as you can, be it TcpListener, HttpListener or even OWIN.
I am making an Win RT app that connects to a desktop app and they start to communicate in UDP and TCP.
I have successfully implemented TCP communication in that I can send from Win RT to Desktop and send from Desktop to Win RT. using StreamSocket on Win RT and TcpListener on desktop.
I also made it to send Udp data from Win RT to desktop without any problem. But I can't receive data's sent from desktop to Win RT. I use the following code and I don't see any problem with that but there must something.
var g = new DatagramSocket();
g.MessageReceived += g_MessageReceived;
g.BindEndpointAsync(new HostName("127.0.0.1"), "6700");
.
.
.
void g_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
{ // <- break point here.
}
That break point never stops the code which means it never gets a message.
I can only think of IBuffer because on my StreamSocket I should get the bytes by reader.GetBuffers() and not reader.GetBytes(). However that's the thing I need to think about on the Win RT and not Desktop. because on Tcp I just send bytes and I get buffers in Win RT so the same should happen for DatagramSocket as well.
reader = DataReader
Thank you guys.
I'm not familiar with the new DatagramSocket class, but usually binding to 127.0.0.1 means that you will only receive messages sent to the loopback adapter. Since your packets are coming from another host, they should be received on a NIC, not the loopback adapter.
Edit: From looking at the documentation for the DatagramSocket API that you're using, you can just use the BindServiceNameAsync() method instead of BindEndpointAsync() in order to bind to the specified port on all adapters, which is the same behavior as my System.Net.Sockets API example below. So, in your example, you'd have:
g.BindServiceNameAsync("6700");
Of course, you'll also want to make sure your firewall settings on the desktop host allow it to listen for incoming UDP packets on the specified port.
Try the following code:
using System.Net;
using System.Net.Sockets;
public class UdpState
{
public UdpClient client;
public IPEndPoint ep;
}
...
private void btnStartListener_Click(object sender, EventArgs e)
{
UdpState state = new UdpState();
//This specifies that the UdpClient should listen on EVERY adapter
//on the specified port, not just on one adapter.
state.ep = new IPEndPoint(IPAddress.Any, 31337);
//This will call bind() using the above IP endpoint information.
state.client = new UdpClient(state.ep);
//This starts waiting for an incoming datagram and returns immediately.
state.client.BeginReceive(new AsyncCallback(bytesReceived), state);
}
private void bytesReceived(IAsyncResult async)
{
UdpState state = async.AsyncState as UdpState;
if (state != null)
{
IPEndPoint ep = state.ep;
string msg = ASCIIEncoding.ASCII.GetString(state.client.EndReceive(async, ref ep));
//either close the client or call BeginReceive to wait for next datagram here.
}
}
Note that in the above code, you should obviously use whatever encoding you're sending the string across with. When I wrote that test app, I sent the string in ASCII. If you're sending it in Unicode, just use UnicodeEncoding.Unicode instead of ASCIIEncoding.ASCII.
If none of this works, you might want to break out a packet capture utility like Wireshark to make sure that the UDP packet from the RT host is, in fact, getting to the desktop host.
What would be a simple design pattern for sharing a COM port over TCP to multiple clients?
For example, a local GPS device that could transmit co-ordinates to remote hosts in realtime.
So I need a program that would open the serial port and accept multiple TCP connections like:
class Program
{
public static void Main(string[] args)
{
SerialPort sp = new SerialPort("COM4", 19200, Parity.None, 8, StopBits.One);
Socket srv = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
srv.Bind(new IPEndPoint(IPAddress.Any, 8000));
srv.Listen(20);
while (true)
{
Socket soc = srv.Accept();
new Connection(soc);
}
}
}
I would then need a class to handle the communication between connected clients, allowing them all to see the data and keeping it synchronized so client commands are received in sequence:
class Connection
{
static object lck = new object();
static List<Connection> cons = new List<Connection>();
public Socket socket;
public StreamReader reader;
public StreamWriter writer;
public Connection(Socket soc)
{
this.socket = soc;
this.reader = new StreamReader(new NetworkStream(soc, false));
this.writer = new StreamWriter(new NetworkStream(soc, true));
new Thread(ClientLoop).Start();
}
void ClientLoop()
{
lock (lck)
{
connections.Add(this);
}
while (true)
{
lock (lck)
{
string line = reader.ReadLine();
if (String.IsNullOrEmpty(line))
break;
foreach (Connection con in cons)
con.writer.WriteLine(line);
}
}
lock (lck)
{
cons.Remove(this);
socket.Close();
}
}
}
The problem I'm struggling to resolve is how to facilitate communication between the SerialPort instance and the threads.
I'm not certain that the above code is the best way forward, so does anybody have another solution (the simpler the better)?
Why write at such a low-level (sockets)? Why not use WCF as the communication between the clients and the server and present a cleaner, strongly-typed interface instead of raw access to the GPS device?
Devices like this are often best managed independently from the clients calling in - i.e. you have your own separate thread that talks to the GPS device, polling it at the appropriate interval and populating shared data structures with the current location - while the clients make service calls and are supplied with data from the shared data structures. All error handling and recovery for the sometimes unreliable device connection is handled by the GPS thread and the clients don't need to each get involved with such nastiness. They can make non-blocking calls to get status updates and those updates might include a status 'position unavailable' while the GPS thread is frantically trying to re-establish communication.
So I would create a service that abstracts the particulars of dealing with this specific device and provides a clean interface to the clients. It might for example offer a services like GetPosition() which returns some class like "GeoCoordinate". That way if you ever need to support other location sensing devices you can add them without making any changes to the client code.
GPS <--Serial--> Server <--WCF--> Clients
I have a system that communicates with hundreds of different devices, many over serial ports and other semi-reliable connections and this is the approach I use. See http://blog.abodit.com.
----- per your additional requirement to use TELNET: maybe something like:
Create a thread that handles all communication with the device itself.
Create a class that encapsulates a single WorkItem - what to send, the response, and a WaitHandle.
Use a Queue to queue up requests from clients. Each client waits on the WaitHandle for its response to be ready.
Let the single communication thread pull work items off that queue, send them to the GPS device, get the response, store the response in the WorkItem (or set a flag for failures), and then set the wait handle to say that the WorkItem is done.
If the requests come in faster than the GPS can handle, add code so it can return cached values for requests coming within a small time window from the last successful request to the device.
In effect you are now presenting a virtual GPS device to all the clients but internally you are serializing all their requests (on a Queue) and managing communication with the GPS device on a single thread so you can do the Request-Response cycle easily without interference.
This also allows you to time-out nicely (on the wait handle) to inform a client that no response is currently available.
you have socat and ser2net and other programs but my experience is very bad... not working properly. I've done this small python program, can be useful. Update port, baudrate... then use any tcp client. Remove first line if don't want to use is as auto executable script
#!/usr/bin/python
import socket
import sys
import serial
#open serial port
ser = serial.Serial('/dev/ttyAMA0', 115200, timeout=0)
#create socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
#bond to the port. Don't use localhost to accept external connections
server_address = ('', 2105)
print('starting up on {} port {}'.format(*server_address))
sock.bind(server_address)
#listen
sock.listen(1)
#loop
while True:
#waits for a new connection
print('waiting for a connection')
connection, client_address = sock.accept()
try:
print('connection from', client_address)
#continously send from serial port to tcp and viceversa
connection.settimeout(0.1)
while True:
try:
data = connection.recv(16)
if data == '': break
ser.write(data)
except KeyboardInterrupt:
connection.close()
sys.exit()
except Exception as e:
pass
received_data = ser.read(ser.inWaiting())
connection.sendall(received_data)
except Exception as e:
print e
finally:
#clean up connection
connection.close()