抽象类是否可继承接口?抽象类里面可以包括抽象方法和非抽象方法,而接口只是定义了方法的原型,没有方法的具体定义。从这方面来分析,抽象类是可以继承接口的。抽象类继承接口后,一定要实现接口中的成员方法么?答案是否定的。抽象类继承接口后,可以实现接口中的成员方法,也可以不实现。如果不实现接口中成员方法,只需将该方法声明为abstract类型就可以了。interface ITemp { void IM();...Read More
posted @ 2010-05-15 23:16 Edenia Views(784) Comments(5) Edit
CLR类型系统CTS(Common Type System)将类型分为两种:值类型与引用类型。对应简单值的类型称为值类型,对应传统“对象”的类型称为引用类型。值类型直接包含数据,值类型的实例分配在堆栈或者内联结果上面,值类型的实例不需要进行垃圾回收。CLR针对C#语言内置了15种类型:object、string、char、bool、decimal、byte、sbyte、sh...Read More
posted @ 2010-05-15 23:10 Edenia Views(904) Comments(0) Edit
AnyTao“字符串驻留”一文链接地址:http://www.cnblogs.com/anytao/archive/2008/08/27/must_net_22.html昨天,看到AnyTao的这篇文章时是第一次听说“字符串驻留”,呵呵,真惭愧。AnyTao没有在文章中给出8个测试的结果原因,下面是我自己的分析。1. { string s1 = "...Read More
posted @ 2010-05-06 20:16 Edenia Views(1323) Comments(23) Edit
class A { public A() { PrintFields(); } public virtual void PrintFields() { } } class B : A { int x = 1; int y; public B() { y = -1; } public override void PrintFields() { Console.WriteLine("x={0},y={...Read More
posted @ 2010-05-03 22:51 Edenia Views(308) Comments(3) Edit
  1. 抽象类是否可继承接口?

抽象类里面可以包括抽象方法和非抽象方法,而接口只是定义了方法的原型,没有方法的具体定义。从这方面来分析,抽象类是可以继承接口的。

 

  1. 抽象类继承接口后,一定要实现接口中的成员方法么?

答案是否定的。

抽象类继承接口后,可以实现接口中的成员方法,也可以不实现。如果不实现接口中成员方法,只需将该方法声明为abstract类型就可以了。

interface ITemp

    {

        void IM();

    }

 

abstract class CTemp : ITemp

    {

        public abstract void IM();

    }

 

  1. (抽象)类继承类和接口的顺序是如何的?

 

    class A

    {

    }

 

    interface IA

    {

    }

 

    interface IB

    {

}

 

能否像下面一样定义一个类?

class B : IA, IB, A

    {

    }

 

答案是不可以。编译器会告诉你:基类A必须在任何接口之前。

所以应该写成:

class B : A, IA, IB

    {

    }

这是为什么呢?

下面是一家之言,仅供参考,不能保证所说的正确性。如果有误,敬请斧正。

针对上面的例子,假设类A中有一个方法时M,接口IA中同时声明了一个方法M。如果类B是按照IA, IB, A这样的顺序继承,则在B类型执行内存分配时,B对象的方法表里面首先加载接口IA中的方法的实现,之后是IB的,再之后是A的。因为A中也有方法M,所以这样的顺序是不是会认为M是IA中成员M的实现呢?如果类B是按照A, IA, IB这样的顺序继承,就不存在这样的情况了。

 

  1. 接口可以继承抽象类么?

接口只定义成员方法的原型,并不包括成员方法的实现。抽象类里面可能有实现的方法。如果说,接口可以继承抽象类,那这就和“接口只声明成员方法的原型,而没有具体方法的定义”相违背。

所以说,接口不能继承抽象类。

 

  1. 接口中可以包含属性和事件,但是不可以包含字段。

       属性和事件在编译的时候编译器会自动将其转化成方法。

 

  1. 接口中不能再声明类型。例如接口中不能再定义类,不能再声明接口等等,但是类里面可以再定义类,再声明接口。

 

例如下面是合理的:

class N

    {

        interface IU { }

        class CU { }

}

 

interface IB

    {

        interface IU { }

        class CU { }

    }

或者

interface IB

    {

        interface IU { }

    }

是通不过编译的。

 

  1. 类如何做到隐藏接口中的实现方法?

如果一个类实现了一个接口中的方法M,则M应该声明为public,否则编译无法通过。但是如何让类的引用无法使用接口中的方法M呢?例如

http://images.cnblogs.com/cnblogs_com/edenia/243832/r_HideMethodsFromInterface.JPG

看上图,你会发现对象a的方法列表中并没有方法M。如何做到呢?

http://images.cnblogs.com/cnblogs_com/edenia/243832/r_HideMethodsFromInterface_1.JPG

 

是的,通过接口名称限定类中实现的接口中的方法。

像上面那样,如何访问到M呢?通过接口类型的引用访问即可,例如IA a = new CA();。

 

再讨论这样做是否有意义?

接口是为不相关的类提供通用的功能的,像上面,如果M对类CA的引用是隐藏的,我们完全可以将M这样的方法脱离出来再定义一个接口就哦可了。所以照这样说,上面那种做法是没有必要的。

posted @ 2010-05-15 23:16 Edenia Views(784) Comments(5) Edit

      CLR类型系统CTS(Common Type System)将类型分为两种:值类型与引用类型。对应简单值的类型称为值类型,对应传统“对象”的类型称为引用类型。

值类型直接包含数据,值类型的实例分配在堆栈或者内联结果上面,值类型的实例不需要进行垃圾回收。CLR针对C#语言内置了15种类型:object、string、char、bool、decimal、byte、sbyte、short、ushort、int、uint、long、ulong、float、double。

      引用类型包括自描述类型、指针类型以及接口类型。其中自描述类型又分为类类型和数组。而类类型又分为自定义类类型、装箱的值类型以及委托类型。

      除了枚举类型以外的值类型的直接基类型是System.ValueType,而枚举类型直接继承于System.Enum,System.Enum又继承于System.ValueType。除了接口外的数据类型均继承于System.Object。

      引用类型的实例是分配在托管堆上面,在对象不再被引用时,GC堆将会对其进行回收,但是对象的引用是分配在堆栈上面的。

下面主要说struct类型。

      struct是值类型,继承于System.ValueType。struct类型可以像class类型一样拥有自己的方法、字段,也可以实现接口,但不支持继承。但是不能给C#的struct指定显示的基类型,即隐含的基类型总是System.ValueType。不能将struct声明为abstract或者sealed,编译器将隐式地添加sealed修饰符。结构的值存在堆栈或者内联上。

 

posted @ 2010-05-15 23:10 Edenia Views(904) Comments(0) Edit

AnyTao“字符串驻留”一文链接地址:http://www.cnblogs.com/anytao/archive/2008/08/27/must_net_22.html

昨天,看到AnyTao的这篇文章时是第一次听说“字符串驻留”,呵呵,真惭愧。

AnyTao没有在文章中给出8个测试的结果原因,下面是我自己的分析。

 

1.

   static void Main() 

        {

            string s1 = "abc";

            Console.WriteLine(string.IsInterned(s1) ?? "null");

        }

结果输出为:abc

 

分析:首先“??”符号也是在AnyTao文章中看到的,也是我第一次知道,该符号为二元运算符,如果“??”的左边不为空则返回符号左边实际应返回的数据(如果左边不是计算得到的那就直接返回左边的内容),否则返回“??”符号右边的内容。

其次就是“字符串驻留”。当CLR初始化时,它会创建一个内部的散列表,其中的键为字符串,值为指向托管堆中字符串对象的引用。刚开始,此散列表为空,当JIT编译器编译方法时,它会在散列表中查找每一个文本常量字符串,如果查找到则返回散列表中该常量字符串对应的(也即托管堆中对象的引用);如果找不到,则在托管堆中构造一个新的string对象指向该常量字符串,然后将该常量字符串和指向该对象的引用添加到散列表里面。

而string.IsInterned()方法判断指定参数是否在散列表里面,该方法接收一个string作为参数,并会在CLR内部的散列表中查找它。如果CLR内部的散列表中含有该字符串,IsInterned将返回散列表中保存的字符串的对象引用;否则,将返回null,而且不会静该字符串添加到散列表中。

所以,当编译执行到string s1 = "abc";时,CLR首先会在托管堆构造一个新的string对象(指向“abc”),然后将文本常量字符串“abc”和指向s1对象的引用添加到散列表里面。然后string.IsInterned(s1)判断s1是在散列表里面的,所以会输出“abc”。

 

记住,CLR的内部散列表里面保存的是源代码的文本常量字符串。CLR的内部散列表是在进程中的所有AppDOMAIN(应用程序域)不再引用这些字符串对象时,垃圾收集器才对其进行释放。

 

2.

static void Main() 
        { 
            string s1 = "ab"; 
            s1 += "c"; 
            Console.WriteLine(string.IsInterned(s1) ?? "null"); 
        }

输出结果:null

分析:

 

如图,通过查看ILDASM发现,执行ldstr的只是两个常量字符串"ab"和"c",所以字符串”abc”并不在散列堆里面。所以输出结果为”null”。

 

3.

static void Main() 
        { 
            string s1 = "abc"; 
            string s2 = "ab"; 
            s2 += "c"; 
 
            string s3 = "ab"; 
            
            Console.WriteLine(string.IsInterned(s1) ?? "null"); 
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
 
            Console.WriteLine(string.IsInterned(s3) ?? "null"); 
        }

输出结果:

abc

abc

ab

通过1、2的分析,我们知道编译结束时散列表里面的文本常量字符串有:”abc”、”ab”、”c”。

string.IsInterned(s1)和string.IsInterned(s3)就不用说了,答案显而易见。说一下string.IsInterned(s2),s2的结果是”abc”,而”abc”在散列表里面是存在的,所以string.IsInterned(s2)结果不为空,将返回会返回字符串”abc”的引用

,我想应该是s1的地址吧。所以Console.WriteLine(string.IsInterned(s2) ?? "null"); 输出”abc”。
 
针对问题3,我增加了一个对比示例,如下:

static void Main()

        {

            string s2 = "ab";

            s2 += "c";

            string s3 = "ab";

            Console.WriteLine(string.IsInterned(s2) ?? "null");

            Console.WriteLine(string.IsInterned(s3) ?? "null"); 
}
输出结果:
null
ab
这样的输出结果与AnyTao问题3的结果区别的原因应该比较清楚了吧?O(∩_∩)O~
 
 
4.
static void Main() 
        { 
            string s1 = "abc"; 
            string s2 = "ab"; 
            string s3 = s2 + "c"; 
            
            Console.WriteLine(string.IsInterned(s3) ?? "null"); 
        }
输出结果:abc
这个由前面的分析可以看出结果,在此不再分析。
 
5.
static void Main() 
        { 
            string s2 = "ab"; 
            s2 += "c"; 
            
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
            
            string s1 = "abc"; 
        }
输出结果:
abc
分析:不管string s1 = "abc";是放在Console.WriteLine(string.IsInterned(s2) ?? "null"); 前面还是后面,在编译的时候,文本常量字符串"abc"都被放到了CLR的散列表里面,所以Console.WriteLine(string.IsInterned(s2) ?? "null");输出”abc”是无容置疑的~。
 
6.

static void Main()

        {

tring s2 = "ab";

            s2 += "c";

            Console.WriteLine(string.IsInterned(s2) ?? "null"); //null

            string s1 = GetStr();

}

 

        private static string GetStr() { return "abc"; }
 
输出结果:null
分析:虽然函数GetStr()返回的是”abc”,但是散列表里面只存在”ab”和”c”。
 
7.
public const string s1 = "abc"; 
        
        static void Main() 
        { 
            string s2 = "ab"; 
            s2 += "c"; 
            
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
        }
输出结果:null
分析:
先看一下ILDASM的结果吧:
 
由此看出散列表里面存在的是”ab”和”c”两个文本常量。所以输出结果是”null”。
 
8.
public static string s1 = "abc"; 
        
        static void Main() 
        { 
            string s2 = "ab"; 
            s2 += "c"; 
            
            Console.WriteLine(string.IsInterned(s2) ?? "null"); 
        }
输出结果:abc
分析:为什么7输出null,而8输出abc呢?什么原因?呵呵,一个是常量一个是静态变量。对比7的反编译我们看一下8的,如图:
 
我们看到,8的反编译结果里面多了一个函数.cctor,它是类型构造器,在类中存在编译时的静态成员初始化时,类会自动添加一个私有的构造函数,反编译后IL里面体现为.cctor,而.ctor是构造函数。我们由.cctor得内容可以看得出”abc”文本常量字符串放入了散列表里面,所以输出结果为”abc”。
 
 
补充:
字符串驻留机制基于String对象的恒定不变性,有助于节省内存。对于动态创建的字符串(比如:string+variable;variable+variable),驻留机制便不起作用,因为驻留机制仅是将源代码中的文本常量字符串放入散列表里面的。而诸如string+variable;variable+variable这些是调用了concact()函数,这可以由反编译代码看出。
 
感谢:
感谢AnyTao,是你的分享,让我提高了自己。
posted @ 2010-05-06 20:16 Edenia Views(1323) Comments(23) Edit

class A
    {
        public A()
        {
            PrintFields();
        }
        public virtual void PrintFields() { }
    }
    class B : A
    {
        int x = 1;
        int y;
        public B()
        {
            y = -1;
        }
        public override void PrintFields()
        {
            Console.WriteLine("x={0},y={1}", x, y);
        }
    }


    class Program
    {       
        static void Main(string[] args)
        {
            B b = new B();
            //A a=new A();
            //a.PrintFields();
            b.PrintFields();
           
            Thread.Sleep(100000);
        }
    }

posted @ 2010-05-03 22:51 Edenia Views(308) Comments(3) Edit

看了AnyTao的按值传递与按引用传递,我感觉自己以前好像并没有弄明白到底什么是按值传递,什么是按引用传递。以前,误以为:传递的参数为引用类型的就是按引用传递。其实,这是一个误解。

下面先以一个例子分析:

public class COboject

    {

        public int i = 10;

}


 

示例一:直接对象的成员的值

 

public class Form1

{

private void Form1_Load(object sender, EventArgs e)

        {

COboject abf = new COboject();

            MessageBox.Show(abf.i.ToString());//10

            ChangeValue(abf);//50

            MessageBox.Show(abf.i.ToString());//50

}

 

 

    private void ChangeValue(COboject cobj)

        {

            cobj.i = 50;

            MessageBox.Show(cobj.i.ToString());

        }

}


 

用内存图示进行分析:

 

 

说明一点,值类型与引用类型内存分配的不同是值类型分配在线程堆栈中,引用类型分配在托管堆中,但是线程堆栈中会分配该引用类型在托管堆中的地址。

所以,调用ChangeValue()函数时在线程堆栈中执行了对abf对象的拷贝。

 

示例二:改变对象的引用(表面上改变)

为什么后面括号里面写上“表面上改变”呢,原因是调用函数的对象实质上未改变,只是被调用函数内改变了。

public class Form1

{

private void Form1_Load(object sender, EventArgs e)

        {

COboject abf = new COboject();

            MessageBox.Show(abf.i.ToString());//10

            ChangeObject(abf);//50

            MessageBox.Show(abf.i.ToString());//10

}

 

 

    private static void ChangeObject(COboject cobj)

        {

            COboject tempObj = new COboject();

            tempObj.i = 50;

            cobj = tempObj;

            MessageBox.Show(cobj.i.ToString());

        }

}


 

用内存图示进行分析:

 

 

说明:对象在被调用函数内的指针发生变化了,但是这个对象只是调用函数内的拷贝,所以调用函数内的对象地址不变。

 

示例三:调用函数内对象的真正改变

 

public class Form1

{

private void Form1_Load(object sender, EventArgs e)

        {

COboject abf = new COboject();

            MessageBox.Show(abf.i.ToString());//10

            ChangeRef(ref abf);//50

            MessageBox.Show(abf.i.ToString());//50

}

 

 

    private static void ChangeRef(ref COboject cobj)

        {

            COboject tempObj = new COboject();

            tempObj.i = 50;

            cobj = tempObj;

            MessageBox.Show(cobj.i.ToString());

        }


 

使用ref关键字,告诉编译器,我传递的是对象的地址的地址,所以当在被调用函数内部更改对象的地址时,调用函数的对象地址也改变了。具体怎么画这个内存图示,虽然脑袋里面有个概念,可是不知道怎么画了,不好意思,大家能理解意思就好。如果有哪位高手可以不吝赐教,给出改图示,LZ在此大谢特谢!!!

posted @ 2010-04-29 22:17 Edenia Views(851) Comments(5) Edit

这篇废话不多说,直接上代码。

首先说明,通讯过程中的异常均不进行处理(连接异常除外),由超时重发控制。

 

一、获取SOCKET连接类TimeOutSocket

 

public class TimeOutSocket

    {

        private static bool IsConnectionSuccessful = false;//连接是否成功

        private static Exception socketexception;

        private static ManualResetEvent TimeoutObject = new ManualResetEvent(false);

 

        public static Socket Connect(IPAddress ipAddress, int port, int timeoutMSec)

        {

            TimeoutObject.Reset();//将事件设为非终止状态,阻止线程

            socketexception = null;

 

            Socket temp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //异步连接

            temp.BeginConnect(new IPEndPoint(ipAddress, port), new AsyncCallback(CallBackMethod), temp);

 

            if (TimeoutObject.WaitOne(timeoutMSec, false))

            {

                //在连接超时时间范围内等待信号量为真则返回SOCKET

                if (IsConnectionSuccessful)

                {

                    return temp;

                }

                else

                {

                    throw new TimeoutException("连接远程主机失败!请检查网络连接是否通畅。");

                }

            }

            else

            {

                //关闭连接,并释放所有相关资源

                temp.Close();

                throw new TimeoutException("连接超时!请检查网络连接是否通畅。");

            }

        }

 

        /// <summary>

        /// 异步连接成功后回调函数

        /// </summary>

        /// <param></param>

        private static void CallBackMethod(IAsyncResult asyncresult)

        {

            try

            {

                IsConnectionSuccessful = false;

                Socket client = asyncresult.AsyncState as Socket;

                //结束挂起的异步连接请求

                client.EndConnect(asyncresult);

                IsConnectionSuccessful = true;

            }

            catch (Exception ex)

            {

                IsConnectionSuccessful = false;

                socketexception = ex;

            }

            finally

            {

                //允许其它线程继续

                TimeoutObject.Set();

            }

        }

}


 

二、通讯变量(常量)

 

private const int ConnectTimeOut = 6000;//连接超时时间,以ms为单位

private byte[] data = new byte[1024];//接收字节数组

private int recvSize = 1024;//接收数据个数

private static int XH = 1;//发送的命令序号 

//下面两个类库ConvertLibrary和DealProtocolData是我封装的类库,在后续中将逐一给出

ConvertLibrary convertData = new ConvertLibrary();//对数据提供转换支持

DealProtocolData dealProtocolData = new DealProtocolData();//按要求处理协议结果  

  

 

三、委托

 

委托,实质上就是指向函数的指针,也是一个类。

        delegate void DealRecvMsgHandler(string str);//用于处理接收到的数据

 

上面一句用ILDASM反编译后如下:

 

可以看出,系统默认生成了其构造函数(.ctor是构造函数),以及成员函数BeginInvoke、EndInvoke以及Invoke。自己定义的委托继承于System.MulticastDelegate(反编译中extends看出)。

 

 

四、异步接收数据回调函数ReceivData()

 

/// <summary>

        ///异步接收数据回调

        /// </summary>

        /// <param></param>

        private void ReceivData(IAsyncResult iar)

        {

            Socket remote = (Socket)iar.AsyncState;

            int recv = 0;

            try

            {

                recv = remote.EndReceive(iar);//异常:远程主机强迫关闭了一个现有的连接

            }

            catch

            {

            }

            string tempStr = convertData.ByteToHex(data, recv);//字节数组转化成16进制

 

            if (tempStr.Length > 0)

            {         

                // DealRecvMsg数据处理函数     

                DealRecvMsgHandler handler = new DealRecvMsgHandler(DealRecvMsg);

                handler.Invoke(tempStr);//同步数据处理

            }

            else

            {

               //由于用了第三方的串口转网口模块,所以有时候会收到空的情况,这时继续接收

                    try

                    {

                        remote.BeginReceive(data, 0, recvSize, SocketFlags.None, new AsyncCallback(ReceivData), PublicCommunication.client);

                    }

                    catch { }

            }

        }


 

五、异步发送数据回调函数(SendData)

 

/// <summary>

        ///异步发送数据回调

        /// </summary>

        /// <param></param>

        private void SendData(IAsyncResult iar)

        {

            Socket remote = (Socket)iar.AsyncState;

            try

            {

                int send = remote.EndSend(iar);

 

                currentSendedOrderDate = DateTime.Now;//记录下发送时间,用于重发时使用

                if (listenerTimer.Enabled == false)// listenerTimer用于超时重发

                    listenerTimer.Enabled = true;

                currentSendedOrderEnum = GetSendedOrderType(sendMsg);//获取已发送命令类型

                currentSendedOrderXH = GetSendedOrderXH(sendMsg);//获取已发送命令序号

 

 //发送后,异步接收

                remote.BeginReceive(data, 0, recvSize, SocketFlags.None, new AsyncCallback(ReceivData), PublicCommunication.client);

            }

            catch

            {

            }

        }


 

 

 

六、接收数据处理函数(只判断命令的合法性,决定下一个命令。具体的数据需要另需委托进行处理。)

 

private void DealRecvMsg(string orgTempStr)

        {

//这里面是一个while循环,循环的条件是命令长度大于某个最小指,每次循环按命令包中命令长度取出命令,并将剩余的接收到的命令用于循环。

//由于通讯追循一定的流程,所以在这里由接收到的命令可以决定下一个命令

//大致给一下这里面的程序

 

while (orgTempStr.Length >= 12)

                    {

                        try

                        {

                            string tempStr = orgTempStr.Substring(0, int.Parse(convertData.ConvertString(orgTempStr.Substring(2, 2), 16, 10)) * 2);//取出一条命令

                            orgTempStr = orgTempStr.Substring(int.Parse(convertData.ConvertString(orgTempStr.Substring(2, 2), 16, 10)) * 2);// orgTempStr保留剩余的命令

 

                            //判断数据合法性:根据协议接收到的数据长度至少为12,而且保证接收到的数据无误(校验码), GetStrXOR()函数是获取校验码,后面给出

 

                            if (tempStr.Length >= 12

                                && dealProtocolData.GetStrXOR(tempStr.Substring(0, tempStr.Length - 2)).Equals(tempStr.Substring(tempStr.Length - 2), StringComparison.OrdinalIgnoreCase))

                            {

                                string workState = tempStr.Substring(8, 2);//获取仪器工作状态

                                string order = tempStr.Substring(4, 2);//获取命令字

 

                                //如果不是上次发送的命令则丢弃当前数据

                                if (!JudgeReceiveOrderWithSendOrder(order, currentSendedOrderEnum))

                                    continue;

                                //命令序号不同则丢弃

                                if (!(int.Parse(convertData.ConvertString(tempStr.Substring(6, 2), 16, 10)) == currentSendedOrderXH))

                                    continue;

 

                                currentSendedOrderDate = DateTime.MinValue;//收到命令后,设置命令的发送时间为DateTime的最小值

                                ReSendTimeCount = -1;//超时重发次数

 

//"00"应该定义成常量,方便使用时如果"00"改为”ff”时不用满程序中修改

                                if (workState.Equals("00", StringComparison.OrdinalIgnoreCase))//仪器正常工作

                                {

if (order.Equals(ProtocolContent.SetParamsCommandWord, StringComparison.OrdinalIgnoreCase))//设置参数命令的返回

                                    {

//决定下一个要发送的命令

sendMsg =……;

}

………..

………..

 

byte[] msg = convertData.HexToByte(sendMsg);

                                    try

                                    {

                                        //异步发送数据

                                        PublicCommunication.client.BeginSend(msg, 0, msg.Length, SocketFlags.None, new AsyncCallback(SendData), PublicCommunication.client);

 

                                        //处理发送序号,保证发送序号在1-255之间

                                        XH = (XH + 1) % 256;

                                        if (XH == 0)

                                            XH = 1;

                                    }

                                    catch

                                    {

                                    }

 

}


 

 

七、超时重发

 

/// <summary>

    /// 命令的枚举值

    /// </summary>

    public enum OrderEnum

    {

        UnKnown = 0

    }

 

private static string sendMsg = String.Empty;//记录每次发送的命令

private static OrderEnum currentSendedOrderEnum = OrderEnum.UnKnown;//记录本次发送的命令的类型

private static int currentSendedOrderXH = -1;//当前发送命令的序号

private static DateTime currentSendedOrderDate = DateTime.MinValue;//最近一次发送命令的时间

private System.Timers.Timer listenerTimer;//用于监控命令发送的定时器

private static int ReSendTimeCount = -1;//重新发送命令的次数

 

 

listenerTimer = new System.Timers.Timer(25);

listenerTimer.Elapsed += new System.Timers.ElapsedEventHandler(listenerTimer_Elapsed);

listenerTimer.Enabled = false;

 

private void listenerTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)

        {

            if (currentSendedOrderDate != DateTime.MinValue)//当没有收到数据时

            {

                double timeInterval = (DateTime.Now - currentSendedOrderDate).TotalMilliseconds;

                if (timeInterval >= 1000)//1S重发

                {

                    ReSendTimeCount++;

                    if (ReSendTimeCount >= 3)

                    {

                        //超时处理

……..

                        return;

                    }

//未超时三次,继续重发

                    SendMsgWhenTimeOut(sendMsg);

                }

            }

        }


 

八、通讯结束或者超时后的处理

 

当无通讯时,客户端关闭,服务器是检测不到的,所以我在做的时候,一次通讯结束或者超时后都会将连接关闭并置为空。

private void WhenCommunicateOver()

{

listenerTimer.Enabled = false;

try

            {

                PublicCommunication.client.Close();

            }

            catch { }

            finally

            {

                PublicCommunication.client = null;

            }

}


 

九、尝试三次连接

 

private void startCommunication()

        {

            int connectTime = 0;

            do

            {

                try

                {

                    PublicCommunication.client = TimeOutSocket.Connect(IPAddress.Parse(PublicModel.InstrumentIP), PublicModel.InstrumentPort, ConnectTimeOut);                    

                    break;

                }

                catch (Exception ec)

                {

                    connectTime++;

                    PublicCommunication.client = null;

                    if (connectTime >= 3)

                    {

                        WhenCommunicateOver();

                        lock (whetherShowErrorMsg)

                        {

                            if (!((bool)whetherShowErrorMsg))

                            {

                                MessageBox.Show(this, ec.Message, "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Error);

                                whetherShowErrorMsg = true;

                            }

                        }

                        Application.DoEvents();

                        return;

                    }

                }

            }

            while (connectTime < 4);

}

posted @ 2010-04-29 22:09 Edenia Views(634) Comments(0) Edit
通讯编程上位机软件实现(SOCKET)Read More
posted @ 2010-04-27 22:13 Edenia Views(923) Comments(1) Edit