我正在尝试连接到远程服务器,登录,然后向其发送数据并使用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
的{{1}}部分未作为参数传递给string data = loginDetails
函数。因此,SendData
功能无法正常工作,并显示SendData
。如果这是问题,如何将SendDataException
(或其他字符串)传递给loginDetails
函数?
使用控制台应用程序进行连接时,连接,登录或发送或接收数据都没有问题,并且连接不会被删除。但是,当我尝试使用telnet连接时,在大约30秒不活动后从服务器返回一条消息,说"连接到主机丢失"。在我的表单应用程序中,连接似乎在大约30秒后被删除,并且由于某种原因,表单应用程序需要大约25秒来告诉我我已连接。 我已尝试在表单应用程序中使用KeepAlive,但显然默认的TCP KeepAlive期限为2小时。如果这是问题,我怎么能把它改成20秒呢?我读过的所有内容都建议让Visual Studio通过我的防火墙(我试过这个 - 没有运气),或者必须更改注册表项,这似乎有点过于复杂。
错误消息是"已建立的连接被主机中的软件中止",错误代码为10053,这让我觉得我已经超时了。我当然可能是错的,问题可能是我没有发现的其他问题。我将衷心感谢您的帮助!感谢。
更新 - 我已经使用同步方法重写了代码(并添加了一个计时器,这样我就可以看到我是否仍然每隔5秒连接一次)。我现在得到两个不同的错误。
第一个说"无法立即完成非阻塞套接字操作" 并生成错误代码10035 。当我仍然连接到远程服务器时,SendData
方法会发生此错误。我认为清除缓冲区可能有所帮助,但这只是意味着我下次尝试阅读时会收到一条空白消息。所以我只能认为我实际上并没有从服务器收到新消息,即使我知道我应该这样做。
第二个是原始错误 - "已建立的连接已被主机中的软件中止",错误代码10053 - 这发生在{{1}我在30秒后断开连接的方法。我已经在不同的地方阅读过更改Receive
属性可能会有所帮助,但我在调用{{1}之前尝试将其设为Send
和Socket.Blocking
方法,既没有帮助。
关于如何让它发挥作用的任何想法?
修改后的代码:
true