最近我负责一个IM项目的开发,服务端和客户端采用TCP协议连接。服务端采用C#开发,客户端采用Delphi开发。在服务端开发中我碰到了各种各样的网络异常断开现象。在处理这些异常的时候有了一些心得,现在写出来和大家分享一下。

那网络异常断开原因主要有那些呢?归纳起来主要有以下两种:

1、客户端程序异常。

  对于这种情况,我们很好处理,因为客户端程序异常退出会在服务端引发ConnectionReset的Socket异常(就是WinSock2中的10054异常)。只要在服务端处理这个异常就可以了。

2、网络链路异常。

  如:网线拔出、交换机掉电、客户端机器掉电。当出现这些情况的时候服务端不会出现任何异常。这样的话上面的代码就不能处理这种情况了。对于这种情况在MSDN里面是这样处理的,我在这里贴出MSDN的原文:

如果您需要确定连接的当前状态,请进行非阻止、零字节的 Send 调用。如果该调用成功返回或引发 WAEWOULDBLOCK 错误代码 (10035),则该套接字仍然处于连接状态;否则,该套接字不再处于连接状态。

  但是我在实际应用中发现,MSDN说的这种处理方法在很多时候根本无效,无法检测出网络已经异常断开了。那我们该怎么办呢?

  我们知道,TCP有一个连接检测机制,就是如果在指定的时间内(一般为2个小时)没有数据传送,会给对端发送一个Keep-Alive数据报,使用的序列号是曾经发出的最后一个报文的最后一个字节的序列号,对端如果收到这个数据,回送一个TCP的ACK,确认这个字节已经收到,这样就知道此连接没有被断开。如果一段时间没有收到对方的响应,会进行重试,重试几次后,向对端发一个reset,然后将连接断掉。

  在Windows中,第一次探测是在最后一次数据发送的两个小时,然后每隔1秒探测一次,一共探测5次,如果5次都没有收到回应的话,就会断开这个连接。但两个小时对于我们的项目来说显然太长了。我们必须缩短这个时间。那么我们该如何做呢?我要利用Socket类的IOControl()函数。我们来看看这个函数能干些什么:

使用 IOControlCode 枚举指定控制代码,为 Socket 设置低级操作模式。

命名空间:System.Net.Sockets 
程序集:System(在 system.dll 中)

语法

C# 
public int IOControl ( 
IOControlCode ioControlCode, 
byte[] optionInValue, 
byte[] optionOutValue 
)

参数 
ioControlCode 
一个 IOControlCode 值,它指定要执行的操作的控制代码。

optionInValue 
Byte 类型的数组,包含操作要求的输入数据。

optionOutValue 
Byte 类型的数组,包含由操作返回的输出数据。

返回值 
optionOutValue 参数中的字节数。

如:

socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);

我们要搞清楚的就是inOptionValues的定义,在C++里它是一个结构体。我们来看看这个结构体:

struct tcp_keepalive 

    u_long  onoff; //是否启用Keep-Alive
    u_long  keepalivetime; //多长时间后开始第一次探测(单位:毫秒)
    u_long  keepaliveinterval; //探测时间间隔(单位:毫秒)
}; 

在C#中,我们直接用一个Byte数组传递给函数:

uint dummy = 0;
byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);//是否启用Keep-Alive
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//多长时间开始第一次探测
BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//探测时间间隔

具体实现代码:

        public static void AcceptThread()
        {
            Thread.CurrentThread.IsBackground = true;
            while (true)
            {
                uint dummy = 0;
                byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
                BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
                BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
                BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);
                try
                {
                    Accept(inOptionValues);
                }
                catch { }
            }
        }

        private static void Accept(byte[] inOptionValues)
        {
            Socket socket = Public.s_socketHandler.Accept();
            socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
            UserInfo info = new UserInfo();
            info.socket = socket;
            int id = GetUserId();
            info.Index = id;
            Public.s_userList.Add(id, info);
            socket.BeginReceive(info.Buffer, 0, info.Buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallBack), info);
        }

好了,这样就成功了。

在C#中利用Keep-Alive处理Socket网络异常断开的方法相关推荐

  1. MVC中利用ViewBag传递Json数据时的前端处理方法

    ** MVC中利用ViewBag传递Json数据时的前端处理方法 ** 用viewBag传递Json字符串到前端时,json字符串中的"会被转义为& quot,前端处理方法为@Htm ...

  2. JQuery中使用Ajax赋值给全局变量失败异常的解决方法,jqueryajax

    我们在用JQuery的Ajax从后台提取数据后想把它赋值给全局变量,但是却怎么都赋不进,为什么呢? 原因其实很简单,我们用的Ajax是异步操作,也就是说在你赋值的时候数据还没提取出来,你当然赋不进去, ...

  3. 常见的Socket网络异常场景分析

    原创:打码日记(微信公众号ID:codelogs),欢迎分享,转载请保留出处. 简介 在目前微服务的背景下,网络异常越来越常见了,而有一些网络异常非常模糊,理解什么情况下会导致什么异常,还是有一定难度 ...

  4. python中语法错误-Python语法错误与异常及异常处理方法

    回顾 在Python进阶记录之基础篇(二十)中,我们介绍了Python面向对象中的类方法和静态方法,以及类中拥有特殊功能的魔法函数.需要重点掌握类方法和静态方法的概念和基本用法,理解魔法函数的作用冰女 ...

  5. CTF pwn中利用pwntools加载不同版本libc调试程序的方法

    在网上找到了很多加载libc的帖子,终于自己走通了一次,现在把方法和资源都整理一下 一.解决方案 python利用pwntools的代码 from pwn import * import pwnlib ...

  6. android+网络下载资源,【已解决】Android中利用HttpClient等库实现网络文件下载

    [问题] 已经获得了Songtaste中歌曲的地址,比如: 中的真实下载地址是: 然后现在想要去下载这样的文件到Android手机的本地某个文件夹中. [解决过程] 1.其中,关于自动处理Cookie ...

  7. 使用SQLDMO中“接口SQLDMO.Namelist 的 QueryInterface 失败”异常的解决方法

    SQLDMO(SQL Distributed Management Objects,SQL分布式管理对象),它封装 Microsoft SQL Server 数据库中的对象.它允许我们通过COM对象, ...

  8. python中exception类的_Python自定义一个异常类的方法

    如何实现自定义一个异常 python内置了许多异常类,为编写代码划定红线,才使调试代码时能及时发现错误.那么我们编写一个模块也可以为使用此模块者划定红线,来约束使用者可用哪些数据,这就需要自定义异常类 ...

  9. KDD CUP 99利用决策分类树进行网络异常检测

    import pandas as pd import numpy as np import matplotlib.pyplot as plt 数据导入与数据探索 数据导入 df=pd.read_csv ...

最新文章

  1. MySQL数据模型图导出ddl脚本_DB2中导出数据库的所有DDL脚本.
  2. HDU 1429 胜利大逃亡(续)
  3. python画图三维-对python mayavi三维绘图的实现详解
  4. 阻塞队列BlockingQueue
  5. 中石油训练赛 - Equidistant(bfs)
  6. j2se--Socket沟通
  7. 集成学习(一)——随机森林以及GBDT
  8. VSS使用手册(ZZ)
  9. 实验二 预测分析算法的设计与实现
  10. 阿里热更新Sophix的故事
  11. Mapstruct使用介绍
  12. 三级数据库知识点学习(五)
  13. 向量的加减(输出重载)
  14. 如何一眼辨别谁有男朋友/女朋友?哈哈哈哈哈哈哈
  15. 强烈推荐一款开源项目! (OPC)微服务能力开放平台!
  16. 3559A sample hifb解析
  17. JavaWeb期末大作业 Javaweb项目 Javaweb Servlet html
  18. Cent os 快捷键设置
  19. vux微信签名拿openid设计
  20. 地下水水质检测方法一览表

热门文章

  1. HRSP热备份路由协议(思科私有协议)
  2. hdu 5367(线段树+区间合并)
  3. poj 3281(最大流)
  4. Django,Ajax,文件上传,ajax发送json数据,基于Ajax的文件上传
  5. C++快速输入输出优化
  6. JAVA --BYTECODE
  7. C#基础第七天-作业-利用面向对象的思想去实现名片-动态添加
  8. python 利用numpy进行数据分析
  9. hibernate映射之多对多双向
  10. Eclipse 3.7 3.x SWT/Jface 开发环境搭建