Socket发送消息之c#调用c++DLL方法

14 篇文章 0 订阅

最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响应消息的二进制数据流转换成C#结构。

  1、仿照C++结构体写出C#的结构来

  using System.Runtime.InteropServices;

  [Serializable] // 指示可序列化

  [StructLayout(LayoutKind.Sequential, Pack = 1)] // 按1字节对齐

  public struct Operator

  {

  public ushort id;

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] // 声明一个字符数组,大小为11

  public char[] name;

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]

  public char[] pass;

  public Operator(string user, string pass) // 初始化

  {

  this.id = 10000;

  this.name = user.PadRight(11, '\0').ToCharArray();

  this.pass = pass.PadRight(9, '\0').ToCharArray();

  }

  }

  2、注意C#与C++数据类型的对应关系


C++与C#的数据类型对应关系表
API数据类型 类型描述 C#类型 API数据类型 类型描述 C#类型
WORD 16位无符号整数 ushort CHAR 字符 char
LONG 32位无符号整数 int DWORDLONG 64位长整数 long
DWORD 32位无符号整数 uint HDC 设备描述表句柄 int
HANDLE 句柄,32位整数 int HGDIOBJ GDI对象句柄 int
UINT 32位无符号整数 uint HINSTANCE 实例句柄 int
BOOL 32位布尔型整数 bool HWM 窗口句柄 int
LPSTR 指向字符的32位指针 string HPARAM 32位消息参数 int
LPCSTR 指向常字符的32位指针 String LPARAM 32位消息参数 int
BYTE 字节 byte WPARAM 32位消息参数 int


  整个结构的字节数是22bytes。

  对应的C++结构体是:

  typedef struct

  {

  WORD id;

  CHAR name[11];

  CHAR password[9];

  }Operator;

  3、发送的时候先要把结构转换成字节数组

  using System.Runtime.InteropServices;

  ///
  /// 将结构转换为字节数组

  ///

  ///

结构对象

 

  /// 字节数组

  public byte[] StructToBytes(object obj)

  {

  //得到结构体的大小

  int size = Marshal.SizeOf(obj);

  //创建byte数组

  byte[] bytes = new byte[size];

  //分配结构体大小的内存空间

  IntPtr structPtr = Marshal.AllocHGlobal(size);

  //将结构体拷到分配好的内存空间

  Marshal.StructureToPtr(obj, structPtr, false);

  //从内存空间拷到byte数组

  Marshal.Copy(structPtr, bytes, 0, size);

  //释放内存空间

  Marshal.FreeHGlobal(structPtr);

  //返回byte数组

  return bytes;

  }

  接收的时候需要把字节数组转换成结构

  ///
  /// byte数组转结构

  ///

  ///

byte数组

 

  ///

结构类型

 

  /// 转换后的结构

  public object BytesToStruct(byte[] bytes, Type type)

  {

  //得到结构的大小

  int size = Marshal.SizeOf(type);

  Log(size.ToString(), 1);

  //byte数组长度小于结构的大小

  if (size > bytes.Length)

  {

  //返回空

  return null;

  }

  //分配结构大小的内存空间

  IntPtr structPtr = Marshal.AllocHGlobal(size);

  //将byte数组拷到分配好的内存空间

  Marshal.Copy(bytes, 0, structPtr, size);

  //将内存空间转换为目标结构

  object obj = Marshal.PtrToStructure(structPtr, type);

  //释放内存空间

  Marshal.FreeHGlobal(structPtr);

  //返回结构

  return obj;

  }

  4、实际操作:

  using System.Collections;

  using System.Collections.Generic;

  using System.Net;

  using System.Net.Sockets;

  byte[] Message = StructToBytes(new Operator("user","pass")); // 将结构转换成字节数组

  TcpClient socket = new TcpClient();

  socket.Connect(ip,port);

  NetworkStream ns = Socket.GetStream();

  ns.Write(Message,0,Message.Length); // 发送

  byte[] Recv = new byte[1024]; // 缓冲

  int NumberOfRecv = 0;

  IList newRecv = new List();

  ns.ReadTimeout = 3000;

  try

  {

  do

  {

  // 接收响应

  NumberOfRecv = ns.Read(Recv, 0, Recv.Length);

  for (int i = 0; i < NumberOfRecv; i++)

  newRecv.Add(Recv[i]);

  }

  while (ns.DataAvailable);

  byte[] resultRecv = new byte[newRecv.Count];

  newRecv.CopyTo(resultRecv, 0);

  Operator MyOper = new Operator();

  MyOper = (Operator)BytesToStruct(resultRecv, MyOper.GetType()); // 将字节数组转换成结构

  在这里取值的时候可能会出现只能取到一个字段,剩余的取不到的问题,怎么回事我也搞不懂,反正我的解决办法就是按照字节的顺序从resultRecv里分别取出对应的字段的字节数组,然后解码,例如:

  Operator.name是11个字节,最后一位是0,Operator.id是2个字节,那么从第3位到第12位的字节就是 Operator.name的内容,取出另存为一个数组MyOperName,Encoding.Default.GetString (MyOperName)就是MyOper.name的内容。

  socket.Close();

  ns.Close();
转自:<a href='http://www.yuloo.com/jsjks/'>计算机培训网</a>

 

文献来源:http://www.yuloo.com/news/1006/430860.html

SanNiuSignal是一个基于异步socket的完全免费DLL;它里面封装了Client,Server以及UDP;有了这个DLL;用户不用去关心心跳;粘包 ;组包;发送文件等繁琐的事情;大家只要简单的几步就能实现强大的通信系统;能帮助到大家是本人觉得最幸福的事情,也希望大家 在用的过程中找出DLL中不足的地方;好改正;此DLL的苹果版和java版正在努力开发中......交流QQ:365368248;此演示源码下载地址:http://pan.baidu.com/s/1eQw1npw 里面包括了SanNiuSignal.DLL 下面为大家介绍一下 如何使用SanNiuSignal. 1 ) :TCPServer服务器,通过静态方法TxStart.startServer注册成功服务器ITxServer;然后通过ITxServer设置好各种属性;最后启动 ITxServer.StartEngine();就可以了;具体可以到demo里去看;相对来说比较简单,下面来说一下需要注意的地方 一:ITxServer.BufferSize,就是接收数据缓冲区大小;默认为1kb,不要小于50字节,而且要和客户端设置成一样;否则会出现不可预知 的错误;这个缓冲区的大小,不会影响你每次发送数据的大小;大小只能提高你的发送速度 二:ITxServer.sendMessage 发送前先判断此IPEndPoint客户端是否在线;因为如果不在线等情况发生;此方法没有任何消息产生;如 果发送成功并且对方已经收到;会触发发送成功的事件; 三:用户可以通过ITxServer.FileLog记录服务器的运行信息; 2 ) :TCPClient客户端,通过静态方法TxStart.startClient(服务器地址或网址, 服务器端口号)注册成功客户端ITxClient;然后通过 ITxClient设置好各种属性;最后启动ITxClient.StartEngine();跟服务器启动差不多;具体可以到demo里去看;客户端要注意的一些地 方 一:如果非服务器强制关闭客户端的情况下,掉线的话,客户端默认是要重连的;他的原理是这样的,每10秒重连一次;重连 ITxClient.ReconnectMax次如果失败;会自动关闭客户端引擎;在重连的过程中;你可以改变客户端连接服务器的IP地址和端口号;但 如果当断开的时候不想重连;可以设置ITxClient.ReconnectMax=0; 二:登录篇,在引擎启动之前,用户可以设置ITxClient.OutTime来设置超时时间;默认为10秒,也就是说10秒之内肯定会有一个登录结果 ;登录成功或登录失败。有了登录结果会触发登录结果事件; 3 ) :Udp引擎,通过静态方法TxStart.startUdp()注册成功UDP--IUdpTx;然后通过IUdpTx设置好各种属性;最后启动 IUdpTx.StartEngine();如果在启动之前要绑定端口号,请在这里设置IUdpTx.Port;否则是随机使用本地端口;无论是UDP还是服务器和 客户端;它们的很多方法和属性以及事件都是相同的,因为他们都继承了通信系统的基接口ITxBase;下面是UDP引擎要注意的地方 一:UDP的优势在于速度快但不太可靠;所以有些属性不能设置的太过,如IUdpTx.BufferSize;默认为1KB,如果在广域网上发送信息, 缓冲区大小不要超过默认值;否则数据会丢失,如果一次性数据大于1KB;也没事,系统会自动分包发送;不太会丢包。 4 ) :文件发送系统,通过静态方法FileStart.StartFileSend(IFileSendMust)生成一个文件发送系统IFileSend;其中IFileSendMust是 必须实现的一个接口;具体参照demo;然后通过IFileSend设置好各种属性;发送还是要通过前面的三个引擎系统发送的;如 ITxClient.SendFile 这时会返回一个文件标签,是一个整数;IFileSend可以通过操作这个标签来操作这个正在发送的文件;接收文件 系统也一样;也是通过文件标签来操作;文件续传也是一样,也要通过前面的三个通信引擎系统进行续传;因为文件系统不能决定用户 通过哪个通信系统进行续传的;例如IUdpTx.ContinueFile进行续传;大家也可以试一下 掉线之后重新连接也可以续传; 5 ) :文件接收系统,通过静态方法FileStart.StartFileReceive(IFileReceiveMust)生成一个文件接收系统IFileReceive;其中 IFileReceiveMust是接收方必须实现的一个接口;具体参照demo;然后通过IFileReceive设置好各种属性;下面来讲讲文件系统要注意 的几个要点 一:每个文件都有一个文件标签,发送系统和接收系统是通过控制这个标签来控制这个文件的;如果是同一个文件传输,标签也一样的; 二:要注意属性BufferSize缓冲区大小,他是代表一次传送的字节数;如果这个文件缓冲区越大,传输速度越快;具体要设置成多少,大 家自己去调试;TCP传输的话一般可以设置成比较大,但UDP的话这个不要超过1KB;所以传输大文件尽量不要用UDP这种不可靠的东东; 三:当网络等各种原因中断文件的时候;会触发文件中断事件;文件自动处于暂停发送或接收状态;等待续传;续传的发送方,如果对方 同意续传;对方将不会触发续传开始的方法;别的大家可以通过demo里去发现; 总结: 其实这个DLL相对来讲是比较简单的;大家只要掌握这五个接口;1:ITxServer TCP服务器接口 2:ITxClient TCP客户端接口 3:IUdpTx UDP接口 4:IFileSend 文件发送方接口 5:IFileReceive 文件接收方接口 前面三个接口是通过TxStart启动;后面二个文件接口 是通过FileStart启动 今天先讲到这里;希望这个DLL对大家有所帮助;不足之处希望通过QQ或博客园大家一起交流;谢谢 此演示源码下载地址:http://pan.baidu.com/s/1eQw1npw 里面包括了SanNiuSignal.DLL
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值