通过TCP发送PC信息

时间:2014-01-11 18:40:25

标签: 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(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());
        }

    }

由于

1 个答案:

答案 0 :(得分:1)

因为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
}

当另一方开始接收数据时,它会读取前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方法:

byte[] msg = SystemInfo.Encode();

要从套接字读取数据后对其进行解码,请按以下方式调用Decode方法:

SystemInfo si = SystemInfo.Decode(msg);

至于你的实际代码,我不知道为什么你在写入它之后从套接字读取,除非你期望返回值。

要考虑的一些事项。希望这会有所帮助。

修改

首先,如果您觉得需要,请使用MsgHeader。上面的例子只是使用消息长度作为标题,即它不包括同步模式或消息类型。您是否需要使用此附加信息取决于您。

对于您添加到SystemInfo类的每个新字段,显然消息的整体大小会增加。因此,需要相应地调整messageLength值。例如,如果您添加int以包含处理器数量,messageLength将增加sizeof(int)。然后,要将其添加到字节数组,只需使用相同的System.Buffer.BlockCopy调用。我已经调整了示例,以便更详细地展示这一点,包括使方法保持静态。