[转帖]   用C#开发TUXEDO客户端(二)_MQ, Tuxedo及OLTP讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  MQ, Tuxedo及OLTP讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 4426 | 回复: 0   主题: [转帖]   用C#开发TUXEDO客户端(二)        下一篇 
guoyongwei
注册用户
等级:少校
经验:910
发帖:52
精华:6
注册:2013-3-11
状态:离线
发送短消息息给guoyongwei 加好友    发送短消息息给guoyongwei 发消息
发表于: IP:您无权察看 2013-3-14 10:12:42 | [全部帖] [楼主帖] 楼主

2.6 多线程调用

多线程调用时,需要在应用程序上下文初始化时,加入多线程标志。如下:

TypedTPINITtpinfo = newTypedTPINIT();
tpinfo.flags = TypedTPINIT.TPMULTICONTEXTS;
AppContextac = AppContext.tpinit(tpinfo);


2.7 结构数据传递问题

Tuxedo windows客户端的原始API是面向C语言的,因此在很多的服务器程序的编写时,会采用struct结构来会传递数据的方案。对于C结构体数据,在tuxedo中对应的消息类型应该是CArray, 在用C#制作客户端时,可以采用TypedCArray这个类型来传递数据,其中,需要特别注意的问题是.netinterop操作时的一些技术细节。

下面是一个具体的例子:

[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct MYMSGBODY
{
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
      public string usrname; // char usrname[20]
      [MarshalAs(UnmanagedType.U4, SizeConst=4)]
      public uint lLogNo; // unsigned long int lLogNo;
      [MarshalAs(UnmanagedType.I4)]
      public int iRecNum; // int iRecNum;
}


MSDN上有详细的结构体interop类型对应表可以查阅。这里要解释其中几个重要的地方:

l StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)


Pack=4表示以最大4字节边界对齐成员。

CharSet = CharSet.Ansi表示字串是ansi字串。之后再详述。

l MarshalAs(UnmanagedType.U4, SizeConst=4)


此属性标记为此string成员是以值传递的,也就是是一个数组,而不是指针。并指定了长度。这个长度是指C串里包括了结尾0的总长度。

Long类型:这个类型在C.net里的有重要不同,在C中, int的大小根据平台不同有16位长,32位长,(在windows和现代的unix中,一般都是32位), long的长度在windowsunix中一般是32位的,而在.net中,long类型的长度是64位,并且interop很多操作不支持对long型数据的转换,如对结构体取size时,如果有long型字段,就会出现异常,这里要注意。

内存分配的对齐问题

这个问题是最难以讲清楚的问题。在C语句的编译器中,都会有一些关于结构体成员如何对齐地址的编译指令或是伪指令,如VC#pragma pack, __declspec( align() )指令等。这些指令指示编译器如何在内存中排布结构体的成员。

其中,pack=n的意思是:结构体中下一个成员的起始地址,要用“成员类型的长度和n之中的比较小的那个”来对齐。比如说下面的结构体成员:

#program pack(8)
Struct ST_E1{
      char s1[2]; // 0偏移开始占到1位置,共2字节
      int i; // min(sizeof int= 4pack=8)=4, 因此,i的起始地址应该按4对齐
      // 也就是空两个字节,到偏移4处开始,到偏移7,共4字节
      char s2[3]; // min(sizeof char=1, pack=8) = 1, 因此s2的起始地址按1对齐,
      // 也就是从偏移8开始,到10,共3字节。
      Char s3 // 同理,s3占偏移量11,一个字节
}


结构体总长度为12个字节长。


Off

0

1

2

3

4

5

6

7

8

9

10

11

V

S1

S1





i

i

i

i

S2

S2

S2

S3

如果pack指为1呢?那么分配的方式如下:


Off

0

1

2

3

4

5

6

7

8

9

V

S1

S1

i

i

i

i

S2

S2

S2

S3

10个字节长。

可以看出,如果两边的pack值不一样,那么这个结构体在送到目的地之后就会出现成员偏移乱掉的问题(开始想念web services了吧?但是我们不总是能选择所处的条件的)。因此,一定要检查服务器与客户端的这个编译选项是不是一样的。一般情况下,pack=4是比较常见的情况。

有关更多的内存对齐方式的讨论,可以参见本人另一博客文章及其评论内容。

http://www.cnblogs.com/haoxiaobo/archive/2005/09/05/230204.html


字符集问题

字符集是另一个需要注意的兼容性问题。例如上面的成员定义:

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string usrname; // char usrname[20]


在大多数面向C语言的API中,基本上是没有charbyte的区别的,但是.net中对于charbyte则有本质的不同,char是指一个与文化相关的符号,而byte指一个8位二进制数的物理存储单位。一个char需要几个byte来保存,要视字符集编码方式而定,在.netjava里,内部的char都是unicode,一个字符两个字节,而在C中,基本上都是ansi(除了windows nt之后新增的那些 _T类型)。

由于这个原因,一定要认真考虑服务器系统的字符集编码,否则会导致字符串在interop转换时,产生非常令人生气的结果。

在结构体的定义时,charset = charset.ansi即通知了interop程序,字符串在向结构体转换时,要用ansi方式进行转换。

更多有关字符集的讨论,请参见本人另一篇博客文章:

http://xiaobohao.spaces.live.com/blog/cns!D1C72860197EBF38!1250.entry


完整的用结构体内存块数据做消息体,调用tuxedo服务的代码如下:

Bea.Tuxedo.ATMI.Utils.tuxputenv("WSNADDR=//10.1.128.227:9401");
Bea.Tuxedo.ATMI.Utils.tuxputenv("WSINTOPPRE71=yes");
AppContext ac = AppContext.tpinit(null);
RecivedStruct rec; // 这个RecivedSTRUCT即是tuxedo服务所规定的返回消息结构体在C#里的对应定义,请参阅本节之前的说明,对C风格的结构进行C#定义。
SendStruct app = new SendStruct ();// 这个SENDSTRUCT即是tuxedo服务所规定的调用消息结构体在C#里的对应定义,请参阅本节之前的说明,对C风格的结构进行C#定义。
app.sOperCode = “…”; // 几个示意成员
app.sDeptCode = “…”;
//…
// 开始把C#结构体的内容复制为byte[]
// 取得长度
intiAppLen = Marshal.SizeOf(app);
int iRecLen = Marshal.SizeOf(typeof(RecivedStruct));
TypedCArray tbSend = new TypedCArray(iAppLen);
TypedBuffer tbRecive = new TypedCArray(iRecLen);
byte[] arAppData = new byte[iAppLen];
// 分配一个系统堆内存并用于一个指针来指向之
IntPtr pApp = Marshal.AllocHGlobal(iAppLen);
IntPtr pRec= Marshal.AllocHGlobal(iRecLen);
// 将托管结构复制到此地址指向的内存块中。
Marshal.StructureToPtr(app, pApp, false);
// 再将此地址块复制到字节数组中。
Marshal.Copy(pApp, arAppData, 0, iLen);
//把此字节数组绑定到要发送数据中。
tbSend.PutBytes(arAppData);
try
{
      // 调用服务,返回一个typedbuffer.
      ac.tpcall(sServiceName, tbSend, ref tbRecive, 0);
      // 开始从这个返回的内容里取出数据。
      // 初始化一个与返回值相同大小的数组。
      byte[] arRecived = newbyte[tbRecive.Size];
      // 从返回值对象中取出字节数组。
      ((TypedCArray)tbRecive).GetBytes(arRecived, arRecived.Length);
      // 用相反的步子把数据从字节流中复制到C#结构中。
      Marsal.Copy(arRecived, 0, pRec, iRecLen);
      Rec = Marsal.PtrToStructure(pRec, typeof(RecivedStruct));
}
catch (TPException tpex)
{
      Trace.TraceError(tpex.ToString());
}
finally
{
ac.tpterm();
Marshal.FreeHGlobal(pApp);
Marshal.FreeHGlobal(pRec);
}




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论