请教sp1234。TcpClient异步通讯的问题。

ahking 2012-07-03 11:20:42
看了你关于这篇文章的回复,了解了很多socket通讯原理的内容,
http://topic.csdn.net/u/20090916/21/28bf59f4-e8db-449f-9904-60ba73ef3aa5.html
现在就是有一个问题,如qldsrx所说的,异步方法在没有收到任何信号的情况下是不会回调结束的,在某些情况下,网络突然中断,该tcp链接该如何释放
...全文
364 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
qldsrx 2012-07-06
  • 打赏
  • 举报
回复
显然必须申明在方法内部啊,如果要重利用(不再new),那么就要作为BeginRead的最后一个参数传递进去。
定义一个自定义的类:
public class State
{
public NetworkStream ns;
public byte[] buffer;
}

然后这段代码
readBuffer = new byte[1024];
networkStream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(readCallBack), networkStream);

改为:
State state = new State();
state.ns = networkStream;
state.buffer = new byte[1024];
networkStream.BeginRead(state.buffer, 0, readBuffer.Length, new AsyncCallback(readCallBack), state);
ahking 2012-07-06
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 的回复:]

你的buffer倒底是什么上面的?networkStream内部的根本看不到也不需要看到,外部定义个buffer变量肯定是一个连接一个使用,而2个连接肯定存在2个networkStream对象而不是一个,每个networkStream只能对应一个连接,而事件对于一个networkStream来说同一个时间只能触发执行一次,根本没有交叉的可能性。不过为了不搞错,还是用Socket更好,那个更直观,……
[/Quote]

//这里声明buffer
private byte[] readBuffer;

private void OnAccept(IAsyncResult ar)
{
TcpListener clientListener = (TcpListener)ar.AsyncState;
//接受客户的连接,得到连接的Socket
TcpClient client = clientListener.EndAcceptTcpClient(ar);

NetworkStream networkStream = client.GetStream();
//异步读取数据
if (networkStream.CanRead)
{

//这里初始化buffer字段,我前面说过了是字段,肯定是在方法外的
readBuffer = new byte[1024];
networkStream.BeginRead(readBuffer, 0, readBuffer.Length, new AsyncCallback(readCallBack), networkStream);//开始异步读取数据

}

//继续监听
clientListener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);


}
ahking 2012-07-06
  • 打赏
  • 举报
回复
没做过socket开发,其机制不太了解,十分感谢qldsrx的耐心回复
lhonyun_lhy 2012-07-06
  • 打赏
  • 举报
回复
使用心跳数据维持连接,如果心跳数据没了就是连接断了,然后主动断开。
qldsrx 2012-07-06
  • 打赏
  • 举报
回复
你的buffer倒底是什么上面的?networkStream内部的根本看不到也不需要看到,外部定义个buffer变量肯定是一个连接一个使用,而2个连接肯定存在2个networkStream对象而不是一个,每个networkStream只能对应一个连接,而事件对于一个networkStream来说同一个时间只能触发执行一次,根本没有交叉的可能性。不过为了不搞错,还是用Socket更好,那个更直观,networkStream是Socket的封装,非高级框架(例如WCF)不要使用,你这里是底层操作,完全应该直接用Socket。
qldsrx 2012-07-06
  • 打赏
  • 举报
回复
你9楼的代码里,readCallBack中申明了一个临时变量byte[] dd = new byte[numberOfBytesRead];
这种情况根本不会被其它线程访问到这个dd变量,那么我当然认为是不会有任何搞错的可能性了,你怕那个Socket自己混淆,我告诉你不可能,不是很清楚吗?只要你代码里面不混淆,.NET底层提供的类不会有BUG出现的。
ahking 2012-07-06
  • 打赏
  • 举报
回复
又上网查了下,
networkStream.BeginRead应该是在上一个readCallBack数据接收完毕或者缓冲区满了时才开始执行,因此应该不存在多个线程同时操作该buffer的问题。
ahking 2012-07-06
  • 打赏
  • 举报
回复
同一个变量被不同的异步线程操作,内容是要变动。
可是你的这段话让我迷惑了:
“不需要你来保证,TCP自动处理的,你的连接端口是固定的,别的连接进来肯定不会用已经用过的端口,否则会报端口被占用的错误,因此通过端口的不同,系统自动识别数据该属于那个连接,肯定不会搞错,即使UDP也不可能搞错。”
让我总以为有内部的机制来维护相关的数据
qldsrx 2012-07-06
  • 打赏
  • 举报
回复
你问的都是常识性问题,自己多看看书,多看看MSDN,难怪sp1234都不高兴来回答你了。
同一个变量被多次操作,肯定数据会变掉,除非你第二次写入时确定不需要第一次写入的内容,所以一般要保证一个线程一个buffer。如果要自己建立缓冲池,申请一个大的buffer重复利用,那么需要有一个管理类负责分配缓存片段,返回ArraySegment结构,但这对你来说还太早,最基本的都没掌握。
ahking 2012-07-06
  • 打赏
  • 举报
回复
等待大侠确认下!!!!!!
ahking 2012-07-06
  • 打赏
  • 举报
回复
难道readBuffer在不同的线程中都有自己的副本?这样就不会发生数据错乱的问题了?
ahking 2012-07-06
  • 打赏
  • 举报
回复
信息很短,最长也不会超过50字节,因此一次可以接收完毕
qldsrx 2012-07-06
  • 打赏
  • 举报
回复
你的代码里,问题很多,readCallBack里面必须再次调用BeginRead才行,因为不是一次就能接收完毕所有数据,谁能保证一次读完?
如果用我给你演示的那个State类,那么只要在readCallBack里面第一行
NetworkStream myNetworkStream = (NetworkStream)ar.AsyncState;
改为
State state = (State)ar.AsyncState; 即可
后面的想必你应该会操作。
ahking 2012-07-06
  • 打赏
  • 举报
回复
我直接这样写,全部代码:

 
class SocketServer
{
/// <summary>
/// 监听端口号
/// </summary>
public int Port { get; set; }

/// <summary>
/// 监听IP地址
/// </summary>
public string IP { get; set; }

private TcpListener tcpListener;

//buffer大小为1k
private const int BufferSize = 1024;

private byte[] ReadBuffer;

/// <summary>
/// 开始监听
/// </summary>
protected void Open()
{
tcpListener = new TcpListener(IPAddress.Parse(IP), Port);
try
{
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);

}
catch (SocketException ex)
{
//logger.WarnFormat("服务器监听发生异常:{0}\nSocket ErrorCode: {1}\n提示:请检查端口是否已被占用", ex.Message, ex.ErrorCode);
throw ex;
}
catch (Exception ex)
{
//logger.Warn(ex);
throw ex;
}
}
/// <summary>
/// 获取监听到的客户端
/// </summary>
/// <param name="ar"> </param>
private void OnAccept(IAsyncResult ar)
{
TcpListener clientListener = (TcpListener)ar.AsyncState;
//接受客户的连接,得到连接的Socket
TcpClient client = clientListener.EndAcceptTcpClient(ar);

NetworkStream networkStream = client.GetStream();
//异步读取数据
if (networkStream.CanRead)
{
ReadBuffer = new byte[BufferSize];
networkStream.BeginRead(ReadBuffer, 0, ReadBuffer.Length, new AsyncCallback(readCallBack), networkStream);//开始异步读取数据

}

//继续监听
clientListener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);


}

/// <summary>
/// TCP读数据的回调函数
/// </summary>
/// <param name="ar"> </param>
private void readCallBack(IAsyncResult ar)
{
NetworkStream myNetworkStream = (NetworkStream)ar.AsyncState;

int numberOfBytesRead;
numberOfBytesRead = myNetworkStream.EndRead(ar);

if (numberOfBytesRead > 0)
{
byte[] dd = new byte[numberOfBytesRead];
Array.Copy(ReadBuffer, 0, dd, 0, numberOfBytesRead);
//信息写入数据库
//DAL.Insert()....
}
else
{
//被动断开时
myNetworkStream.Close();
myNetworkStream.Dispose();

}
}

}
ahking 2012-07-06
  • 打赏
  • 举报
回复

//这样不行?
ahking 2012-07-06
  • 打赏
  • 举报
回复
我直接这样写,全部代码:

[code=c#]
class SocketServer
{
/// <summary>
/// 监听端口号
/// </summary>
public int Port { get; set; }

/// <summary>
/// 监听IP地址
/// </summary>
public string IP { get; set; }

private TcpListener tcpListener;

//buffer大小为1k
private const int BufferSize = 1024;

private byte[] ReadBuffer;

/// <summary>
/// 开始监听
/// </summary>
protected void Open()
{
tcpListener = new TcpListener(IPAddress.Parse(IP), Port);
try
{
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);

}
catch (SocketException ex)
{
//logger.WarnFormat("服务器监听发生异常:{0}\nSocket ErrorCode: {1}\n提示:请检查端口是否已被占用", ex.Message, ex.ErrorCode);
throw ex;
}
catch (Exception ex)
{
//logger.Warn(ex);
throw ex;
}
}
/// <summary>
/// 获取监听到的客户端
/// </summary>
/// <param name="ar"></param>
private void OnAccept(IAsyncResult ar)
{
TcpListener clientListener = (TcpListener)ar.AsyncState;
//接受客户的连接,得到连接的Socket
TcpClient client = clientListener.EndAcceptTcpClient(ar);

NetworkStream networkStream = client.GetStream();
//异步读取数据
if (networkStream.CanRead)
{
ReadBuffer = new byte[BufferSize];
networkStream.BeginRead(ReadBuffer, 0, ReadBuffer.Length, new AsyncCallback(readCallBack), networkStream);//开始异步读取数据

}

//继续监听
clientListener.BeginAcceptTcpClient(new AsyncCallback(OnAccept), null);


}

/// <summary>
/// TCP读数据的回调函数
/// </summary>
/// <param name="ar"></param>
private void readCallBack(IAsyncResult ar)
{
NetworkStream myNetworkStream = (NetworkStream)ar.AsyncState;

int numberOfBytesRead;
numberOfBytesRead = myNetworkStream.EndRead(ar);

if (numberOfBytesRead > 0)
{
byte[] dd = new byte[numberOfBytesRead];
Array.Copy(ReadBuffer, 0, dd, 0, numberOfBytesRead);
//信息写入数据库
//DAL.Insert()....
}
else
{
//被动断开时
myNetworkStream.Close();
myNetworkStream.Dispose();

}
}

}
[/code]
qldsrx 2012-07-05
  • 打赏
  • 举报
回复
不需要你来保证,TCP自动处理的,你的连接端口是固定的,别的连接进来肯定不会用已经用过的端口,否则会报端口被占用的错误,因此通过端口的不同,系统自动识别数据该属于那个连接,肯定不会搞错,即使UDP也不可能搞错。
ahking 2012-07-05
  • 打赏
  • 举报
回复
还有个问题比较困惑,在onaccept中的networkStream.BeginRead前声明了
byte[] readBuffer = new byte[1024];
然后在readCallBack中得到信息流,这时可能有别的连接进来,然后又开始向readBuffer写信息,我怎么保证得到的信息流是我想要的呢?
private void readCallBack(IAsyncResult ar)
{
NetworkStream myNetworkStream = (NetworkStream)ar.AsyncState;

int numberOfBytesRead;
numberOfBytesRead = myNetworkStream.EndRead(ar);

if (numberOfBytesRead > 0)
{
byte[] dd = new byte[numberOfBytesRead];
}
else
{
//连接断开时
myNetworkStream.Close();
myNetworkStream.Dispose();

}
}

ahking 2012-07-05
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

不需要你来保证,TCP自动处理的,你的连接端口是固定的,别的连接进来肯定不会用已经用过的端口,否则会报端口被占用的错误,因此通过端口的不同,系统自动识别数据该属于那个连接,肯定不会搞错,即使UDP也不可能搞错。
[/Quote]

那可以把该byte[] readBuffer放到字段中:如
private byte[] readBuffer;

在OnAccept中
readBuffer = new byte[1024];
OnAccept和readCallBack都对这个readBuffer操作,是否可以?

另外一个问题:与服务器通讯的很多终端都是用一个端口号,有可能同时对服务器发送信息请求连接,传输的信息内容也不相同。
由于是异步方式,不等开始读取消息就异步开始另一个监听,并仍然使用当前的异步回调程序来处理,这时候我就很担心reabBuffer中到底获取的是哪个连接的信息?
qldsrx 2012-07-03
  • 打赏
  • 举报
回复
networkstream关闭的话,对应的Socket也就一起关闭了,两者本来就是一个东西,只是访问的形式不同而已,你应该多看看MSDN
加载更多回复(5)

110,535

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧