软件的客户端原来是用DELPHI写的,翻译成C#,服务器端是VC写的,原来的协议比较复杂,我就用DELPHI写了一个非常简单的现在卡在了C#发送结构体,结构体中字符串与BYTE类型的都没有问题,但是发送整数据(delphi中integer,C#中int或Int32)收到的数据是错误的另外如果结构体中有结构体数组时,应该如何写,如何给结构体的数据赋值呢?
上代码 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;using System.Windows.Forms;
using System.Runtime.InteropServices;
using MTWS.Net.SocketClient;
 namespace MTWS.Net.AppCode
{    
public partial class TestTcp : Form   
 {
        public TestTcp()
        {
            InitializeComponent();
        }
         struct TMsgHeadInfo
        {
            //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public char MsgCode;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            public char[] MsgType;
            public byte Sender;
            //public int SendInt;
        }
         struct TMsgBody
        {
            public TMsgHeadInfo msghead;   //假如这里是一个结构体数组时应该如何定义和赋值呢?
      //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
            //public TMsgHeadInfo[] msghead;  这样是不对的
      //public TMsgHeadInfo[2] msghead; 这样也不对
     //此处到底应该怎样定义与赋值?另外,协议是原来定好了的,肯定不能变了,改协议来解决这个问题的话,就不用提了
     public Int32 body;   //这里哪个类型对就DELPHI里的integer?
        } 
        public static byte[] StructToBytes(object structObj) 
       { 
           //得到结构体的大小
     int size = Marshal.SizeOf(structObj);
          //创建byte数组       
     byte[] bytes = new byte[size];
          //分配结构体大小的内存空间       
     IntPtr structPtr = Marshal.AllocHGlobal(size); 
          //将结构体拷到分配好的内存空间        
     Marshal.StructureToPtr(structObj, structPtr, false); 
           //从内存空间拷到byte数组      
     Marshal.Copy(structPtr, bytes, 0, size); 
           //释放内存空间       
     Marshal.FreeHGlobal(structPtr); 
           //返回byte数组      
     return bytes;
        }
        private void TestTcp_Load(object sender, EventArgs e) 
       {                 } 
        private void button1_Click(object sender, EventArgs e) 
       { 
           int ret;
           TMsgBody msgbody;
            //TMsgHeadInfo msghead;
            TCPClient tcp = new TCPClient(); 
           string code = "zp"; 
           string type = "ai";
            //string body = "cc"; 
           ret = tcp.ConnectServer("127.0.0.1", 3578); 
           if (ret == 0) 
           { 
               MessageBox.Show("连接服务器失败");              
           } 
           else
            {
                MessageBox.Show("连接服务器成功");
                msgbody.msghead.MsgCode = code.ToCharArray(); 
                msgbody.msghead.MsgType = type.ToCharArray(); 
                msgbody.msghead.Sender = 5; 
                msgbody.body = 44;//    服务器端收到后,实际不是44,其他数据类型没有问题 
        byte[] msg = StructToBytes(msgbody);
                if (tcp.SendMsg(msg) > 0) 
               { 
                   MessageBox.Show("发送成功"); 
               } 
               else 
               { 
                  MessageBox.Show("发送失败"); 
               } 
           }
         } 
   }
}服务器端delphi代码,组件用的indy9unit Unit1;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPServer;type
  TChar2 = array[0..1] of Char;
  TMsgHead=packed record
    MsgCode, MsgType: TChar2;
    sender: Byte;
    senderint: Integer;
  end;  TMsgBody=packed record
    msgHead: TMsgHead;
    body: Integer;
  end;  TForm1 = class(TForm)
    IdTCPServer1: TIdTCPServer;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure IdTCPServer1Execute(AThread: TIdPeerThread);
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  Form1: TForm1;implementation{$R *.dfm}procedure TForm1.Button1Click(Sender: TObject);
begin
  IdTCPServer1.DefaultPort := 3578;
  IdTCPServer1.Active := True;
end;function Min(AValueOne, AValueTwo : Integer): Integer;
begin
  If AValueOne > AValueTwo then
  begin
    Result := AValueTwo
  end //If AValueOne > AValueTwo then
  else
  begin
    Result := AValueOne;
  end; //..If AValueOne > AValueTwo then
end;function PCharToStr(ABuf: array of char; ABufLen: Integer): string;
{
  将PChar(#0结尾)转换为string
}
var
  LenOfStr: Integer;
begin
  LenOfStr := Min(ABufLen, StrLen(ABuf));
  if LenOfStr <= 0 then
  begin
    Result := '';
  end
  else
  begin
    SetLength(Result, LenOfStr);
    Move(ABuf[0], Result[1], LenOfStr);
  end;
end;procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
//var msgHead: TMsgHead;
var msgBody: TMsgBody;
begin
    if AThread.Connection.Connected then begin
      FillChar(msgBody, SizeOf(TMsgBody), 0);
      AThread.Connection.ReadBuffer(msgBody, SizeOf(TMsgBody));
      Edit1.Text := PCharToStr(msgBody.msgHead.MsgCode, 2);
      Edit2.Text := PCharToStr(msgBody.msgHead.MsgType, 2);
      Edit3.Text := IntToStr(Integer(msgBody.msgHead.sender));
      Edit4.Text := IntToStr(msgBody.msgHead.senderint);
    end;
end;end.C#socketStructureDelphi结构体

解决方案 »

  1.   

    如果这样做复杂,我建议你用几分钟,自己实现结构体=>字节流的正反向转换。BitConverter.ToBytes()方法可以将你常见的数值类型转化为字节流,拼接到一个List<byte>中,最后发送这个List中内容即可。(List.ToArray()后就是字节数组了)反之,收到字节数组后,可以例如BitConverter.ToInt32的方法转化为变量。
      

  2.   

    结构体到字节流的转换有啊,至于你后面所说的list,这个真心没用过,c#我是半路出家,半瓶子醋的水平,很多基础的东西不懂,有机会要恶补一下,现在只好求代码了
            public byte[] StructToBytes(object obj)
            {
                int size = Marshal.SizeOf(obj);
                byte[] bytes = new byte[size];
                IntPtr structPtr = Marshal.AllocHGlobal(size); //分配结构体大小的内存空间
                Marshal.StructureToPtr(obj, structPtr, false); //将结构体拷到分配好的内存空间
                Marshal.Copy(structPtr, bytes, 0, size);       //从内存空间拷到byte数组
                Marshal.FreeHGlobal(structPtr);                //释放内存空间
                return bytes;
            }        public object BytesToStruct(byte[] bytes, Type type)
            {
                int size = Marshal.SizeOf(type);
                if (size > bytes.Length)
                    return null;
                IntPtr structPtr = Marshal.AllocHGlobal(size); //分配结构大小的内存空间
                Marshal.Copy(bytes, 0, structPtr, size);       //将byte数组拷到分配好的内存空间
                object obj = Marshal.PtrToStructure(structPtr, type);
                Marshal.FreeHGlobal(structPtr);
                return obj;
            }
      

  3.   

    上次不是跟你提过了。把DELPHI的msgBody 转换成byte后打印出来。看看他里面真实的字节内容是什么,
    然后再看看C#的结构体转成Byte里面真实的字节内容是什么两者对比就知道是什么原因了。总觉的C#的结构体转成字节后会出问题
      

  4.   

    BitConverter.ToBytes没有这个方法,有GetBytes
      

  5.   

    通过网络发送时,整数需要从主机字节序(小端)转换成网络字节充(大端)
    发送前要用方法System.Net.IPAddress.HostToNetworkOrder()进行转换
      

  6.   

    这个方法如何使用?直接System.Net.IPAddress.HostToNetworkOrder(msgbody.body);  这样报错啊
      

  7.   


    使用了这个方法后,在服务器端“正常”了,但是只限于256以下的数字,如果c#发256,delphi显示0,以此类似,到了512,又显示0,513显示1
      

  8.   


    使用了这个方法后,在服务器端“正常”了,但是只限于256以下的数字,如果c#发256,delphi显示0,以此类似,到了512,又显示0,513显示1256刚好是8为二进制数字,这里,这里,是否丢掉高位的字节了?
    给个建议:
    1、你写一发和收的两个小程序。发送程序发送设定数据,然后在接收端调试看看你接收到的数据到底是什么样的!!!依据此分析。当然,结构体里面,你还是要用到上面的marshal类的处理。
    2、接收端用DELPHI程序接收,在看看它接收的数据是什么,比较下,据此查找原因和找到解决方法。
      

  9.   


    使用了这个方法后,在服务器端“正常”了,但是只限于256以下的数字,如果c#发256,delphi显示0,以此类似,到了512,又显示0,513显示1256刚好是8为二进制数字,这里,这里,是否丢掉高位的字节了?
    给个建议:
    1、你写一发和收的两个小程序。发送程序发送设定数据,然后在接收端调试看看你接收到的数据到底是什么样的!!!依据此分析。当然,结构体里面,你还是要用到上面的marshal类的处理。
    2、接收端用DELPHI程序接收,在看看它接收的数据是什么,比较下,据此查找原因和找到解决方法。
    本身写的就是一个测试程序,代码很少,都在楼上,你所说的调试应该如何调试?
      

  10.   


    使用了这个方法后,在服务器端“正常”了,但是只限于256以下的数字,如果c#发256,delphi显示0,以此类似,到了512,又显示0,513显示1256刚好是8为二进制数字,这里,这里,是否丢掉高位的字节了?
    给个建议:
    1、你写一发和收的两个小程序。发送程序发送设定数据,然后在接收端调试看看你接收到的数据到底是什么样的!!!依据此分析。当然,结构体里面,你还是要用到上面的marshal类的处理。
    2、接收端用DELPHI程序接收,在看看它接收的数据是什么,比较下,据此查找原因和找到解决方法。delphi处用了这个方法Edit4.Text := IntToStr(ntohl(msgBody.body));
    ntohl网络顺序转主机顺序
      

  11.   


    使用了这个方法后,在服务器端“正常”了,但是只限于256以下的数字,如果c#发256,delphi显示0,以此类似,到了512,又显示0,513显示1byte类型的不用转换了,高于一个字节才需要
    也不知道你的协议是不是完全遵循NetworkOrder的,不过问题很可能就出在数字存储字节序了
    你最好把发送和接收到的都纪录下了,然后比对一下
    //将每个字节用两位十六进制的形式输出
    int i = 1234;
    Console.WriteLine(string.Join(" ", BitConverter.GetBytes(i).Select(b => b.ToString("X2")).ToArray()));
    Console.WriteLine(string.Join(" ", BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(i)).Select(b => b.ToString("X2")).ToArray()));
      

  12.   


    使用了这个方法后,在服务器端“正常”了,但是只限于256以下的数字,如果c#发256,delphi显示0,以此类似,到了512,又显示0,513显示1byte类型的不用转换了,高于一个字节才需要
    也不知道你的协议是不是完全遵循NetworkOrder的,不过问题很可能就出在数字存储字节序了
    你最好把发送和接收到的都纪录下了,然后比对一下
    //将每个字节用两位十六进制的形式输出
    int i = 1234;
    Console.WriteLine(string.Join(" ", BitConverter.GetBytes(i).Select(b => b.ToString("X2")).ToArray()));
    Console.WriteLine(string.Join(" ", BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(i)).Select(b => b.ToString("X2")).ToArray()));
    不懂linq
      

  13.   

    我用的是.net2.0,不知道这样写对不对                string x2 = "";
                    msg = BitConverter.GetBytes(msgbody.body);
                    
                    for (int i = 0; i < msg.Length - 1; i++)
                    {
                        x2 += msg[i].ToString("X2");                    MessageBox.Show(x2);
                    }                x2 = "";
                    byte[] msg2 = BitConverter.GetBytes(System.Net.IPAddress.HostToNetworkOrder(msgbody.body));
                    for (int i = 0; i < msg.Length - 1; i++)
                    {
                        x2 += msg2[i].ToString("X2");                    MessageBox.Show(x2);
                    }
      

  14.   


    使用了这个方法后,在服务器端“正常”了,但是只限于256以下的数字,如果c#发256,delphi显示0,以此类似,到了512,又显示0,513显示1256刚好是8为二进制数字,这里,这里,是否丢掉高位的字节了?
    给个建议:
    1、你写一发和收的两个小程序。发送程序发送设定数据,然后在接收端调试看看你接收到的数据到底是什么样的!!!依据此分析。当然,结构体里面,你还是要用到上面的marshal类的处理。
    2、接收端用DELPHI程序接收,在看看它接收的数据是什么,比较下,据此查找原因和找到解决方法。
    本身写的就是一个测试程序,代码很少,都在楼上,你所说的调试应该如何调试?写一个测试结构体,赋值,然后收发,把收到的字节流,打印出来。通过这样调试
      

  15.   

    你把你发送的整数,用那方法打印出来,然后把服务那边收到的ntohl(msgBody.body)也打印出来,应该就能看出问题了
      

  16.   

    问题已解决,加上[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]就可以,谢谢楼上各位