C# 通过TCP发送PC信息

C# 通过TCP发送PC信息,c#,tcp,windows-services,C#,Tcp,Windows Services,我试图通过TCP向Windows服务发送各种PC信息,如可用硬盘空间、总RAM等。我有下面的代码,它基本上创建了一个由|分割的信息字符串,准备在Windows服务TCP服务器中进行处理,并将其放入SQL表中 像我这样做是最好的还是有更好的方法 public static void Main(string[] args) { Program stc = new Program(clientType.TCP); stc.tcpClient(server

我试图通过TCP向Windows服务发送各种PC信息,如可用硬盘空间、总RAM等。我有下面的代码,它基本上创建了一个由|分割的信息字符串,准备在Windows服务TCP服务器中进行处理,并将其放入SQL表中

像我这样做是最好的还是有更好的方法

    public static void Main(string[] args)
    {
        Program stc = new Program(clientType.TCP);
        stc.tcpClient(serverAddress, Environment.MachineName.ToString() + "|" + FormatBytes(GetTotalFreeSpace("C:\\")).ToString());
        Console.WriteLine("The TCP server is disconnected.");
    }

    public void tcpClient(String serverName, String whatEver)
    {
        try
        {
            //Create an instance of TcpClient.
            TcpClient tcpClient = new TcpClient(serverName, tcpPort);

            //Create a NetworkStream for this tcpClient instance.
            //This is only required for TCP stream.
            NetworkStream tcpStream = tcpClient.GetStream();

            if (tcpStream.CanWrite)
            {
                Byte[] inputToBeSent = System.Text.Encoding.ASCII.GetBytes(whatEver.ToCharArray());

                tcpStream.Write(inputToBeSent, 0, inputToBeSent.Length);

                tcpStream.Flush();
            }

            while (tcpStream.CanRead && !DONE)
            {
                //We need the DONE condition here because there is possibility that
                //the stream is ready to be read while there is nothing to be read.
                if (tcpStream.DataAvailable)
                {
                    Byte[] received = new Byte[512];

                    int nBytesReceived = tcpStream.Read(received, 0, received.Length);

                    String dataReceived = System.Text.Encoding.ASCII.GetString(received);

                    Console.WriteLine(dataReceived);

                    DONE = true;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("An Exception has occurred.");
            Console.WriteLine(e.ToString());
        }

    }

谢谢

因为TCP是基于流的,所以在消息中有一些指示符在另一端读取完整消息时向其发出信号是很重要的。有两种传统的方法可以做到这一点。首先,在每条消息的末尾可以有一些特殊的字节模式。当另一端读取数据时,当看到特殊的字节模式时,它知道它已经读取了完整的消息。使用此机制需要实际消息中不太可能包含的字节模式。另一种方法是在消息的开头包含数据的长度。我就是这样做的。我的所有TCP消息都包含如下结构的短头:

class MsgHeader
{
    short syncPattern;  // e.g., 0xFDFD
    short msgType;      // useful if you have different messages
    int   msgLength;    // length of the message minus header
}
byte[] msg = SystemInfo.Encode();
SystemInfo si = SystemInfo.Decode(msg);
当另一方开始接收数据时,它会读取前8个字节,验证同步模式(为了安全起见),然后使用消息长度读取实际消息。读取消息后,它将根据消息类型处理消息


我建议创建一个类来收集您感兴趣的系统信息,并能够对其进行编码/解码,例如:

using System;
using System.Text;

class SystemInfo
{
    private string machineName;
    private int freeSpace;
    private int processorCount;

    // Private so no one can create it directly.
    private SystemInfo()
    {
    }

    // This is a static method now.  Call SystemInfo.Encode() to use it.
    public static byte[] Encode()
    {
        // Convert the machine name to an ASCII-based byte array.
        var machineNameAsByteArray = Encoding.ASCII.GetBytes(Environment.MachineName);

        // *THIS IS IMPORTANT*  The easiest way to encode a string value so that it
        // can be easily decoded is to prepend the length of the string.  Otherwise,
        // you're left guessing on the decode side about how long the string is.

        // Calculate the message length.  This does *NOT* include the size of
        // the message length itself.
        // NOTE:  As new fields are added to the message, account for their
        //        respective size here and encode them below.
        var messageLength = sizeof(int)                   + // length of machine name string
                            machineNameAsByteArray.Length + // the machine name value
                            sizeof(int)                   + // free space
                            sizeof(int);                    // processor count

        // Calculate the required size of the byte array.  This *DOES* include
        // the size of the message length.
        var byteArraySize = messageLength + // message itself
                            sizeof(int);    // 4-byte message length field

        // Allocate the byte array.
        var bytes = new byte[byteArraySize];

        // The offset is used to keep track of where the next field should be
        // placed in the byte array.
        var offset = 0;

        // Encode the message length (a very simple header).
        Buffer.BlockCopy(BitConverter.GetBytes(messageLength), 0, bytes, offset, sizeof(int));

        // Increment offset by the number of bytes added to the byte array.
        // Note that the increment is equal to the value of the last parameter
        // in the preceding BlockCopy call.
        offset += sizeof(int);

        // Encode the length of machine name to make it easier to decode.
        Buffer.BlockCopy(BitConverter.GetBytes(machineNameAsByteArray.Length), 0, bytes, offset, sizeof(int));

        // Increment the offset by the number of bytes added.
        offset += sizeof(int);

        // Encode the machine name as an ASCII-based byte array.
        Buffer.BlockCopy(machineNameAsByteArray, 0, bytes, offset, machineNameAsByteArray.Length);

        // Increment the offset.  See the pattern?
        offset += machineNameAsByteArray.Length;

        // Encode the free space.
        Buffer.BlockCopy(BitConverter.GetBytes(GetTotalFreeSpace("C:\\")), 0, bytes, offset, sizeof(int));

        // Increment the offset.
        offset += sizeof(int);

        // Encode the processor count.
        Buffer.BlockCopy(BitConverter.GetBytes(Environment.ProcessorCount), 0, bytes, offset, sizeof(int));

        // No reason to do this, but it completes the pattern.
        offset += sizeof(int).

        return bytes;
    }

    // Static method.  Call is as SystemInfo.Decode(myReceivedByteArray);
    public static SystemInfo Decode(byte[] message)
    {
        // When decoding, the presumption is that your socket code read the first
        // four bytes from the socket to determine the length of the message.  It
        // then allocated a byte array of that size and read the message into that
        // byte array.  So the byte array passed into this function does *NOT* have
        // the 4-byte message length field at the front of it.  It makes no sense
        // in this class anyway.

        // Create the SystemInfo object to be populated and returned.
        var si = new SystemInfo();

        // Use the offset to navigate through the byte array.
        var offset = 0;

        // Extract the length of the machine name string since that is the first
        // field encoded in the message.
        var machineNameLength = BitConverter.ToInt32(message, offset);

        // Increment the offset.
        offset += sizeof(int);

        // Extract the machine name now that we know its length.
        si.machineName = Encoding.ASCII.GetString(message, offset, machineNameLength);

        // Increment the offset.
        offset += machineNameLength;

        // Extract the free space.
        si.freeSpace = BitConverter.ToInt32(message, offset);

        // Increment the offset.
        offset += sizeof(int);

        // Extract the processor count.
        si.processorCount = BitConverter.ToInt32(message, offset);

        // No reason to do this, but it completes the pattern.
        offset += sizeof(int);

        return si;
    }
}
要对数据进行编码,请按如下方式调用
encode
方法:

class MsgHeader
{
    short syncPattern;  // e.g., 0xFDFD
    short msgType;      // useful if you have different messages
    int   msgLength;    // length of the message minus header
}
byte[] msg = SystemInfo.Encode();
SystemInfo si = SystemInfo.Decode(msg);
要在从套接字读取数据后对其进行解码,请按如下方式调用
decode
方法:

class MsgHeader
{
    short syncPattern;  // e.g., 0xFDFD
    short msgType;      // useful if you have different messages
    int   msgLength;    // length of the message minus header
}
byte[] msg = SystemInfo.Encode();
SystemInfo si = SystemInfo.Decode(msg);
至于您的实际代码,我不知道为什么在写入套接字之后要从它读取,除非您期望返回值

有些事情要考虑。希望这有帮助

编辑 首先,如果您觉得需要,请使用
MsgHeader
。上面的示例只是将消息长度用作报头,即,它不包括同步模式或消息类型。您是否需要使用此附加信息取决于您自己


对于添加到
SystemInfo
类中的每个新字段,消息的总大小都会明显增加。因此,需要相应地调整
messageLength
值。例如,如果添加一个
int
以包含处理器数量,
messageLength
将增加
sizeof(int)
。然后,要将其添加到字节数组,只需使用相同的调用。我已经对示例进行了调整,以更详细地说明这一点,包括将方法设置为静态。

感谢这一点,尽管我对编码字节的最终结构有点困惑。如何在字节数组中添加更多的机器名和可用空间?您是否可以更改代码以在编码的消息中显示更多的变量?另外,我还需要使用MsgHeader类吗?Cheers@MattBaughan,我在示例中添加了一个处理器计数字段,以及解码逻辑。嗯。