1

我目前正在开发一个聊天程序。它包含一个表单(MainWindow)和一个类(TCPServerConnector)。MainWindow 是包含两个文本框、两个按钮和一个列表框的 UI。

在第一个文本框中,您必须编写要连接的服务器 ip,而不是单击“连接”按钮。第二个文本框包含您的书面文本,第二个按钮发送此文本。列表框应该显示程序从服务器接收到的所有聊天/文本,但我没有让它工作。

Main-Form 初始化 TCPServerConnector 的对象并将 IP 和文本传递给它。但是,当您收到一些文本时,班级必须将收到的文本传递给表单。

我尝试使用委托(以前从未使用过它们),但这并没有解决我的问题,我只是遇到了一些关于跨线程的异常..

还要创建 MainWindow 的新实例,因为它不引用 Main-Form 的旧实例。

我已经添加了下面类的代码,希望有人可以解决我的问题;)


主要形式:

public partial class MainWindow : Form
{

    public MainWindow()
    {
        InitializeComponent();
    }

    TCPServerConnector TestConnection = new TCPServerConnector();

    private void Form1_Load(object sender, EventArgs e)
    {
    }

    private void srvConnect_Click(object sender, EventArgs e)
    {
        TestConnection.ConnectToServer(srvConnectionIP.Text.ToString());
    }

    private void srvSend_Click(object sender, EventArgs e)
    {
        TestConnection.SendEnteredString(srvTextToSend.Text.ToString());
    }

    private void FormMain_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        TestConnection.CloseConnection();
    }
}

在“OnRecievedData”方法中,我添加了一个消息框来显示接收到的文本,因为现在我无法将它添加到表单列表框中。

TCPServerConnector 类:

class TCPServerConnector
{
    private Socket m_sock;
    private byte[] m_byBuff = new byte[256];

    public void ConnectToServer(string ServerAddress)
    {
        try
        {
            if (m_sock != null && m_sock.Connected)
            {
                m_sock.Shutdown(SocketShutdown.Both);
                System.Threading.Thread.Sleep(10);
                m_sock.Close();
            }

            m_sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPEndPoint epServer = new IPEndPoint(IPAddress.Parse(ServerAddress), 399);

            m_sock.Blocking = false;
            AsyncCallback onconnect = new AsyncCallback(OnConnect);
            m_sock.BeginConnect(epServer, onconnect, m_sock);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void OnConnect(IAsyncResult ar)
    {
        Socket sock = (Socket)ar.AsyncState;

        try
        {
            if (sock.Connected)
                SetupRecieveCallback(sock);
            else
                MessageBox.Show("Unable to establish connection");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void OnRecievedData(IAsyncResult ar)
    {
        Socket sock = (Socket)ar.AsyncState;

        try
        {
            int nBytesRec = sock.EndReceive(ar);
            if (nBytesRec > 0)
            {
                string sRecieved = Encoding.ASCII.GetString(m_byBuff, 0, nBytesRec);
                MessageBox.Show(sRecieved); //TO SHOW IF SOMETHING HAPPENED. HERE IS MY PROBLEM
                SetupRecieveCallback(sock);
            }
            else
            {
                Console.WriteLine("Client {0}, disconnected", sock.RemoteEndPoint);
                sock.Shutdown(SocketShutdown.Both);
                sock.Close();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void SetupRecieveCallback(Socket sock)
    {
        try
        {
            AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
            sock.BeginReceive(m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData, sock);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void SendEnteredString(string EnteredData)
    {
        if (m_sock == null || !m_sock.Connected)
        {
            MessageBox.Show("You have to be connected to send something.");
            return;
        }

        try
        {
            Byte[] byteDateLine = Encoding.ASCII.GetBytes(EnteredData.ToCharArray());
            m_sock.Send(byteDateLine, byteDateLine.Length, 0);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    public void CloseConnection()
    {
        if (m_sock != null && m_sock.Connected)
        {
            m_sock.Shutdown(SocketShutdown.Both);
            m_sock.Close();
        }
    }
}
4

2 回答 2

0

原因: 您正在获得跨线程异常,因为您在一个线程上创建 GUI 并在另一个线程上访问它。这在 c# 中是不允许的。

你需要像这样改变你的线路

 MethodInvoker method = delegate
    {
        MessageBox.Show(sRecieved);
    };

if (InvokeRequired)
    BeginInvoke(method);
else
    method.Invoke();
于 2013-08-06T12:07:22.670 回答
0

在您的连接器类中创建事件 DataReceived 并在收到数据时引发它:

public class TCPServerConnector
{
    public event Action<string> DataReceived; // event

    public void OnRecievedData(IAsyncResult ar)
    {    
        try
        {
            Socket sock = (Socket)ar.AsyncState;
            int nBytesRec = sock.EndReceive(ar);
            if (nBytesRec > 0)
            {
                string sRecieved = Encoding.ASCII.GetString(m_byBuff, 0, nBytesRec);
                if (DataReceived != null) // check if subscribed
                    DataRecieved(sRecieved); // raise event with data

                SetupRecieveCallback(sock);
            }
            else
            {
                Console.WriteLine("Client {0}, disconnected", sock.RemoteEndPoint);
                sock.Shutdown(SocketShutdown.Both);
                sock.Close();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    // rest of code is same
}

并以主要形式订阅此事件:

public MainWindow()
{
    InitializeComponent();
    TestConnection.DataRecieved += TestConnection_DataRecieved;
}

private void TestConnection_DataRecieved(string data)
{ 
   if (InvokeRequired) // check if you are on main thread
   {
       // invoke same method on main thread
       Invoke((MethodInvoker)(() => TestConnection_DataRecieved(data)));
       return;
   }

   // show data in listbox
}

注意:您可以EventHandler<TEventArgs>为事件使用委托类型(就像 Microsoft 所做的那样)并创建受保护的方法OnDataReceived(string data)来引发事件。但为了简单起见,我使用Action<string>了委托。

于 2013-08-06T12:07:42.253 回答