Socket开发异常总结

Socket异常开发总结

事情概况:今天早上手机充值业务突然停止,客户端又不断提交充值订单上来,短时间出现大量客户投诉,经检查,后在同事Aaron的帮助下,查找出问题原因

首先看一下充值系统架构,手机充值Win Service 会每稍钟从数据库里查询出一条没有提交充值的订单去充值。结构类图概要如下

 

其中充值的类里是去查询订单,送给SocketHelpSubmit方法去充值

主要程序如下

查询Win service 

 

代码
 internal partial class TopupService3 : ServiceBase
    {
        
private readonly Timer _submitTimer;


        
public TopupService3()
        {
          
            _submitTimer 
= new Timer(1000);
            _submitTimer.Elapsed 
+= SubmitTimerElapsed;

            _verifyTimer 
= new Timer(5 * 60 * 1000);
            _verifyTimer.Elapsed 
+= VerifyTimerElapsed;
        }

        
protected override void OnStart(string[] args)
        {
            _submitTimer.Start();
        }

        
protected override void OnStop()
        {
            _submitTimer.Stop();
        
        }

        
internal void SubmitTimerElapsed(object sender, ElapsedEventArgs e)
        {
            _submitTimer.Stop();
           
            
try
            {

             
//查询提交充值            

            }
            
catch (SqlException sqlException)
            {
               
   
            }
            
catch (Exception exception)
            {

            }
            
finally
            {
                _submitTimer.Start();
            }
       }
   }

 

 

提交充值的SocketHelp

 

代码
 public class SocketHelp
    {
        
#region  手机充值

        
public static string Submit()
        {
            
try
            {
                IPAddress ip 
= IPAddress.Parse(Host);
                var ipe 
= new IPEndPoint(ip, Port); //把ip和端口转化为IPEndPoint实例
                DFSubmitLog.Debug("建立sockets连接");
                c 
= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //创建一个Socket
                DFSubmitLog.Debug("Connecting......");
                c.Connect(ipe); 
//连接到服务器
                byte[] bs = Encoding.UTF8.GetBytes(upOrderContent);
                c.Send(bs, bs.Length, 
0); //发送测试信息
                string recvStr = "";
                var recvBytes 
= new byte[1024];
                Thread.Sleep(SocketTimeout);
                
int bytes = c.Receive(recvBytes, recvBytes.Length, 0);
                recvStr 
+= Encoding.UTF8.GetString(recvBytes, 0, bytes);
                c.Close();
                
return recvStr;
            }
            
catch (ArgumentNullException e)
            {
            
                
return "";
               
            }
            
catch (SocketException e)
            {
                
return "";
            }
            
finally
            {
                
if (c != null)
                {
                   
                }
            }

    }

 

 

以上程序的Socket部份有问题,就是在Socket发送信息出去以后,程序就相当于停在那了,没有接收信息。此时不知道是发送的过程中出了问题,还是接收的过程中出了问题,总之程序是没有获取到异常。引用Aaron的原话是Socket处于无限期等待当中,在通讯过程中,由于客户端是周期性地向服务器请求数据,若客户端的请求指令或是服务端发来的数据包丢失的话,那么客户端将一直等待,陷入假死状态。

针对这个问题,把Socket程序修改成同步Socket,代码如下

 

代码
public class SocketHelp
    {
        
#region  手机充值

        
public static string Submit(Order record, string productInfo)
        { 
            
try
            {
                IPAddress ip 
= IPAddress.Parse(Host);
                IPEndPoint ipe 
= new IPEndPoint(ip, Port); 
                
using (Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                {
                    clientSocket.ReceiveTimeout 
= SocketTimeout;
                    clientSocket.SendTimeout 
= SocketTimeout;
                    DFSubmitLog.Debug(
"Connecting......");
                    clientSocket.Connect(ipe);
                    
byte[] bs = Encoding.UTF8.GetBytes(upOrderContent);
                    clientSocket.Send(bs, bs.Length, 
0);
                    
string recvStr = String.Empty;
                    
byte[] recvBytes = new byte[1024];
                    Thread.Sleep(SocketTimeout);
                    
int bytes = clientSocket.Receive(recvBytes, recvBytes.Length, 0);
                    recvStr 
= Encoding.UTF8.GetString(recvBytes, 0, bytes);
                    clientSocket.Shutdown(SocketShutdown.Both);
                    clientSocket.Close();
                    
return recvStr;
                }
            }
            
catch (ArgumentNullException e)
            {
                
return String.Empty;
            }
            
catch (SocketException e)
            {
                
return string.Empty ;
            }
            
catch (Exception e)
            {
                
return string.Empty;
            }
        }

    }

 

 

指定了,发送数据时间,接收数据时间,超过这个时间了就引发socket异常

修改了程序以后,我把其中变量SocketTimeout设置成500,即500毫秒,由于这个时间还是不够长,在程序起动的时候会引发   SocketException: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败

后来我把SocketTimeout设置成2000,即2秒钟就没有问题,程序可以正常运行

相关Socket类的MSND地址是:

http://msdn.microsoft.com/zh-cn/library/attbb8f5(v=VS.80).aspx

其中关于Socket类的几个属性资料摘录如下

1.       当数据发送和数据接收完成之后,可使用 Shutdown 方法来禁用 Socket。在调用 Shutdown 之后,可调用 Close 方法来释放与 Socket 关联的所有资源

2.       Socket.SendTimeout 属性

获取或设置一个值,该值指定之后同步 Send 调用将超时的时间长度。

命名空间:System.Net.Sockets属性值

超时值(以毫秒为单位)。如果将该属性设置为 1 499 之间的值,该值将被更改为 500。默认值为 0,指示超时期限无限大。指定 -1 还会指示超时期限无限大。

3 Socket.ReceiveTimeout 属性

获取或设置一个值,该值指定之后同步 Receive 调用将超时的时间长度。

命名空间:System.Net.Sockets

程序集:System(在 system.dll 中)

超时值(以毫秒为单位)。默认值为 0,指示超时期限无限大。指定 -1 还会指示超时期限无限大。

异常

--------------------------------------------------------------------------------

异常类型 条件

SocketException

   试图访问套接字时发生错误。

ObjectDisposedException

   Socket 已关闭。

ArgumentOutOfRangeException

 为设置操作指定的值小于 -1

--------------------------------------------------------------------------------

此选项仅适用于同步 Receive 调用。如果超过超时期限,Receive 方法将引发 SocketException

 

 

其它参考文摘

http://blog.163.com/gfmq_312/blog/static/17823682200951711112373/

在通讯过程中,由于客户端是周期性地向服务器请求数据,若客户端的请求指令或是服务端发来的数据包丢失的话,那么客户端将一直等待,陷入假死状态。由于默认为无超时,所以一定要记得设定客户端接收的超时时间,若服务器端无相应,客户端应该抛出异常,而不是毫无意义的等待

 

posted @ 2010-12-17 16:08  赵一村  阅读(4198)  评论(2编辑  收藏  举报