4

最近,我接到了一个任务...

“开发可以安装在办公室或企业的各种 Windows 机器上的 Windows 窗体应用程序。只有一台机器(ALPHA 机器)中会有一个数据库。这个数据库将被其他 Beta 机器上的应用程序用来访问数据. 应用程序本身会设法检查它是 Alpha 还是 Beta(有数据库文件吗?),因此必须充当服务器或客户端。”

除了网络和应用程序间通信要求之外,我什么都能做。所以,我开始在互联网上学习套接字编程,我已经浏览了这个链接......

我正在努力的想法是......

  1. 让客户端将消息发送到服务器。
  2. 让服务器接受此消息并将此消息放入队列中。
  3. 阅读消息以获取客户端的 IP 地址及其数据请求。
  4. 将此请求应用于数据库并获取结果。
  5. 将结果转换为字符串。
  6. 将其发送给请求的客户端。

我可以设法执行步骤 3,4 和 5。我被困在 1、2 和 6 上。

朝着这个...

我为服务器以及在调用时返回套接字的客户端创建了一个函数。我创建了一个单独的函数,因为我希望我的代码在多年之后变得干净、整洁且易于理解。

在下面检查我的代码...

对于服务器...

 private Socket GetServerReady()
    {
           IPEndPoint RemoteEP = new IPEndPoint(IPAddress.Any, 8000);
            Socket newSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            newSock.Connect(RemoteEP);
            newSock.Listen(10);
            return (newSock);
    }

您会注意到任何地方都没有 Accept() 方法,这是因为我希望像下面这样调用它以供进一步使用...

Socket CltSock = GetServerReady().Accept();

客户代码是...

private Socket GetClientReady()
    {
        IPEndPoint RemoteEP = new IPEndPoint(IPAddress.Parse(txtBxHost2.Text.Trim()), 8000);
        Socket ServerSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        ServerSock.Connect(RemoteEP);
        return (ServerSock);
    }

最后,问题是......

  • “调用我上面写的函数的合适位置在哪里?”

  • “我应该在 Form_Load() 事件中调用服务器和客户端函数吗?”

  • “我的主要意图是上面提到的第 1,2 和 6 点,下一步必须是什么?”

我不希望我可以照原样复制完整的代码。只需正确的程序和概念上的一些细节就可以了。

我将只使用一台 PC 进行测试。此外,另一个限制是,它将全部编码在一个应用程序中。我不想为客户端和服务器编写两个单独的应用程序。

我希望我把一切都说清楚了,让你明白。

多谢。

等待回复。

4

1 回答 1

1

我一直在努力完成工作,并以某种方式设法找到了解决方案。

以下是我的解决方案:

服务器端代码:(我将此代码放在一个函数中,如果捕获到任何异常,该函数会循环执行)

private void Looper()
    {
        int i = 0;
        int AttemptCount = 1;
        while (i == 0)
        {
            try
            {
                TcpListener tL = new TcpListener(Network.GetLocalIPAddress(), 56009);
                tL.Start(10);
                Socket tS = tL.AcceptSocket();

                if (tS.Connected)
                {
                    NetworkStream nS = new NetworkStream(tS);
                    StreamReader Reader = new StreamReader(nS);

                    Output = Reader.ReadToEnd().Trim();

                    Reader.Close();
                    nS.Close();
                    tS.Close();
                    tL.Stop();

                    //If Done, End Execution
                    i = 1;
                }
                else
                {
                    MessageBox.Show("The connection to the client is broken or failed..!!\n\nPlease check connection and try again.","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
                }
            }
            catch (SystemException ex)
            {
                //If Not, Loop Execution Again
                if (MessageBox.Show("Exception: " + ex.Message + "\n\nAttempt Count: " + AttemptCount + "\n\nDo you want to terminate the transmission?", "Error", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes)
                {
                    i = 1;
                    ResetTimer.Stop();
                }
                else
                {
                    i = 0;
                    AttemptCount++;
                }
            }
        }
    }

当上述函数被调用时,服务器等待接受任何传入的套接字。如果由于端口重用或任何原因导致某处出现任何错误,它会自行循环并重置服务器。(因此,我们不必一次又一次地手动调用服务器函数。)

一旦服务器接受任何传入的套接字,执行就会成功结束。很多时候,即使在成功接收之后,我们也不想继续调用服务器。因此,我没有在按钮“click_event”中调用此函数,而是在计时器 Tick_Event 中调用它。因此,在服务器端消除了人类的需求。

这导致了一个问题。一旦服务器开始等待接受,它就处于阻塞模式。它将所有进程和控件挂在同一线程中。因此,我将对上述函数的调用移至 BackgroundWorker 的“Do_Work”事件。

检查以下代码:

private void GetServerReady()
    {
        if (!bW.IsBusy)
        {
            bW.RunWorkerAsync();
            txtBxHistory.Text += "\r\n" + Output;
        }
    }

    private void bW_DoWork(object sender, DoWorkEventArgs e)
    {
        Looper();
    }

    private void ResetTimer_Tick(object sender, EventArgs e)
    {
        GetServerReady();
    }
  1. “bW”是“后台工作人员”。
  2. “输出”是我全局定义的变量。

我们需要一个变量的原因是,

BackgroundWorker 有自己的线程来执行放置在其“Do_Work”事件中的代码。因此,BackgroundWorker 不能使用我们应用程序线程中的 TextBox 来存储接收到的输出。对变量执行此操作,然后将 TextBox 的 Text 属性设置为此变量即可。

客户端代码:

private void btnSend_Click(object sender, EventArgs e)
    {
        TcpClient socketForServer;
            try
            {
                socketForServer = new TcpClient(txtBxDestIP.Text.Trim(), 56009);
            }
            catch
            {
                MessageBox.Show("Failed to connect to server at " + txtBxDestIP.Text.Trim() + ":999", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            NetworkStream networkStream = socketForServer.GetStream();
            StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
            try
            {
                string InputString;
                InputString = Network.GetLocalIPAddress() + ": " + txtBxData.Text;
                streamWriter.Write(InputString);
                streamWriter.Flush();
                socketForServer.Close();
                txtBxHistory.Text += "\r\nMe: " + txtBxData.Text.Trim();
                txtBxData.Clear();
            }
            catch
            {
                MessageBox.Show("Exception reading from Server.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            streamWriter.Close();
            networkStream.Close();
            socketForServer.Close();
    }
  1. “txtBxDestIP”是一个文本框,其目标 IP 地址为文本。
  2. “txtBxData”是一个包含要发送的文本的文本框。

这段代码对我来说完美无缺。通过上述解决方案,我可以实现从第 1 步到第 6 步的所有动机(在上面的问题中提到。)

我希望它也对其他人有所帮助。请建议是否有更好和有效的方法来执行此操作。

谢谢。问候。

于 2013-05-25T10:15:07.997 回答