我正在尝试连接到远程服务器,登录,然后使用 C# 表单应用程序向它发送数据并从它接收数据。最初我写了一个控制台应用程序,效果很好,但是我需要使用表单。问题是表单应用程序中的我BeginSend
和BeginReceive
方法不起作用。
当我的套接字第一次连接到远程服务器(成功)时,我收到一条来自远程服务器的消息,并且我的ConnectionStatusTest
节目我仍然连接。但似乎在我向它发送数据之前连接就被断开了。来自服务器的原始消息再次显示(而不是我想要接收的新消息),然后显示SendDataException
和ReceiveDataException
消息框,并ConnectionStatusTest
告诉我我不再连接。
这是我的代码:
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ServerForm
{
public partial class LoginForm : Form
{
public LoginForm()
{
InitializeComponent();
}
Socket tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
public class StateObject
{
public Socket clientSocket = null;
public const int recvBufferSize = 1024;
public byte[] recvbuffer = new byte[recvBufferSize];
public StringBuilder sb = new StringBuilder();
}
private static ManualResetEvent connectionCompleted = new ManualResetEvent(false);
private static ManualResetEvent sendCompleted = new ManualResetEvent(false);
private static ManualResetEvent recvCompleted = new ManualResetEvent(false);
private static String response = String.Empty;
private void LoginForm_Shown(object sender, EventArgs e)
{
try
{
IHostEntry ipHost = Dns.GetHostEntry("*server's web address*");
IPAddress serverIP = ipHost.AddressList[0];
int port = 7500;
IPEndPoint remoteEP = new IPEndPoint(serverIP, port);
tcpSocket.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), tcpSocket);
connectionCompleted.WaitOne();
tcpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
//tcpSocket.IOControl(IOControlCode.KeepAliveValues, inValue, outValue);
AsyncSendReceiveData.ReceiveData(tcpSocket);
AsyncSendReceiveData.recvCompleted.WaitOne();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "LoginForm_Shown Exception");
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
Socket tcpSocket = (Socket)ar.AsyncState;
tcpSocket.EndConnect(ar);
if (tcpSocket.Connected == true)
{
MessageBox.Show(String.Format("Socket connected to server ({0})",
tcpSocket.RemoteEndPoint.ToString()));
}
connectionCompleted.Set();
}
catch (Exception e)
{
MessageBox.Show(e.Message, "ConnectCallback Exception");
}
}
private void passText_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)13)
{
try
{
string usernameString = userText.Text.ToUpper();
string passwordString = passText.Text.ToUpper();
string loginDetails = String.Format("LOGIN {0}:{1}",
usernameString, passwordString);
string data = loginDetails;
AsyncSendReceiveData.SendData(tcpSocket, data);
AsyncSendReceiveData.sendCompleted.WaitOne();
AsyncSendReceiveData.ReceiveData(tcpSocket);
AsyncSendReceiveData.recvCompleted.WaitOne();
ConnectionStatusTest();
string versionString = "VERSION 3";
data = versionString;
AsyncSendReceiveData.SendData(tcpSocket, data);
AsyncSendReceiveData.sendCompleted.WaitOne();
AsyncSendReceiveData.ReceiveData(tcpSocket);
AsyncSendReceiveData.recvCompleted.WaitOne();
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message, "KeyPress Exception");
}
}
}
public class AsyncSendReceiveData
{
public static ManualResetEvent sendCompleted = new ManualResetEvent(false);
public static ManualResetEvent recvCompleted = new ManualResetEvent(false);
public static void ReceiveData(Socket tcpSocket)
{
try
{
StateObject stateobj = new StateObject();
stateobj.clientSocket = tcpSocket;
tcpSocket.BeginReceive(stateobj.recvbuffer, 0,
StateObject.recvBufferSize, 0,
new AsyncCallback(ReceiveCallback), stateobj);
}
catch (SocketException e)
{
MessageBox.Show(e.Message + "\n" + "Exception occurred" +
e.StackTrace.ToString(), "ReceiveData Exception");
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
StateObject stateobj = (StateObject)ar.AsyncState;
Socket tcpSocket = stateobj.clientSocket;
int bytesRead = tcpSocket.EndReceive(ar);
if (bytesRead > 0) //This if statement seems to cause slow connection.
{
//Adds more data received to sb
stateobj.sb.Append(Encoding.ASCII.GetString(stateobj.recvbuffer,
0, bytesRead));
//Get the rest of the data, if there is any more.
tcpSocket.BeginReceive(stateobj.recvbuffer, 0,
StateObject.recvBufferSize, 0,
new AsyncCallback(ReceiveCallback), stateobj);
}
else
{
if (stateobj.sb.Length > 1)
{
response = stateobj.sb.ToString();
}
recvCompleted.Set();
MessageBox.Show(response);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message, "ReceiveCallback Exception");
}
}
public static void SendData(Socket tcpSocket, String data)
{
try
{
byte[] sendDataBytes = Encoding.ASCII.GetBytes(data);
tcpSocket.BeginSend(sendDataBytes, 0, sendDataBytes.Length,
SocketFlags.None, new AsyncCallback(SendCallback), tcpSocket);
}
catch (SocketException e)
{
MessageBox.Show(e.Message + "\n" + "Exception occurred" +
e.StackTrace.ToString(), "SendData Exception");
}
}
private static void SendCallback(IAsyncResult ar)
{
try
{
Socket tcpSocket = (Socket)ar.AsyncState;
tcpSocket.EndSend(ar);
sendCompleted.Set();
}
catch (Exception e)
{
MessageBox.Show(e.Message, "SendCallback Exception");
}
}
}
public void ConnectionStatusTest()
{
try
{
byte[] tmp = new byte[1];
bool blockingState = tcpSocket.Blocking;
tcpSocket.Blocking = false;
tcpSocket.Send(tmp, 0, 0, SocketFlags.None);
}
catch (SocketException SockEx)
{
if (SockEx.NativeErrorCode.Equals(10035))
MessageBox.Show("Still connected, but the send would block");
else
{
MessageBox.Show(String.Format("Disconnected: error code {0}",
SockEx.NativeErrorCode.ToString()));
}
}
finally
{
bool blockingState = tcpSocket.Blocking;
}
MessageBox.Show(String.Format("Connected: {0}",
tcpSocket.Connected.ToString()));
我花了很多时间试图弄清楚为什么 BeginSend 和 BeginReceive 方法不起作用,我认为我已经将其缩小到以下两件事之一:
在
passText_KeyPress
函数中, 的data
部分string data = loginDetails
不作为参数传递给SendData
函数。因此该SendData
功能无法正常工作并SendDataException
显示 。如果这是问题所在,我如何将loginDetails
(或其他字符串)传递给SendData
函数?使用控制台应用程序连接时,连接、登录或发送或接收数据都没有问题,并且连接不会断开。但是,当我尝试使用 telnet 进行连接时,在大约 30 秒不活动后从服务器返回一条消息,说“与主机的连接丢失”。在我的表单应用程序中,连接似乎也在大约 30 秒后断开,并且由于某种原因,表单应用程序需要大约 25 秒来告诉我我已连接。我曾尝试在表单应用程序中使用 KeepAlive,但显然默认的 TCP KeepAlive 期限为 2 小时。如果这是问题所在,我该如何将其更改为 20 秒?我读过的所有内容都建议让 Visual Studio 通过我的防火墙(我试过这个 - 没有运气),或者必须更改注册表项,
错误消息是“已建立的连接被主机中的软件中止”,错误代码是 10053,这让我觉得我不知何故超时了。我当然可能是错的,问题可能是我没有发现的其他问题。我将衷心感谢您的帮助!谢谢。
更新- 我已经使用同步方法重写了代码(并添加了一个计时器,这样我就可以每 5 秒查看一次是否仍然连接)。我现在得到两个不同的错误。
第一个说“无法立即完成非阻塞套接字操作”并产生错误代码 10035。Receive
当我仍然连接到远程服务器时,该方法会发生此错误。我认为清除缓冲区可能会有所帮助,但这只是意味着我下次尝试阅读它时会收到一条空白消息。所以我只能认为我实际上并没有收到来自服务器的新消息,即使我知道我应该这样做。
第二个是原始错误 - “已建立的连接被主机中的软件中止”,错误代码 10053 -Send
当我在 30 秒后断开连接时发生这种情况。我已经在不同的地方读到更改Socket.Blocking
属性可能会有所帮助,但我已经尝试在调用该方法之前进行它true
,但都没有帮助。false
Receive
关于如何让它发挥作用的任何想法?
修改后的代码:
namespace ServerForm
{
public partial class LoginForm : Form
{
public LoginForm()
{
InitializeComponent();
}
Socket tcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
byte[] rcvdBytes = new byte [1024];
System.Timers.Timer timer1 = new System.Timers.Timer(5000);
private void LoginForm_Shown(object sender, EventArgs e)
{
try
{
timer1.Enabled = true;
timer1.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer1.AutoReset = true;
IPHostEntry ipHost = Dns.GetHostEntry("*server's web address*");
IPAddress serverIP = ipHost.AddressList[0];
int port = 7500;
IPEndPoint remoteEP = new IPEndPoint(serverIP, port);
tcpSocket.Connect(remoteEP);
tcpSocket.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.KeepAlive, true); //Has no effect, as I get
//disconnected after 30 seconds.
if (tcpSocket.Connected == true)
{
MessageBox.Show(String.Format("Socket connected to primary
server {0}", tcpSocket.RemoteEndPoint.ToString()));
}
int bytesRcvd = tcpSocket.Receive(rcvdBytes);
MessageBox.Show(String.Format("Response received: {0}",
Encoding.ASCII.GetString(rcvdBytes, 0, rcvdBytes.Length),
"Connected to server"));
Array.Clear(rcvdBytes, 0, rcvdBytes.Length);
}
catch (Exception ex)
{
MessageBox.Show(String.Format(ex.Message, "LoginForm_Shown Exception"));
}
}
private void passText_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)13)
{
try
{
string usernameString = userText.Text.ToUpper();
string passwordString = passText.Text.ToUpper();
string loginDetails = String.Format("LOGIN {0}:{1}",
usernameString, passwordString);
byte[] loginBytes = Encoding.ASCII.GetBytes(loginDetails);
tcpSocket.Send(loginBytes); //SocketException (0x80004005):
//"An established connection was aborted
//by the software in your host machine"
//occurs here once disconnected. Error code 10053
int bytesRcvd = tcpSocket.Receive(rcvdBytes); //SocketException
//(0x80004005): "A non-blocking socket operation could not be
//completed immediately" occurs here while connected.
//Error code 10035
MessageBox.Show(String.Format("Response received: {0}",
Encoding.ASCII.GetString(rcvdBytes, 0, rcvdBytes.Length)));
//Displays same welcome message from server, or nothing if
//I clear the rcvdBytes buffer first, and not a new message.
string versionString = "VERSION 3";
byte[] versionBytes = Encoding.ASCII.GetBytes(versionString);
tcpSocket.Send(versionBytes);
bytesRcvd = tcpSocket.Receive(rcvdBytes);
MessageBox.Show(String.Format("Response received: {0}",
Encoding.ASCII.GetString(rcvdBytes, 0, rcvdBytes.Length)));
ConnectionStatusTest();
}
catch (SocketException sockEx)
{
MessageBox.Show(String.Format("SocketException: {0}",
sockEx.ToString()));
}
catch (ArgumentNullException argNullEx)
{
MessageBox.Show(String.Format("ArgumentNullException: {0}",
argNullEx.ToString()));
}
catch (Exception ex)
{
MessageBox.Show(String.Format("Exception: {0}", ex.ToString()));
}
}
}
public void ConnectionStatusTest()
{
try
{
byte[] tmp = new byte[1];
bool blockingState = tcpSocket.Blocking;
tcpSocket.Blocking = false;
tcpSocket.Send(tmp, 0, 0, SocketFlags.None);
}
catch (SocketException SockEx)
{
if (SockEx.NativeErrorCode.Equals(10035))
MessageBox.Show("Still connected, but the send would block");
else
{
MessageBox.Show(String.Format("Disconnected: error code {0}",
SockEx.NativeErrorCode.ToString()));
}
}
finally
{
bool blockingState = tcpSocket.Blocking;
}
MessageBox.Show(String.Format("Connected: {0}",
tcpSocket.Connected.ToString()));
}
private void LoginForm_FormClosed(object sender, FormClosedEventArgs e)
{
tcpSocket.Shutdown(SocketShutdown.Both);
tcpSocket.Close();
Form1 Childform1 = new Form1();
Childform1.MdiParent = ParentForm;
Childform1.Show();
}
private void OnTimedEvent(object sender, EventArgs e)
{
ConnectionStatusTest();
}
}