1

我正在寻找一种在 TCP 中实现协议以发送和接收简单字符串消息的方法。我有一个客户端和一个服务器应用程序,当它们第一次开始运行时,我按下客户端上的连接按钮以连接到服务器。服务器的列表框上会弹出一些文本,说明客户端已连接,客户端的列表框上会出现一些文本,说明它已连接到服务器。

那部分工作正常,但现在我希望能够将其他字符串发送到服务器,并且根据我发送的字符串会使服务器执行某个操作,我该怎么做?想到的第一个想法是在某处使用 if-then 语句。我将在下面发布我的代码:

服务器:

    private static int port = 8080;
    private static TcpListener listener;
    private static Thread thread;


   private void Form1_Load(object sender, EventArgs e)
    {

        listener = new TcpListener(new IPAddress(new byte[] { 10, 1, 6, 130 }), port);
        thread = new Thread(new ThreadStart(Listen));
        thread.Start(); 


    }

   private void Listen()
    {
        listener.Start();
        listBox1.Invoke(new EventHandler(delegate { listBox1.Items.Add("Listening on: " + port.ToString()); }));


        while (true)
        {              
            listBox1.Invoke(new EventHandler(delegate { listBox1.Items.Add("Waiting for connection...."); }));
            TcpClient client = listener.AcceptTcpClient();
            Thread listenThread = new Thread(new ParameterizedThreadStart(ListenThread));
            listenThread.Start(client);

        }
    }

    //client thread 
    private void ListenThread(Object client)
    {

        NetworkStream netstream = ((TcpClient)client).GetStream();

        listBox1.Invoke(new EventHandler(delegate { listBox1.Items.Add("Request made"); }));


        byte[] resMessage = Encoding.ASCII.GetBytes("Connected to Server");



        netstream.Write(resMessage, 0, resMessage.Length);
        netstream.Flush();

    } 

客户:

 TcpClient tcpclnt;

 private void buttonConnect_Click(object sender, EventArgs e)
    {
        try
        {
            tcpclnt = new TcpClient();
            userEventBox.Items.Add("Connecting.....");

            try
            {

                tcpclnt.Connect("10.1.6.130", 8080);

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());


                return;
            }

            Stream stm = tcpclnt.GetStream();

            byte[] bb = new byte[100];
            int k = stm.Read(bb, 0, 100);

            string returndata = System.Text.Encoding.ASCII.GetString(bb);

            userEventBox.Items.Add(returndata);
            tabControl1.Enabled = true;


           //need a way for the server see this string
            string populateList = "Test";

            ASCIIEncoding asen = new ASCIIEncoding();

            byte[] ba = asen.GetBytes(populateList);
            stm.Write(ba, 0, ba.Length);              
        }

        catch (Exception ex)
        {
            Console.WriteLine("Error..... " + ex.StackTrace);
        }
    }

我很困惑,希望您能在这个问题上提供任何帮助。谢谢你。

来源-> C# 客户端-服务器协议/模型问题

4

1 回答 1

4

建立连接后,客户端和服务器都需要持续监控它们的NetworkStream对象以获取新数据。收到数据后,应将其附加到某种数据缓冲区,直到您收到足够的数据以构成完整的消息。收到适当数量的数据后,您可以尝试从中解析消息并做出适当的响应。

本质上,您需要设置如下所示的逻辑:

var data = new byte[1024];
var dataLength = 0;
var dataBuffer = new MyCustomDataBuffer();
while (true)
{
    while (stream.DataAvailable)
    {
        dataLength = stream.Read(data, 0, data.Length);
        dataBuffer.Append(data, dataLength);
        while (dataBuffer.ContainsCompleteMessage())
        {
            dataBuffer.ProcessMessage();
        }
    }
}

这个例子的控制流程被大大简化了,但它应该可以理解这个想法。我也没有提供 的实现MyCustomDataBuffer,但是编写这样一个类并不太复杂;它实际上只是一个字节流。

我们如何知道我们是否收到了完整的消息?好吧,这就是协议的重点:建立让我们知道这些事情的规则。让我们考虑一个简单的数据协议示例,其中每条消息都由相同的两部分组成:

  • 2 个标头字节,表示消息的总大小(以字节为单位)
  • 构成消息字符串的任意字节数

通过这个协议,我们知道所有的消息都至少有两个字节长(理论上最小的有效消息是00 02,它代表一个空字符串)。我们还知道消息的前两个字节告诉我们消息的总大小,因此我们将知道何时停止读取数据。

因此,要实现上述ContainsCompleteMessage()代码中的方法,我们需要:

  • 确保数据缓冲区中至少有 2 个字节;
  • 将这两个字节转换为整数;
  • 并确定我们的数据缓冲区中是否至少有那么多字节。

一旦我们知道我们有一条有效的消息——并且我们知道它有多大——我们所要做的就是从我们的数据缓冲区中切掉前 N 个字节(其中 N 是消息的大小),然后拉出消息字符串出消息数据的相关段:

// here, 'msg' is a byte array that contains just the message data
var msgString = Encoding.UTF8.GetString(msg, 2, msg.Length - 2);
switch (msgString)
{
    case "kill": KillServer(); break;
    // etc. 
}

我在这里描述的协议将满足您在网络上发送短字符串的声明需求。泛化它可以让你发送更复杂的对象;您只需要添加指定消息布局的其他元数据。我把它留给读者作为练习。

于 2012-07-25T20:43:55.663 回答