一种C# TCP异步编程中遇到的问题
最近在维护公司的一个socket服务端工具,该工具主要是提供两个socket server服务,对两端连接的程序进行数据的透明转发。
程序运行期间,遇到一个问题,程序的一端是GPRS设备,众所周知,GPRS设备的网络连接十分的不问题,由此会产生不少的“奇怪”问题。
实际过程中,程序运行几个小时后,无线端的socket server断开就再也无法打开。找了很久都没发现。
通过wireshark抓取通信报文,一般是在TCP的三次握手时出的问题。
常规的TCP三次握手,由TCP的标识可简单看作:SYN-SYN ACK-ACK,实际遇到问题时,标识为:SYN-RST ACK。
可以明显看出,服务端发出了重置的标识,用来积极的拒绝了客户端的连接。
程序的server部分代码,采用的常规的TCP异步编程方式,一下是MSDN代码
// This server waits for a connection and then uses asynchronous operations to
// accept the connection with initial data sent from the client.
// Establish the local endpoint for the socket.
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Bind the socket to the local endpoint, and listen for incoming connections.
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections and receive data from the client.
Console.WriteLine("Waiting for a connection...");
// Accept the connection and receive the first 10 bytes of data.
// BeginAccept() creates the accepted socket.
int receivedDataSize = 10;
listener.BeginAccept(null, receivedDataSize, new AsyncCallback(AcceptReceiveDataCallback), listener);
// Wait until a connection is made and processed before continuing.
allDone.WaitOne();
}
}
public static void AcceptReceiveDataCallback(IAsyncResult ar)
{
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
// End the operation and display the received data on the console.
byte[] Buffer;
int bytesTransferred;
Socket handler = listener.EndAccept(out Buffer, out bytesTransferred, ar);
//再次投递接收,实现一直接收socket的操作
listener.BeginAccept(null, receivedDataSize, new AsyncCallback(AcceptReceiveDataCallback), listener);
}
经过问题的定位,可以判断可能是程序的异步接收回调中出了问题,但实际添加调试信息后发现,在程序出现端口无法打开后,再进行回调函数操作,并无信息打出。
TCP异步编程,一般是成对的出现beginXXX...endXXX,再通过回调函数进行具体处理。
如下为accept的回调函数,代码中使用了try..catch来捕获异常,实际问题可能就出在这里,代码如下:
public static void AcceptReceiveDataCallback(IAsyncResult ar)
{
// Get the socket that handles the client request.
Socket listener = (Socket) ar.AsyncState;
// End the operation and display the received data on the console.
byte[] Buffer;
int bytesTransferred;
try{
Socket handler = listener.EndAccept(out Buffer, out bytesTransferred, ar);
}
catch(异常1 e){
...
return;
}
catch(异常2 e){
...
return;
}
//再次投递接收,实现一直接收socket的操作
listener.BeginAccept(null, receivedDataSize, new AsyncCallback(AcceptReceiveDataCallback), listener);
}
程序在实际出现端口不能打开之前曾经进入过“异常1”/“异常2”,判断很可能是程序进行了return,而无法再次投递接收操作。
此时所有的端口打开操作,都会进入socket.listen(backlog)的队列中,当accpet队列中的内容无法通过完整begin..end操作取出,队列满后socket的底层协议栈则会拒绝新的socket连入。
此处是个不明显的坑,花了好几天才发现,实际修改情况待检验。。。
一种C# TCP异步编程中遇到的问题的更多相关文章
- WPF工作笔记:本地化支持、主进程通知、两种最常用异步编程方式
1.本地化支持 (1)重写控件默认的依赖属性LanguageProperty FrameworkElement.LanguageProperty.OverrideMetadata( typeof(Fr ...
- 【转】C# Async/Await 异步编程中的最佳做法
Async/Await 异步编程中的最佳做法 Stephen Cleary 近日来,涌现了许多关于 Microsoft .NET Framework 4.5 中新增了对 async 和 await 支 ...
- C#异步编程中的最佳实践(做法)
原文地址Stephen Cleary 写得很详细,尤其讲到了 GUI 上下文调用,在APS.NET中它会阻塞 GUI 线程,从而导致死锁.而控制台中却不存在这个问题. 比如开发过程中本地写控制台程序测 ...
- 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
[Linux网络编程]TCP网络编程中connect().listen()和accept()三者之间的关系 基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: conn ...
- 探究SynchronizationContext在.Net异步编程中的地位
原文:探究SynchronizationContext在.Net异步编程中的地位 引言: 多线程编程/异步编程非常复杂,有很多概念和工具需要去学习,贴心的.NET提供Task线程包装类和await/a ...
- 你不知道的this—JS异步编程中的this
Javascript小学生都知道了javascript中的函数调用时会 隐性的接收两个附加的参数:this和arguments.参数this在javascript编程中占据中非常重要的地位,它的值取决 ...
- 【Linux 网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数:对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...
- 异步编程中使用帮助类来实现Thread.Start()的示例
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 异步编程中的最佳做法(async await)
阅读1:http://blog.csdn.net/nacl025/article/details/9163495 阅读2:http://www.cnblogs.com/x-xk/archive/201 ...
随机推荐
- php实现MVC
在PHP中使用MVC越来越流行了,特别是在一些开源的框架当中.MVC足以应对大多数的情况,但还有一些情况是其不太适合的,如比较简单的个人博客,对于只有几百篇文章量级的博客,使用MVC让人觉得有些太复杂 ...
- linux内存查看
一般用free命令,显示整体内存使用状况: 第二行从OS角度,used包括内核+应用+buffers+cached使用的内存,buffers/cached是磁盘缓存的大小 第三行从应用角度,可用内存= ...
- oracle checkpoint 详解
Oracle checkpoint详解 topcheckpoint扫盲 top什么是checkpoint 在数据库系统中,写日志和写数据文件是数据库中IO消耗最大的两种操作,在这两种操作中写数据文件属 ...
- [改善Java代码]避开基本类型数组转换列表陷阱
开发中经常用到Arrays和Collections这两个工具类. 在数组和列表之间进行切换.非常方便.但是也会遇到一些问题. 看代码: import java.util.Arrays; import ...
- CSS3动画与过渡
transform:在使用2D或3D转换前需用transform-style申明转换的类型,preserve-3d或者preserve-2d 属性 translate(): 通过 translate( ...
- Commons Codec - 常见的编码解码
Base64 Base64 编码 assertEquals("T3chIQ==", Base64.encodeBase64String("Ow!!".getBy ...
- Linux 命令 - ps: 显示当前进程的快照
命令格式 ps [options] 实例 a) 查看所有的进程. huey@huey-K42JE:~$ ps aux | head USER PID %CPU %MEM VSZ RSS TTY STA ...
- AngularJS Boostrap Pagination Sample
首先,样式是这样的 首先,Service端是Webapi REST JSON格式 第二,我们建立一个Wrapper Class,这里你也可以定义一个Generic<T>,作为示例,我们这里 ...
- python学习day2--python基础
python没有常量的定义方式,一般常量可人为用全部大写字母来表示. 如: MYSQL_CONNECTION='192.168.10.2' 以os库为例: import os os.system('i ...
- 删除mssqlserver表数据,使id从0开始
********************************* 注意备份好数据! *************************** 1.删除表数据 delete 表名 2.执行 dbcc c ...