如您所要求,在客户端可以按照这篇文章中所建议的做以下操作,不过您可能需要考虑将同步的Send
部分改为异步的Send
,像这样:
clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket)
而您的回调函数可能如下所示:
private static void endSendCallback(IAsyncResult ar) {
try {
SocketError errorCode;
int result = clientSocket.EndSend(ar, out errorCode);
Console.WriteLine(errorCode == SocketError.Success ?
"Successful! The size of the message sent was :" + result.ToString() :
"Error with error code: " + errorCode.ToString()
);
} catch (Exception e) {
Console.WriteLine("Unhandled EndSend Exception! " + e.ToString());
}
}
针对以上更改后的完整客户端代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace TcpClientConsoleApplication {
class Program {
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static Socket clientSocket;
static void Main(string[] args) {
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
loopConnect(3, 3);
string result = "";
do {
result = Console.ReadLine();
if (result.ToLower().Trim() != "exit") {
byte[] bytes = Encoding.ASCII.GetBytes(result);
clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket);
}
} while (result.ToLower().Trim() != "exit");
}
private static void endSendCallback(IAsyncResult ar) {
try {
SocketError errorCode;
int result = clientSocket.EndSend(ar, out errorCode);
Console.WriteLine(errorCode == SocketError.Success ?
"Successful! The size of the message sent was :" + result.ToString() :
"Error with error code: " + errorCode.ToString()
);
} catch (Exception e) {
Console.WriteLine("Unhandled EndSend Exception! " + e.ToString());
}
}
static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
int attempts = 0;
while (!clientSocket.Connected && attempts < noOfRetry) {
try {
++attempts;
IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
} catch (Exception e) {
Console.WriteLine("Error: " + e.ToString());
}
}
if (!clientSocket.Connected) {
Console.WriteLine("Connection attempt is unsuccessful!");
return;
}
}
private const int BUFFER_SIZE = 4096;
private static byte[] buffer = new byte[BUFFER_SIZE];
private static void endConnectCallback(IAsyncResult ar) {
try {
clientSocket.EndConnect(ar);
if (clientSocket.Connected) {
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
} else {
Console.WriteLine("End of connection attempt, fail to connect...");
}
} catch (Exception e) {
Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
}
}
const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0;
private static void receiveCallback(IAsyncResult result) {
System.Net.Sockets.Socket socket = null;
try {
socket = (System.Net.Sockets.Socket)result.AsyncState;
if (socket.Connected) {
int received = socket.EndReceive(result);
if (received > 0) {
receiveAttempt = 0;
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, data.Length);
Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) {
++receiveAttempt;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else {
Console.WriteLine("receiveCallback is failed!");
receiveAttempt = 0;
clientSocket.Close();
}
}
} catch (Exception e) {
Console.WriteLine("receiveCallback is failed! " + e.ToString());
}
}
}
}
上述代码的完整解释(注意异步
Send
时进行了更改)如下所示(第7点已更改,第8点已添加):
客户端:
Similarly, put the Socket
class in the class context rather than method context and initialize it as soon as you start your program
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static Socket clientSocket;
static void Main(string[] args) {
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
Then start to connect by ASync
BeginConnect
. I would normally go further by LoopConnect
just for failure handling like this.
static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
int attempts = 0;
while (!clientSocket.Connected && attempts < noOfRetry) {
try {
++attempts;
IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnect, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
} catch (Exception e) {
Console.WriteLine("Error: " + e.ToString());
}
}
if (!clientSocket.Connected) {
Console.WriteLine("Connection attempt is unsuccessful!");
return;
}
}
Similar concept to what you do to the server BeginAccept
you need to define endConnectCallback
for the ASync
BeginConnect
you use. But here, unlike server which needs to re-calling BeginAccept
, once you are connected, you do not need to do any new BeginConnect
since you only need to be connected once.
You may want to declare buffer
etc. Then, after you connect, don't forget the next ASync
BeginReceive
to handle the message retrieval part (similar with the server)
private const int BUFFER_SIZE = 4096;
private static byte[] buffer = new byte[BUFFER_SIZE];
private static void endConnectCallback(IAsyncResult ar) {
try {
clientSocket.EndConnect(ar);
if (clientSocket.Connected) {
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
} else {
Console.WriteLine("End of connection attempt, fail to connect...");
}
} catch (Exception e) {
Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
}
}
Naturally, you need to define your receiveCallback
, just like what you did for the server. And yes, it is as you have guessed, it is almost identical to what you did for the server!
You can do anything you want with your data. Note that the data you receive is actually in byte[]
, not string
. So you can do anything with it. But for example's sake, I will just use string
to display.
const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0;
private static void receiveCallback(IAsyncResult result) {
System.Net.Sockets.Socket socket = null;
try {
socket = (System.Net.Sockets.Socket)result.AsyncState;
if (socket.Connected) {
int received = socket.EndReceive(result);
if (received > 0) {
receiveAttempt = 0;
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, data.Length);
Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) {
++receiveAttempt;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else {
Console.WriteLine("receiveCallback is failed!");
receiveAttempt = 0;
clientSocket.Close();
}
}
} catch (Exception e) {
Console.WriteLine("receiveCallback is failed! " + e.ToString());
}
}
And next one (before the very last) -- Yes, again, as you have already guessed, you just need to do something on your main routine - suppose you want to use it to BeginSend
data. Because you use Console
but you want it to send things as byte[]
, you need to do the conversion (see the explanation in server 9 of the linked post).
static void Main(string[] args) {
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
loopConnect(3, 3);
string result = "";
do {
result = Console.ReadLine();
if (result.ToLower().Trim() != "exit") {
byte[] bytes = Encoding.ASCII.GetBytes(result);
clientSocket.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, endSendCallback, clientSocket);
}
} while (result.ToLower().Trim() != "exit");
}
And finally, at the very very last, you only need to declare the async EndSend
callback function and you are done!
private static void endSendCallback(IAsyncResult ar) {
try {
SocketError errorCode;
int result = clientSocket.EndSend(ar, out errorCode);
Console.WriteLine(errorCode == SocketError.Success ?
"Successful! The size of the message sent was :" + result.ToString() :
"Error with error code: " + errorCode.ToString()
);
} catch (Exception e) {
Console.WriteLine("Unhandled EndSend Exception! " + e.ToString());
}
}