I'm struggling with some piece of simple code. However, I can't get it done. I have this server, which must accept connections from multiple clients (asynchronously, obviously). So, I have:
IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 8082);
TcpListener tcpListener = new TcpListener(IPAddress.Any, 8082);
server = tcpListener.Server;
server.Bind(ipEndPoint);
server.Listen(4);
server.BeginAccept(new AsyncCallback(beginConnection), server);
And,
static void beginConnection(IAsyncResult iar)
{
Console.WriteLine("Client connected");
Socket s = (Socket)iar.AsyncState;
server = s.EndAccept(iar);
server.Listen(4);
server.BeginAccept(beginConnection, s);
}
Then, when I try to connect myself, the first client works OK. It just sends a message to this server, and the server sends it back to the client. The server functions as an echo. But when I try to connect another clients it doesn't work. I have also put the Console.WriteLine("Client connected"), but the server doesn't write anything.
How can I fix this problem?
I think I'm not passing the right parameter to the first BeginAccept method. Instead of server socket, I should be passing the tcpListener.
Then I would have:
static void beginConnection(IAsyncResult iar)
{
Console.WriteLine("Client connected");
TcpListener tcpListener = (TcpListener)iar.AsyncState;
Socket s = tcpListener.Server.EndAccept(iar);
tcpListener.Server = s; // But I would have this error
server.Listen(2);
server.BeginAccept(beginConnection, s);
}
But I would have the error that I marked it up. In the first version nothing is modified, so I think this it's the issue in the first version of the code.
First, you do not need the Server property of the TcpListener. You should simply call Start(), even without Bind.
Second, EndAccept() is also should be called at TcpListener and return a TcpClient instance which you must use for sending and receiving data.
A simple example of what I just said might be presented as:
{
TcpListener listener = new TcpListener(IPAddress.Any, 8082);
listener.Start();
AcceptClient();
}
void AcceptClient()
{
listener.BeginAccept(ClientConnected, null);
}
void ClientConnected(IAsyncResult ar)
{
TcpClient client = listener.EndAccept();
AcceptClient();
// Now you can send or receive data using the client variable.
}
Related
I am a C#/WPF developer and have not many experience in network-communication (socket/tcp) and just trying to get a simple working example for a TCP-Listener-project for exchanging messages between server and client.
I think Im nearly there where I want to come to, but have an open issue left: When I start the server and after that the client, the messages get exchanged like wanted. After that I close the client and restart it, the second connection gets refused (at "await client.ConnectAsync(ipEndPoint)") with following message (sorry, its translated from german to english):
"A connection could not be established because the target computer refused the connection"
Do you maybe have any hint for me, what I am doing wrong? I also tried closing clients connection in the client app, but same behaviour.
Server-code:
public async Task<bool> StartServer()
{
IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint ipEndPoint = new(ipAddress, 8888);
using Socket listener = new(
ipEndPoint.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
listener.Bind(ipEndPoint);
listener.Listen(100);
Socket client = await listener.AcceptAsync();
while (true)
{
var buffer = new byte[1_024];
var received = client.Receive(buffer, SocketFlags.None);
var response = Encoding.UTF8.GetString(buffer, 0, received);
var eom = "<|EOM|>";
if (response.IndexOf(eom) > -1 /* is end of message */)
{
AddLogText($"Socket server received message: \"{response.Replace(eom, "")}\"");
var ackMessage = "Hallo Client!<|ACK|>";
var echoBytes = Encoding.UTF8.GetBytes(ackMessage);
await client.SendAsync(echoBytes, 0);
AddLogText($"Socket server sent acknowledgment: \"{ackMessage}\"");
break;
}
}
client.Shutdown(SocketShutdown.Both);
client.Close();
return true;
}
Client Code:
private async Task<bool> StartClient()
{
IPHostEntry ipHostInfo = await Dns.GetHostEntryAsync("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint ipEndPoint = new(ipAddress, 8888);
using Socket client = new(
ipEndPoint.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
await client.ConnectAsync(ipEndPoint);
while (true)
{
var message = "Hallo Server?<|EOM|>";
var messageBytes = Encoding.UTF8.GetBytes(message);
_ = await client.SendAsync(messageBytes, SocketFlags.None);
Console.WriteLine($"Socket client sent message: \"{message}\"");
var buffer = new byte[1_024];
var received = await client.ReceiveAsync(buffer, SocketFlags.None);
var response = Encoding.UTF8.GetString(buffer, 0, received);
if (response.Contains("<|ACK|>"))
{
Console.WriteLine($"Socket client received acknowledgment: \"{response}\"");
break;
}
}
return true;
}
Your StartServer method returns after the first client socket finishes, disposing the listening socket.
Usually, you want to keep the listening socket open and listening, and call AcceptAsync in an infinite loop.
But I have to caution you: socket programming is extremely difficult. For example, your socket reading code is far too simplistic to properly handle message framing. I recommend self-hosting ASP.NET or something else that handles the protocol-level details for you.
You might have misunderstood how the server works. It should be like this:
Create a socket and call Bind and Listen. This part is okay.
Call Accept, which waits for a client to connect, and then returns a socket connected to that specific client.
Send and receive data from the client.
If you want to wait for another client, after you are done with the first one, then of course you need to call Accept again.
It is also common for servers to handle several clients at the same time, for example by using threads (or async). One thread just calls Accept over and over, and whenever a new client connects, it starts a new thread to handle that client. You don't need this for a toy program, that only handles one connection at a time. But you do need it for a real server, otherwise one client can connect, and be really slow, and your server won't handle any other clients while that is happening.
If you are not doing a "graceful close" there is no need to call Shutdown - you can just Close the client's socket to disconnect the client. When you Close the main server socket you stop accepting new connections. Shutdown doesn't apply to the main server socket.
I have two servers and a client. One server is on the same computer where the client is. I need to disconnect from the local server and connect to the remote one.
AutoResetEvent disconnectDone = new AutoResetEvent(false);
IPEndPoint localEndPoint = new IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList[0], PORT);
Socket socket;
// somewhere I initialize the socket and connect to the local end point
public void someButton_Click(object sender, EventArgs e)
{
string IP = someTextBox.Text;
if (socket.Connected)
{
socket.Shutdown(SocketShutdown.Both);
socket.BeginDisconnect(true, DisconnectCallback, socket);
disconnectDone.WaitOne();
}
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Parse(IP), PORT);
socket.BeginConnect(remoteEndPoint, ConnectCallback, socket);
}
private void DisconnectCallback(IAsyncResult AR)
{
Socket socket = (Socket)AR.AsyncState;
socket.EndDisconnect(AR);
disconnectDone.Set();
}
It freezes at the line with the WaitOne method because DisconnectCallback doesn't answer.
If in the BeginDisconnect method I change true to false then it "works". But further BeginConnect gives me an exception that the socket is still connected.
I really do not understand how all these disconnect things work. Or maybe I'm wrong with those thread methods (WaitOne and Set). Please help!
I'm working with UDP and i was wondering about the Accept Method when multiple machine need to connect to a server. So far i was working with UDPCliente class, IPEndPoint class and BeginRecieve / EndRecieve Method to create a server where multiple machine can connect at the same time.
My question is simple do i need to use Accept Method to handler incoming connection and create a new socket for each new connection ?
What is the best way to handler multiple connection with UDP ?
The code samples i've seen so far create a new UDPClient class and a IPEndPoint where the server is listening for connections after that, the code call the BeginRecieve passing a function where the data is recieved and then starts the process of BeginRecieve again.
These are the code samples i've been using so far:
public static void receiveCallback(IAsyncResult ar)
{
UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
byte[] receiveBytes = u.EndReceive(ar, ref e);
UdpState s = new UdpState();
s.e = e;
s.u = u;
u.BeginReceive(new AsyncCallback(receiveCallback), s);
}
public static void receiveMessages()
{
IPEndPoint e = new IPEndPoint(IPAddress.Any, 5050);
UdpClient u = new UdpClient(e);
UdpState s = new UdpState();
s.e = e;
s.u = u;
u.BeginReceive(new AsyncCallback(receiveCallback), s);
}
UDP is connectionless, so there's nothing to accept. If you need connections over UDP, then you have to implement them. Ideally, you would assign each "connection" some kind of identifier and include it in each datagram sent to the server. In some cases, it may be sufficient just to rely on the IP address and port to identify "connections".
But you can do it however you want. UDP treats each datagram as independent.
Short answer - no, you don't use accept() with UDP.
In UDP there are no connections, only datagrams. One side sends them and the other might receive them. Each datagram has information about the sender (IP address and port) that your server app can extract to differentiate the clients.
I am currently using this function to send data from the server to the clients
private static void send_message(string ip, string message)
{
byte[] packetData = System.Text.UTF8Encoding.UTF8.GetBytes(message);
int port = 11000;
IPEndPoint ep = new IPEndPoint(IPAddress.Parse(ip), port);
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
client.SendTo(packetData, ep);
}
But this would mean a destination IP/port can only have one client open to receive the data, because having two clients open would mean one client can retrieve data that was meant for another (if I'm correct).. how do I solve this?
Receiving function:
private static Int32 port = 11000;
private static UdpClient udpClient = new UdpClient(port);
public static void receive_threaded()
{
Thread t = new Thread(() =>
{
while (true)
{
IPEndPoint remoteIPEndPoint = new IPEndPoint(IPAddress.Any, port);
byte[] content = udpClient.Receive(ref remoteIPEndPoint);
if (content.Length > 0)
{
string message = Encoding.UTF8.GetString(content);
parseMessage(message);
}
}
});
t.Start();
}
1) You should implement some kind of protocol so that your server has a "well known" port to accept connections. Use this port to inform your client ANOTHER port where the client must connect. Use a different port for each client.
Your client conects to the server at 11000. Your server assigns a unique port for the client, let's say 11001 for the firts client. Then the server opens a connection at 11001. The client closes connection at 11000 and opens a new connection at 11001 to receive the data.
2) Why UDP?
I don't see why you need to open a new socket at all. You already have each client's address and port, from the first packet they sent you. Just send a packet to that address:port. I absolutely don't get the other suggestion of setting up extra ports either.
I wrote a TCP server to use the BeginAccept/EndAccept pattern. With this, I coded up a simple UnitTest using a TcpClient, and measured each portion. All tests are localhost, so I am surprised to see that TCP connection is consistently taking 1 second. I have set the Socket.NoDelay = true although I believe this only affects Send/Receive. I am not receiving the first packet of data. Any help or ideas on speed this up are appreciated.
Note: I can not change the client side to keep the connection open, and I need to be able to handle a lot of requests per second if possible.
Server Side:
public void Start()
{
System.Net.IPHostEntry localhost = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
System.Net.IPEndPoint endpoint;
int port = Properties.Settings.Default.ListenPort;
//
// Setup the connection to listen on the first IP, and specified port
//
endpoint = new IPEndPoint(IPAddress.Any, port);
listenSocket = new Socket(endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listenSocket.Bind(endpoint);
listenSocket.NoDelay = true; // do not wait 200 milliseconds for new data to be buffered on the connection
listenSocket.Listen(int.MaxValue);
Console.WriteLine("Listening on {0}:{1}", localhost.AddressList[0], port);
//
// Post the accept. The accept method will continuously post a new accept as soon as a connection is made
//
while (true)
{
accepted.Reset();
Connection connection = connections.Pop();
listenSocket.BeginAccept(AcceptCallback, connection);
accepted.WaitOne();
}
}
private static void AcceptCallback(IAsyncResult ar)
{
accepted.Set();
Connection connection = ar.AsyncState as Connection;
Socket remoteSocket = null;
try
{
remoteSocket = listenSocket.EndAccept(ar);
remoteSocket.NoDelay = true;
connection.RemoteSocket = remoteSocket;
//
// Start the Receive cycle
//
Receive(connection);
}
catch (SocketException)
{
Disconnect(connection);
}
}
Simple Test Client:
[TestMethod()]
public void ClientTest()
{
TestContext.BeginTimer("Connection");
TcpClient client = new TcpClient("localhost", 10300);
NetworkStream stream = client.GetStream();
TestContext.EndTimer("Connection");
...
Using a LoadTest I loaded 25 users, and the Transaction "Connection" always takes above 1 second.
Not sure why, but simply changing this:
TestContext.BeginTimer("Connection");
TcpClient client = new TcpClient("localhost", 10300);
TestContext.EndTimer("Connection");
To this:
TestContext.BeginTimer("Connection");
TcpClient client = new TcpClient();
client.Connect("localhost", 10300);
TestContext.EndTimer("Connection");
Drops the time from 1 second to .13 seconds. Will have to investigate as to why, but hopefully this will help somebody out in the future.
When you attempt to connect using the TcpClient constructor on a host that resolves to both Ipv6 and Ipv4 addresses, the connection using Ipv6 is attempted first. If it fails then it attempts to connect using the Ipv6 address. This is the cause of the 1 second delay. Here is the MSDN link: