Profilo di Bin蜉蝣之冢FotoBlogElenchiAltro ![]() | Guida |
|
|
06 gennaio 转:C#和C++结构体Socket通信转: C#和C++结构体Socket通信最近在用C#做一个项目的时候,Socket发送消息的时候遇到了服务端需要接收C++结构体的二进制数据流,这个时候就需要用C#仿照C++的结构体做出一个结构来,然后将其转换成二进制流进行发送,之后将响应消息的二进制数据流转换成C#结构。 1、仿照C++结构体写出C#的结构来 1using System.Runtime.InteropServices; 2 ![]() 3 [Serializable] // 指示可序列化4 [StructLayout(LayoutKind.Sequential, Pack = 1)] // 按1字节对齐5 public struct Operator6 ![]() 7{ 8 public ushort id;9 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] // 声明一个字符数组,大小为1110 public char[] name;11 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)]12 public char[] pass;13 ![]() 14 public Operator(string user, string pass) // 初始化15 { 16 this.id = 10000;17 this.name = user.PadRight(11, '\0').ToCharArray();18 this.pass = pass.PadRight(9, '\0').ToCharArray();19 }20 }21 ![]() 22 ![]()
2、注意C#与C++数据类型的对应关系
整个结构的字节数是22bytes。 对应的C++结构体是:
1 typedef struct2{ 3 WORD id; 4 CHAR name[11];5 CHAR password[9];6 }Operator;7 ![]() 8 ![]() 3、发送的时候先要把结构转换成字节数组
1 using System.Runtime.InteropServices; 2 ![]() 3 /// <summary> 4 /// 将结构转换为字节数组5 /// </summary>6 /// <param name="obj">结构对象</param>7 /// <returns>字节数组</returns>8 public byte[] StructToBytes(object obj)9 { 10 //得到结构体的大小11 int size = Marshal.SizeOf(obj);12 //创建byte数组13 byte[] bytes = new byte[size];14 //分配结构体大小的内存空间15 IntPtr structPtr = Marshal.AllocHGlobal(size);16 //将结构体拷到分配好的内存空间17 Marshal.StructureToPtr(obj, structPtr, false);18 //从内存空间拷到byte数组19 Marshal.Copy(structPtr, bytes, 0, size);20 //释放内存空间21 Marshal.FreeHGlobal(structPtr);22 //返回byte数组23 return bytes;24 ![]() 25 }26 ![]() 27 ![]() 接收的时候需要把字节数组转换成结构
1/// <summary> 2 /// byte数组转结构3 /// </summary>4 /// <param name="bytes">byte数组</param>5 /// <param name="type">结构类型</param>6 /// <returns>转换后的结构</returns>7 public object BytesToStruct(byte[] bytes, Type type)8 { 9 //得到结构的大小10 int size = Marshal.SizeOf(type);11 Log(size.ToString(), 1);12 //byte数组长度小于结构的大小13 if (size > bytes.Length)14 { 15 //返回空16 return null;17 }18 //分配结构大小的内存空间19 IntPtr structPtr = Marshal.AllocHGlobal(size);20 //将byte数组拷到分配好的内存空间21 Marshal.Copy(bytes, 0, structPtr, size);22 //将内存空间转换为目标结构23 object obj = Marshal.PtrToStructure(structPtr, type);24 //释放内存空间25 Marshal.FreeHGlobal(structPtr);26 //返回结构27 return obj;28 }29 ![]() 4、实际操作:
1 using System.Collections;2 using System.Collections.Generic;3 using System.Net;4 using System.Net.Sockets;5 ![]() 6 byte[] Message = StructToBytes(new Operator("user","pass")); // 将结构转换成字节数组7 ![]() 8 TcpClient socket = new TcpClient();9 ![]() 10 socket.Connect(ip,port);11 ![]() 12 NetworkStream ns = Socket.GetStream();13 ![]() 14 ns.Write(Message,0,Message.Length); // 发送15 ![]() 16 byte[] Recv = new byte[1024]; // 缓冲17 ![]() 18 int NumberOfRecv = 0;19 ![]() 20 IList<byte> newRecv = new List<byte>();21 ns.ReadTimeout = 3000;22 try23{ 24 do25{ 26 // 接收响应27 NumberOfRecv = ns.Read(Recv, 0, Recv.Length);28 for (int i = 0; i < NumberOfRecv; i++)29 newRecv.Add(Recv[i]);30 }31 while (ns.DataAvailable);32 byte[] resultRecv = new byte[newRecv.Count];33 newRecv.CopyTo(resultRecv, 0);34 ![]() 35 Operator MyOper = new Operator();36 ![]() 37 MyOper = (Operator)BytesToStruct(resultRecv, MyOper.GetType()); // 将字节数组转换成结构38 ![]() 在这里取值的时候可能会出现只能取到一个字段,剩余的取不到的问题,怎么回事我也搞不懂,反正我的解决办法就是按照字节的顺序从resultRecv里分别取出对应的字段的字节数组,然后解码,例如: Operator.name是11个字节,最后一位是0,Operator.id是2个字节,那么从第3位到第12位的字节就是Operator.name的内容,取出另存为一个数组MyOperName,Encoding.Default.GetString(MyOperName)就是MyOper.name的内容。
1 socket.Close();2 ![]() 3 ns.Close();4 ![]() 以上是从别处转过来的,方便自己看看.特此注明! 10 maggio 被MS郁闷了两天.这个问题很妖异我本来做了2个下拉框,一个省,一个城市,用Icallbackeventhandler接口来实现所谓的AJAX效果.郁闷从此开始,回调不执行了.我开始用ALERT查JS代码,无果.接着看后台代码,那几行也没什么问题,在PAGEONLOAD里只有2段代码,一段是注册JS,一段是填充省份下拉框,我把填充那段注释掉,再运行一次,结果奇迹般的正常了,我开始怀疑是不是代码冲突.试了几个位置,只要是执行了填充的,就不能正常回调了.
以下是我的代码:
protected void Page_Load(object sender, EventArgs e)
{ initSelect(); ClientScriptManager cm = Page.ClientScript; String cbReference = cm.GetCallbackEventReference(this, "args", "GetData", "", false); String callbackScript = @"function CallServer(args,context) { args=select('"+this.DropDownList2.ClientID+"',context);" + cbReference + ";}"; cm.RegisterClientScriptBlock(this.GetType(), "CallServer", callbackScript, true); } private void initSelect() { Bo.ProvinceBo pbo = new Bo.ProvinceBo(); this.DropDownList2.DataSource = pbo.GetAll(); this.DropDownList2.DataTextField = "prov"; this.DropDownList2.DataValueField = "prov"; this.DropDownList2.DataBind(); } 然后开始分段测试initSelect()里面的语句发现,只要把DataValueField 等于ID列,就可以正常执行.prov列是中文,ID列是数字,只有这2个区别.
现在问题找到了.中文......网上搜了一下,也有一位大哥碰到过这样的问题,他讲的比较全面.
17 luglio 关于AJAX与JSON我之前用AJAX做过一个项目,虽然在用户体验方面表现的确实不错,但是数据的传递做的不太好,当时只是小试,而且XML的解析和构成是自己YY出来的,相当笨拙.虽然在HTML上看不出什么问题,但是我自己心里知道,这些代码是经不起WEB考验的.而且,对于AJAX来说,我的使用显得太狭隘,只是传传数据,当时就觉得自己的技术有些干涩.
前段日子,有人对我说,可以用AJAX调用服务器端的 类.方法() ,我当时就觉得这才是一个广义上的AJAX使用,但是一直想不出怎么实现,我被GET 和POST的传值方式束缚住了,那个时候我就否定了用GET方法来实现,理由很简单,太笨拙,而且一个太长的URL本身就是包袱,但是POST在AJAX中,老实说,我没有用过.
知道最近我开始看JSON,我觉得这样的数据结构太好了,似乎很容易实现方法的调用.
虽然在一开始学习AJAX的时候,JSON的书已经在硬盘上了,而且就在AJAX边上,如果当时早一点看,我想,之前那个项目,应该有更多有趣的地方.
不过看了总不算晚.总比没看要好.
明天试试这个想法.JAVA和ASPX上都试试吧...... 30 giugno 转: IIS打不开ASP解决方案 暑假回来后IIS(XP下)突然出了问题(未作过任何设置,只是升级过):无法解释ASP/PHP,对于一个网站设计人员来说,这就象吃饭没有筷子一样不爽。后来试过多种解决方案均无效,算了,等不及,还是用APACHE了,但用起来总是感觉怪怪的;后来又在虚拟机中装了2K Server 来运行IIS,但这样总是太麻烦,遂决定彻底解决XP下IIS无法解释动态页的问题,又在网上搜索之,终于于今日解决,下面将流程写于下,以供来人方便: 出错原因: XP下IIS无法解释ASP等动态页主要是由微软的一个BUG造成的。由于系统原因使IWAM帐号的密码错误,致使出现IIS500内部错误。 IWAM 帐号简介: IWAM 账号是安装 IIS5 时系统自动建立的一个内置账号,主要用于启动进程之外的应用程序的Internet信息服务。IWAM账号的名字会根据每台计算机 NETBIOS 名字的不同而有所不同,通用的格式是IWAM_MACHINENAME,即由“IWAM”前缀、连接线“_”加上计算机的NETBIOS名字组成。我的计算机的NETBIOS名字是JALLEN,因此我的计算机上IWAM账号的名字就是IWAM_JALLEN,这一点与IIS匿名账号ISUR_MACHINENAME的命名方式非常相似。 IIS 500错误最终原因: IWAM账号建立后被Active Directory、IIS metabase数据库和COM+应用程序三方共同使用,账号密码被三方分别保存,并由操作系统负责这三方保存的IWAM密码的同步工作。按常理说,由操作系统负责的工作我们大可放心,不必担心出错,但不知是BUG还是其它什么原因,系统的对IWAM账号的密码同步工作有时会失败,使三方IWAM账号所用密码不统一。当IIS或COM+应用程序使用错误IWAM的密码登录系统,启动IIS Out-Of-Process Pooled Applications时,系统会因密码错误而拒绝这一请求,导致IIS Out-Of-Process Pooled Applications启动失败,也就是我们在ID10004错误事件中看到的“不能运行服务器{3D14228D-FBE1-11D0-995D-00C04FD919C1} ”(这里{3D14228D-FBE1-11D0-995D-00C04FD919C1} 是IIS Out-Of-Process Pooled Applications的KEY),不能转入IIS5应用程序,HTTP 500内部错误就这样产生了。 解决方法: 知道了导致HTTP 500内部错误的原因,解决起来就比较简单了,那就是人工同步IWAM账号在Active Directory、IIS metabase数据库和COM+应用程序中的密码。 操作过程/步骤: 具体操作分三步,均需要以管理员身份登录计算机以提供足够的操作权限(IWAM账号以IWAM_MACHINENAME为例)。 (一)更改Active Directory中IWAM_MACHINENAME账号的密码 因IWAM账号的密码由系统控制,随机产生,我们并不知道是什么,为完成下面两步的密码同步工作,我们必须将IWAM账号的密码设置为一个我们知道的值。 1、控制面版中选择“管理工具”->"计算机管理"->"本地用户和组" ->"用户" 2、找到“IWAM_MACHINENAME”,右击选择“重设密码(T)...”,在跳出的重设密码对方框中给IWAM_MACHINENAME设置新的密码,这儿我们设置成“IIS500”(没有引号的),确定,等待密码修改成功。 (二)同步IIS metabase中IWAM_MACHINENAME账号的密码 可能因为这项改动太敏感和重要,微软并没有为我们修改IIS metabase中IWAM_MACHINENAME账号密码提供一个显式的用户接口,只随IIS5提供了一个管理脚本adsutil.vbs,这个脚本位于C:\inetpub\adminscripts子目录下(位置可能会因你安装IIS5时设置的不同而有所变动)。 adsutil.vbs脚本功能强大,参数非常多且用法复杂,这里只提供使用这个脚本修改IWAM_MACHINENAME账号密码的方法: adsutil SET w3svc/WAMUserPass Password "Password"参数就是要设置的IWAM账号的新的密码。因此我们将IIS metabase中IWAM_MACHINENAME账号的密码修改为“IIS500”的命令就是: c:\Inetpub\AdminScripts>adsutil SET w3svc/WAMUserPass "IIS500" 具体操作:"运行"->"CMD"->转到"C:\Inetpub\AdminScripts>"目录->输入 adsutil SET w3svc/WAMUserPass "IIS500" 确定即可; 修改成功后,系统会有如下提示: WAMUserPass: (String) "******" (三)同步COM+应用程序所用的IWAM_MACHINENAME的密码 实际上可能微软已经发现IWAM账号在密码同步方面存在问题,因此在IIS5的管理脚本中单独为IWAM账号密码同步编写了一个脚本synciwam.vbs,我们就使用这个IWAM账号的同步脚本synciwam.vbs来操作,这个脚本同adsutil.vbs一样,也位于C:\inetpub\adminscripts子目录下(位置可能会因你安装IIS5时设置的不同而有所变动)。 synciwam.vbs脚本用法比较简单: cscript synciwam.vbs [-v|-h] “-v”参数表示详细显示脚本执行的整个过程(建议使用),“-h”参数用于显示简单的帮助信息。 我们要同步IWAM_MACHINENAME账号在COM+应用程序中的密码,只需要执行“cscript synciwam.vbs -v”即可,如下: cscript c:\inetpub\adminscripts\synciwam.vbs -v 具体操作:在以上界面接着输入 cscript synciwam.vbs -v 确定即可; 以下是成功后的系统显示信息: Microsoft (R) Windows Script Host Version 5.6 版权所有(C) Microsoft Corporation 1996-2000。保留所有权利。 WamUserName:IWAM_MACHINENAME WamUserPass:IIS500 IIS Applications Defined: Name, AppIsolated, Package ID w3svc, 0, {3D14228C-FBE1-11d0-995D-00C04FD919C1} Root, 2, IISHelp, 2, IISAdmin, 2, IISSamples, 2, MSADC, 2, ROOT, 2, IISAdmin, 2, IISHelp, 2, Root, 2, Root, 2, Out of process applications defined: Count: 1 {3D14228D-FBE1-11d0-995D-00C04FD919C1} Updating Applications: Name: IIS Out-Of-Process Pooled Applications Key: {3D14228D-FBE1-11D0-995D-00C04FD919C1} 它首先从IIS的metabase数据库找到IWAM账号"IWAM_MACHINENAME"并取出对应的密码“IIS500”,然后查找所有已定义的IIS Applications和Out of process applications,并逐一同步每一个Out of process applications应用程序的IWAM账号密码。 使用synciwam.vbs脚本时,要注意一个问题,那就是在你运行synciwam.vbs之前,必须保证IIS metabase数据库与Active Directory中的IWAM密码已经一致。因为synciwam.vbs脚本是从IIS metabase数据库而不是从Active Directory取得IWAM账号的密码,如果IIS metabase中的密码不正确,那synciwam.vbs取得的密码也会不正确,同步操作执行到“Updating Applications”系统就会报80110414错误,即“找不到应用程序{3D14228D-FBE1-11D0-995D-00C04FD919C1}”。 PS:在同步COM+应用程序所用的IWAM_MACHINENAME的密码时又发生"Error: 8004E00F:"错误,于是在"控制面版"->"管理工具"->"组件服务"->"计算机"->"我的电脑"查看COM+应用程序,恩!居然这里出错,说无法与MICROSOFT 分布式事件处理协调程序交谈……;于是再查,得到如下解决方案。 解决步骤: 1、删除注册表中的键: &S226; HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\MSDTC &S226; HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC &S226; HKEY_CLASSES_ROOT\CID 2、停止MSDTC服务:net stop msdtc 3、卸载MSDTC服务:msdtc -uninstall 4、重新安装MSDTC服务:msdtc -install 5、重新启动电脑 02 aprile 写个演练项目,有个地方阻塞了.大家直接看代码吧....
private void DoRead(IAsyncResult ar)
{ int BytesRead; string strMessage; try
{ // Finish asynchronous read into readBuffer and return number of bytes read. BytesRead = _client.GetStream().EndRead(ar); if (BytesRead < 1) { // if no bytes were read server has close. Disable input window. //MarkAsDisconnected(); return; } // Convert the byte array the message was saved into, minus two for the // Chr(13) and Chr(10) strMessage = Encoding.GetEncoding("gb2312").GetString(readBuffer, 0, BytesRead-2); ProcessCommands(strMessage); // Start a new asynchronous read into readBuffer. _client.GetStream().BeginRead(readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(DoRead), null); }
catch( Exception e) { //MarkAsDisconnected(); Console.WriteLine(e.Message); } } 这里接受到数据以后,收到聊天信息,一个FORM运行下面的代码SHOW一个聊天窗口,并加载聊天信息
chatFrm chatfrtemp;
private void Chatwith(UserConnect sender,string data) { if(chatRoom.Count==0) { chatfrtemp = new chatFrm(sender.Client,sender.Name); chatRoom.Add(sender.Name,chatfrtemp); chatfrtemp.Chat(data); chatfrtemp.Show(); } else if(chatRoom.Contains(sender.Name)) { chatfrtemp = (chatFrm)chatRoom[sender.Name]; chatfrtemp.Chat(data); chatfrtemp.Focus(); //chatfrtemp.Update(); } else { chatfrtemp = new chatFrm(sender.Client,sender.Name); chatRoom.Add(sender.Name,chatfrtemp); chatfrtemp.Chat(data); chatfrtemp.Show(); } } 执行到SHOW()以后,这个窗口就卡住了.如果换用ShowDialog().那么上面的那个接受方法被阻塞,收不到数据,窗口不卡了,不过也失去了显示的意义...哎.有什么能人,帮忙看看吧. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|