c# - 使用 Visual C# (Dot Net Framework 4.0) 在 "Socket Programming"中需要帮助?

标签 c# .net sockets networking client-server

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

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

除了网络和应用程序间通信要求外,我可以做所有事情。所以,我开始通过互联网学习套接字编程,我经历了this link ...

我正在研究的想法是...

  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 进行测试。此外,另一个限制是,它将全部编码在一个应用程序中。我不想为客户端和服务器编写两个单独的应用程序。

我希望我说的很清楚,以便您理解。

非常感谢。

等待响应。

最佳答案

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

下面是我的解决方案:

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

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”是“BackgroundWorker”。
  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 步的所有动机(在上面的问题中提到。)

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

谢谢。 问候。

关于c# - 使用 Visual C# (Dot Net Framework 4.0) 在 "Socket Programming"中需要帮助?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16730939/

相关文章:

C#动态选择字符串列表

.net - BinaryReader ReadByte() 是否超时?

java - 为连接到服务器应用程序的每个客户端打开一个新的数据库连接?

c# - 将字符串转换为 double

c++ - 从套接字提取数据?

node.js - 在 Post 请求上触发 Socket

c# - 在 IIS7 中以编程方式管理 FTP 服务器的状态实际上是可能的吗?

c# - 如何从中继器获取更新的文本框值?

c# - 忽略所有属性,但 Entity Framework 6 中的一些属性

.net - 线程池与专用线程 - 何时选择哪个