电子病历V6产品功能
电子病历宕机问题解决
常用SQL语句汇总
常用术语
产品简述说明
疑难杂症篇
服务器宕机起不来
门诊释放情况
TCP连接的释放
患者无法新建文书
尝试读取或写入受保护的内存。这通常指示其他内存已损坏。
电子病历部分界面会缩小情况
EPM平台相关
统计各个区的需求
问题记录 回退功能的有此权限的人员
主键 索引
电子病历功能大全图
嘉和Linux服务维护常见问题
Linux排查思路
OCX页眉情况汇总处理
接口定义清单
病案首页功能
A01 登录相关
B01 诊断录入
C01 患者列表
C02 刷新患者数据
D01 新建、保存、元素赋值病历
D02 病历签名(前中后)
D03病历修改申请、编辑
D04删除病历
D5打开病历
D06 刷新诊断
D07手术信息维护
D08 按钮等元素点击触发
D09 会诊接口
D10 特殊-牙位图等
D11 患者签名接口
D12 病历书写位置添加按钮
D13 文书打印
D14 病历完成
D99 病历书写作废接口
E01 质控接口
F01临床路径接口
G01手术申请
H 检验、检查、医嘱
I01 病案归档
J01 会诊
K01单病种质控
L01 检验危急值
L02 检查危急值
J01 患者过敏源
电子病历功能技术参数
门诊电子病历显示其他医院的页眉问题
JC-XX系统集成分类汇总
严重精神障碍患者管理治疗数据直报系统
JC01 CDSS或AI内涵质控集成
惠每CDSS接触
嘉和CDSS接口开发方案
灵医智惠
智能病历质控系统
JC02 DRG/DIP
黄石妇幼前置接口文档(赛思软件)
艾登DRG/DIP协作管理平台
国新健康院端系统信息采集接口 开发文档V2.0
DLL调用界面样例
院端智慧医保管控平台接口规范
JH03 公卫/传染病/院感
公卫软件三方软件交互-3.0
JC04 单病种集成
卫宁单病种管理软件对外接口文档
his嵌入病历书写清单
电子病历报表分析
TNM评估报表
电子病历评级
门诊速度
多院区模板几种样式
本文档使用 MrDoc 发布
-
+
首页
TCP连接的释放
# TCP连接的释放 ### 数据传输结束后,通信的双方都可以释放连接,并停止发送数据。假设现在双端都处于ESTABLISHED状态 1.客户端A的TCP进程先向服务端发出连接释放报文段,并停止发送数据,主动关闭TCP连接。释放连接报文段中FIN=1,序号为seq=u,该序号等于前面已经传送过去的数据的最后一个字节的序号+1。这时,A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。TCP规定:FIN报文段即使不携带数据,也要消耗掉一个序号,这是TCP连接释放的第一次挥手。 2.B收到连接释放报文段后即发出确认释放连接的报文段,该报文段中,ACK=1,确认号为ack=u+1,其自己的序号为v,该序号等于B前面已经传送过的数据的最后一个字节的序号+1。然后B进入CLOSE-WAIT(关闭等待)状态,此时TCP服务器进程应该通知上层的应用进程,因而A到B这个方向的连接就释放了,这时TCP处于半关闭状态,即A已经没有数据要发了。但B若发送数据,A仍要接收,也就是说B到A这个方向的连接并没有关闭,这个状态可能会持续一些时间。这是TCP连接释放的第二次挥手。 3.A收到B的确认后,就进入了FIN-WAIT-2(终止等待2),等待B发出连接释放报文段,如果B已经没有要向A发送的数据了,其应用进程就通知TCP释放连接。这是B发出的连接释放报文段中,FIN=1,确认号还必须重复上次已发送过的确认号,即ack=u+1,序号seq=w,因为在半关闭状态可能又发送了一些数据,因此该序号为半关闭状态发送的数据的最后一个字节的序号+1.这时B进入LAST-ACK(最后确认),等待A的确认,这是TCP连接的第三次挥手。 4.A收到B的连接释放请求后,必须对此发出确认。确认报文段中,ACK=1,确认号ack=w+1,而自己的序号seq=u+1,而后进入TIME-WAIT(时间等待)状态。这时候,TCP连接还没有释放掉,必须经过时间等待计时器设置的时间2MSL(Maximum Segment Lifetime最大报文寿命)后,A才进入CLOSED状态。RFC(Requests for Comments 请求注解文档)建议设为2分钟,因此从A进入TIME-WAIT状态后,要经过4分钟才能进入到CLOSED状态,而B只要收到了A的确认后,就进入了CLOSED状态。二者都进入了CLOSED状态后,连接就完全释放了,这是TCP连接的第四次挥手。 1.双方主动关闭TCP连接释放流程 与可以双方同时建立TCP传输连接一样,TCP传输连接关闭也可以由双方同时主动进行(正常情况下都是由一方发送第一个FIN数据段进行主动连接关闭,另一方被动接受连接关闭) 当两端对于的网络应用层进程同时调用CLOSE原语,发送FIN数据段执行关闭命令时,两端均从ESTABLISHED状态转变为FIN WAIT 1状态。任意一方收到对端发来的FIN数据段后,其状态均由FIN-WAIT-1转变为CLOSING状态,并发送最后的ACK数据段。当收到最后的ACK数据段后,状态转变为TIME_WAIT,在等待2MSL后进入到CLOSED状态,最终是否整个TCP传输连接。 2.为什么A在TIME-WAIT状态必须等待2MSL时间呢? 1.为了保证A发送的最后一个ACK报文段能够到达B。该ACK报文段很有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,B可能会重传这个FIN+ACK报文段,而A就在这2MSL时间内收到这个重传的FIN-ACK报文段,接着A重传一次确认,重新启动2MSL计时器,最后A和B都进入CLOSED状态。如果A在TIME-WAIT状态不等待一段时间就直接释放连接,到CLOSED状态,那么就无法收到B重传的FIN+ACK报文段,也就不会再发送一次确认ACK报文段,B就无法正常进入CLOSED状态。 2.防止已失效的请求连接出现在本连接中。在连接处于2MSL等待时,任何迟到的报文段将被丢弃,因为处于2MSL等待的、由该socket(IP和端口对)定义的连接在这个时间内将不能被再用,这样就可以使下一个新的连接中不会出现这种旧的连接之前延迟的报文段。 # 关于WCF客户端对象的释放:Close( ) & Abort( ) 其实六七年前WCF盛行的时候也讨论过这个问题。 Close( ) 使 ClientBase<TChannel> 对象从其当前状态转换到关闭状态。 Abort( ) 使 ClientBase<TChannel> 对象立即从其当前状态转换到关闭状态。 文档描述仅仅能看出“立即”两个字的差别,但究竟该怎么用就有点迷茫了。 通过反编译可以发现,两个方法源自接口 ICommunicationObject,该接口“为系统中所有面向通信的对象(包括通道、通道管理器、工厂、侦听器以及调度程序和服务主机)定义基本状态机的协定。” 观察源码可知 Abort( ) 方法不会抛出异常, 而 Close( )方法则可能抛出 TimeoutException 和 CommunicationException。 另外,ClientBase<TChannel> 实现了接口 IDisposable接口,这意味着我们可以在代码中使用 using 语句块,这样在出作用域时会自动调用 Dispose( ) 方法来释放资源。不过Dispose( )方法内部也仅仅是调用了 Close( )方法,因此也是存在抛异常的可能。 # 测试结果 ```c# public class Program { static void Main(string[] args) { Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < 1000; i++) { EndpointAddress endpointAddress = new EndpointAddress( new Uri("net.tcp://127.0.0.1:8100/JHDBService/JHDBService")); NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true); JHDBServiceClient client; using (client = new JHDBServiceClient(binding, endpointAddress)) { client.ChannelFactory.Opened+=ChannelFactory_Opened; client.ChannelFactory.Closing +=ChannelFactory_Closing; client.ChannelFactory.Closed+=ChannelFactory_Closed; IContextChannel contextChannel = client.InnerChannel; contextChannel.OperationTimeout = TimeSpan.FromMinutes(5); DataSet ds = client.GetDataSet("select * from jhmr_app_catalog_dict"); //client.Abort(); try { client.Close(); } catch (Exception ex) { System.Console.WriteLine(ex.Message); } } var state = client.State; } sw.Stop(); System.Console.WriteLine(sw.ElapsedMilliseconds); System.Console.ReadKey(); } private static void ChannelFactory_Closed(object sender, EventArgs e) { System.Console.WriteLine("Closed"); } private static void ChannelFactory_Closing(object sender, EventArgs e) { System.Console.WriteLine("Closing"); } private static void ChannelFactory_Opened(object sender, EventArgs e) { System.Console.WriteLine("Opened"); } } ``` | | Close | Abort | | :---: | :----: | :----: | | 1000 | 18638 | 17587 | | 5000 | 76717 | 70572 | | 10000 | 153799 | 136644 | 门诊程序测试 | | Close | Abort | | :------: | :---: | :---: | | 首次加载 | 3639 | 3257 | | 二次加载 | 3153 | 2871 | | 三次加载 | 3149 | 2863 | # [在客户端正确的关闭WCF连接! zt](https://www.cnblogs.com/zeroone/archive/2013/06/13/3133425.html) **在客户端正确的关闭WCF连接!** 如果你直接将客户端调用Close关闭,或者使用using语句,那你将是个悲剧,这点相信使用WCF的同志都知道的,因为ClientBase类的Close()方法被调用后,实际上是关闭了一个网络会话,并且会抛出异常!`CommunicationException和``TimeoutException!` 这似乎违反常理,但确实发生了。因为一般来说Close函数都不会抛出异常。这个问题的解决办法是使用Try-Catch语句包含Close()方法,然后再异常处理中使用Abort函数释放资源! 同样,ClientBase类的IDisposable接口的实现函数Dispose方法中会调用Close函数,使用Reflector查看ClientBase<TChannel> 的代码便知  所以你使用using语句同样不能正确关闭WCF连接!而我们使用using语句就是为了不管发生什么情况,只要离开作用域之后,就会释放相关资源。而WCF的ClientBase类设计很明显违反了这一观念!简直是坑爹啊! 解决这一问题网上以后很多方案,首先说说微软的,也许是微软也意识到这一点,所以微软在提供的WCF示例程序中特意包含了一个叫做UsingUsing的示例,名字听起来有点怪  主要解决的方法是如下的代码 [](javascript:void(0);)  ```c# 1 // This method shows the correct way to clean up a client, including catching the 2 // approprate Exceptions. 3 static void DemonstrateCleanupWithExceptions() 4 { 5 // Create a client 6 CalculatorClient client = new CalculatorClient(); 7 try 8 { 9 // Demonstrate a successful client call. 10 Console.WriteLine("Calling client.Add(0.0, 0.0);"); 11 double addValue = client.Add(0.0, 0.0); 12 Console.WriteLine(" client.Add(0.0, 0.0); returned {0}", addValue); 13 14 // Demonstrate a failed client call. 15 Console.WriteLine("Calling client.Divide(0.0, 0.0);"); 16 double divideValue = client.Divide(0.0, 0.0); 17 Console.WriteLine(" client.Divide(0.0, 0.0); returned {0}", divideValue); 18 19 // Do a clean shutdown if everything works. In this sample we do not end up 20 // here, but correct code should Close the client if everything was successful. 21 Console.WriteLine("Closing the client"); 22 client.Close(); 23 } 24 catch (CommunicationException e) 25 { 26 // Because the server suffered an internal server error, it rudely terminated 27 // our connection, so we get a CommunicationException. 28 Console.WriteLine("Got {0} from Divide.", e.GetType()); 29 client.Abort(); 30 } 31 catch (TimeoutException e) 32 { 33 // In this sample we do not end up here, but correct code should catch 34 // TimeoutException when calling a client. 35 Console.WriteLine("Got {0} from Divide.", e.GetType()); 36 client.Abort(); 37 } 38 catch (Exception e) 39 { 40 // In this sample we do not end up here. It is best practice to clean up the 41 // client if some unexpected Exception occurs. 42 Console.WriteLine("Got unexpected {0} from Divide, rethrowing.", e.GetType()); 43 client.Abort(); 44 throw; 45 } 46 } ```  [](javascript:void(0);) 其实就是使用try-catch语句捕获关闭时的异常!试想要是每次都这样调用WCF服务将会是多么痛苦的一件事情!网上也有人使用Linq的Expression来解决这一问题 于是我借用函数式编程的思想,设计了一个使用lambda表达式来解决这一问题的方案! 关键的函数很简单,一看便知道是我是怎样设计的! [](javascript:void(0);)  ``` 1 public static class SvcClient 2 { 3 public static void Invoke<TClient>(Action<TClient> act) 4 where TClient : System.ServiceModel.ICommunicationObject, new() 5 { 6 TClient client = new TClient(); 7 try 8 { 9 act(client); 10 client.Close(); 11 } 12 catch (System.ServiceModel.CommunicationException) 13 { 14 client.Abort(); 15 } 16 catch (TimeoutException) 17 { 18 client.Abort(); 19 } 20 catch (Exception) 21 { 22 client.Abort(); 23 throw; 24 } 25 } 26 } ```  [](javascript:void(0);) 实际上这一设计的思想就是:将所有调用WCF服务的所有代码做为一个函数传入进来,然后我再内部使用try-catch语句包裹整个调用过程,这样就巧妙的将处理关闭连接异常的代码与实际调用过程分离开来! 函数的使用过程也比较方便 ```c# 1 SvcClient.Invoke<Service1Client>(client => 2 { 3 //在此处添加调用WCF的代码 4 }); ``` 代码看起来比较简洁优美,个人比较满意,而且不用再担心调用结束后不能正确关闭WCF连接,资源不能正确释放的问题了!
孙端己
2024年2月2日 17:11
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码