上篇.net平台下C#socket通信(上)介绍了socket通信的基本原理及最基本的通信方式。本文在此基础上就socket通信时经常遇到的问题做一个简单总结,都是项目中的一些小问题,拿来此处便于下次使用,同时对在使用socket时出现些许问题的同仁们多一个粗浅建议。不足之处请提出,谢谢。

本文主要讲述:

1、正常通信中握手建立

2、一对多的通信

3、发送接收数据格式转换

4、资源释放

5、开启并保持服务监听

1、握手建立正常的通信通道

  项目需要通信的双方(假设是一个上位机、一个下位机)之间需要建立一个稳定的通道,以便进行通信。本项目中具体操作是:上位机作为服务器,下位机作为客户端,同时制定通信协议。上位机首先打开监听等待建立通道,下位机主动连接上位机后发送连接成功的信息到上位机,上位机根据通信协议发送数据到下位机,此时通道已经建立。但为了保险起见(同时遵循三次握手),客户端再次发送数据到上位机告知通道建立完毕。

2、一对多通信

  项目需求是一个上位机多个下位机,这就确定了上位机做为服务器端,下位机作为客户端主动连接服务器。一对一通信时只有一个socket通道,因此无论是上位机还是下位机在发送和接收数据的时候都不会存在数据乱发乱收的情况。一对多意味着上位机和下位机会建立起多个通道,因此在发送数据时需要记录哪一个下位机处于哪个socket通道中,以便进行逻辑处理。本文处理一对多通信的过程是:

1)首先建立一个对话类Session:

public class Session
{
public Socket ClientSocket { get; set; }//客户端的socket
public string IP;//客户端的ip public Session(Socket clientSocket)
{
this.ClientSocket = clientSocket;
this.IP = GetIPString();
} public string GetIPString()
{
string result = ((IPEndPoint)ClientSocket.RemoteEndPoint).Address.ToString();
return result;
}
}

2)在服务端socket监听时:

IPEndPoint loaclEndPoint = new IPEndPoint(IPAddress.Any, Port);
SocketLister = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketLister.Bind(loaclEndPoint); try
{
SocketLister.Listen(MaxConnection);
while (IsRunning)
{
ClientSocket = SocketLister.Accept();
//保存socket
Session newSession = new Session(ClientSocket);
lock (sessionLock)
{
sessionTable.Add(newSession.IP, newSession);
} SocketConnection socketConnection = new SocketConnection(ClientSocket);
socketConnection.ReceiveDatagram();//接收数据
}
}
catch (SocketException ex)
{
} //声明 public Hashtable sessionTable = new Hashtable ();//包括客户端会话

private object sessionLock = new object();

 

为了便于理解,把整个服务端socket的建立都写在上面。

3)发送数据到不同的客户端

Hashtable ht = serverSocket.sessionTable;
foreach (Session session in ht.Values)
{
if (session.IP == "127.0.0.1")//example
{
SocketConnection socketConnection = new SocketConnection(session.ClientSocket);
string str = "C300010002D2";
byte[] sendBytes = StrToHexByte(str);
socketConnection.Send(sendBytes);
}
}

SocketConnection类已经被使用多次,写在下面:

public class SocketConnection:IDisposable
{
public ServerSocket Server { get; set; }
public Byte[] MsgBuffer = null;
private int totalLength = 0;
public int CurrentBufferLength; private Socket _ClientSocket = null;
public Socket ClientSock
{
get{ return this._ClientSocket; }
} public SocketConnectionType Type { get; private set; } #region Constructor
public SocketConnection(ServerSocket server, Socket sock)
{
this.Server = server;
this._ClientSocket = sock;
this.Type = SocketConnectionType.Server;
} public SocketConnection(Socket sock)
{
this._ClientSocket = sock;
this.Type = SocketConnectionType.Client;
}
#endregion #region Events
public SocketConnectionDelegate OnConnect = null;//是否连接
public SocketConnectionDelegate OnLostConnect = null;//中断连接
public ReceiveDataDelegate OnReceiveData = null;//接收数据 #endregion #region Connect
public void Connect(IPAddress ip, int port)
{
this.ClientSock.BeginConnect(ip, port, ConnectCallback, this.ClientSock);
} private void ConnectCallback(IAsyncResult ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
handler.EndConnect(ar);
if (OnConnect != null)
{
OnConnect(this);
}
ReceiveDatagram();
}
catch (SocketException ex)
{
}
}
#endregion #region Send
public void Send(string data)
{
Send(System.Text.Encoding.UTF8.GetBytes(data));
} public void Send(byte[] byteData)
{
try
{
int length = byteData.Length;
byte[] head = BitConverter.GetBytes(length);
byte[] data = new byte[head.Length + byteData.Length];
Array.Copy(head, data, head.Length);
Array.Copy(byteData, 0, data, head.Length, byteData.Length);
this.ClientSock.BeginSend(data, 0, data.Length, 0, new AsyncCallback(SendCallback), this.ClientSock);
}
catch (SocketException ex)
{
}
} private void SendCallback(IAsyncResult ar)
{
try
{
Socket handler = (Socket)ar.AsyncState;
handler.EndSend(ar);
}
catch (SocketException ex)
{
}
} #endregion #region ReceiveDatagram
public void ReceiveDatagram()
{
SocketStateObject state = new SocketStateObject();
state.workSocket = _ClientSocket;
_ClientSocket.BeginReceive(state.buffer, 0, SocketStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
} private void ReceiveCallback(IAsyncResult ar)
{
SocketStateObject state = (SocketStateObject)ar.AsyncState;
Socket handler = state.workSocket;
if (handler.Connected)
{
try
{
state.bytesRead = handler.EndReceive(ar);
if (state.bytesRead > 0)
{
OnDataRecivedCallback(state.buffer, state.bytesRead);
Array.Clear(state.buffer, 0, state.buffer.Length);
state.bytesRead = 0;
handler.BeginReceive(state.buffer, 0, SocketStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
else
{
//if (OnDisconnect != null)
//{
// OnDisconnect(this);
//}
Dispose();
}
}
catch (SocketException ex)
{ }
}
} private void ReceiveCallBack00(IAsyncResult ar)
{
try
{
int REnd = _ClientSocket.EndReceive(ar);
if (REnd > 0)
{ OnDataRecivedCallback(MsgBuffer, REnd );
Array.Clear(MsgBuffer, 0, MsgBuffer.Length);
REnd = 0; _ClientSocket.BeginReceive(MsgBuffer, 0, MsgBuffer.Length, 0, new AsyncCallback(ReceiveCallBack00), null);
}
else
{
if (OnLostConnect != null)
{
OnLostConnect(this);
} Dispose();
}
}
catch (Exception ex)
{ } } private void OnDataRecivedCallback(byte[] data, int length)
{
if (length > 0)
{
if (this.MsgBuffer == null)
{
byte[] bytelength = new byte[4];
Array.Copy(data, bytelength, 4);
this.totalLength = BitConverter.ToInt32(bytelength, 0);
this.MsgBuffer = new byte[this.totalLength];
this.CurrentBufferLength = 0;
}
if (this.totalLength > 0)
{
int offset = 0;
if (CurrentBufferLength == 0)
{
offset = 4;
}
if (length + this.CurrentBufferLength >= this.totalLength + offset)
{
int len = this.totalLength - CurrentBufferLength;
Array.Copy(data, offset, this.MsgBuffer, this.CurrentBufferLength, len);
byte[] tmp = this.MsgBuffer.Clone() as byte[];
OnReceiveData(new MessageData(this, tmp));
this.MsgBuffer = null;
if (length - len - offset > 0)
{
tmp = new byte[length - len - offset];
Array.Copy(data, offset + len, tmp, 0, tmp.Length);
OnDataRecivedCallback(tmp, tmp.Length);
}
}
else
{
Array.Copy(data, offset, this.MsgBuffer, this.CurrentBufferLength, length - offset);
this.CurrentBufferLength += length - offset;
}
}
else
{
}
}
} public void Dispose()
{
try
{
this.ClientSock.Shutdown(SocketShutdown.Both);
this.ClientSock.Close();
this.Server = null; //if (OnLostConnect != null)
//{
// OnLostConnect(this);
//}
}
catch
{
}
}
#endregion
}

3、处理需要发送和接收到的数据

  项目需要是上位机获取数据进行逻辑处理,然后通过tcp/ip协议发送给下位机,下位机在接收到数据的同时发送确认信息到上位机。项目过程中,上位机在开发过程中需要调试其发送数据、接收数据是否成功,此处借助于USR-  TCP232小工具。但是涉及到一个问题,下位机发送和接收都是byte字节数组,那么开发的上位机应该如何发送和接收数据?在.net平台下C#socket通信(上),有服务器端的发送和接收函数,发送数据时将要发送的字符串转换为byte数组,接收时再将字节数组转换为16进制字符串。如下:

//字节数组转换为16进制字符串
public string ByteToHexStr(byte[] bytes)
{
string str = "";
if (bytes != null)
{
for (int i = 0; i < bytes.Length; i++)
{
str += bytes[i].ToString("X2");
}
} return str;
} //字符串转换为16进制byte数组
public byte[] StrToHexByte(string data)
{
data = data.Replace(" ", "");
if ((data.Length % 2) != 0)
{
data += " ";
} byte[] bytes = new byte[data.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert .ToByte (data.Substring (i * 2,2),16);
} return bytes;
}

4、资源释放

  开发项目使用平台是.net,工具vs2010,语言是C#,因为.net有垃圾回收机制,因此在实际开发中产生的托管资源都是系统自动释放完成。在做本项目时采用winform进行开发的,在此过程中发现一个问题:在关闭Form窗体是运行的系统并没有完全关闭。查找原因,应该是有资源没有被释放。而socket套接字产生的资源恰好是非托管资源,此现象表明系统中有socket资源没有被完全释放掉。因此写了一个资源释放函数:

public void Dispose()
{
try
{
this.ClientSocket.Shutdown(SocketShutdown.Both);
this.ClientSocket.Dispose();
this.ClientSocket.Close();
this.ClientSocket = null;
}
catch
{
}
}

上述函数的功能就是释放socket所产生的资源,调用后发现还是存在此问题,几经调试发现虽然把产生socket通道的监听客户端资源释放完毕,服务器端的serversocket并没有被释放,于是有了下一个函数:

public void CloseSocket()
{
if (serverSocket != null)
{
serverSocket.SocketLister.Dispose();
serverSocket.SocketLister = null;
serverSocket.Dispose();//调用的上一个函数
serverSocket = null;
}
}

在上述函数完成后,套接字socket所产生的资源确实被释放完毕,系统在form关闭后能真正关闭。到此资源好像已经被释放掉,但紧接着新的问题产生了:

在什么时候什么地方调用释放资源的函数?

个人简单看法:

1)系统中止时调用

2)socket通道中断时调用

补充:

5、开启并保持服务监听

在socket通信中,服务端的socket监听其实是需要一直打开并且保持的,只有这样才能随时监听连接的客户端。项目中示例:

private void button1_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(new ThreadStart(StartServer));
thread.Start();
} public void StartServer()
{
int port = Convert.ToInt32(GetText(this.tbPort));
string ipStr = GetText (this.tbServerIPStr);
if (serverSocket == null)
{
serverSocket = new ServerSocket(port);
serverSocket.Start(ipStr);//
}
else
{
MessageBox.Show("监听已开启");
}
} public void Start(string ipStr)
{
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, Port);
//IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(ipStr), Port);
SocketLister = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketLister.Bind(localEndPoint); try
{
SocketLister.Listen(MaxConnection);
while (IsRunning)
{
ClientSocket = SocketLister.Accept();
//保存socket
Session newSession = new Session(ClientSocket);
lock (sessionLock)
{
sessionTable.Add(newSession.IP, newSession);
} SocketConnection socketConnection = new SocketConnection(ClientSocket);
socketConnection.ReceiveDatagram();
}
}
catch (SocketException ex)
{
}
}

解释:点击按钮开启新的线程thread,执行方法StartServer,StartServer调用方法Start,Start方法中使用死循环开启并保持监听。

.net平台下C#socket通信(转)的更多相关文章

  1. .net平台下C#socket通信(中)

    上篇.net平台下C#socket通信(上)介绍了socket通信的基本原理及最基本的通信方式.本文在此基础上就socket通信时经常遇到的问题做一个简单总结,都是项目中的一些小问题,拿来此处便于下次 ...

  2. .net平台下C#socket通信(上)

    在开始介绍socket前先补充补充基础知识,在此基础上理解网络通信才会顺理成章,当然有基础的可以跳过去了.都是废话,进入正题. TCP/IP:Transmission Control Protocol ...

  3. .net 平台下, Socket通讯协议中间件设计思路(附源码)

    .net 平台下,实现通讯处理有很多方法(见下表),各有利弊: 序号 实现方式 特点 1 WCF 优点:封装好,方便.缺点:难学,不跨平台 2 RocketMQ,SuperSocket等中间件 优点: ...

  4. 移动平台下的Socket几个问题

    在页游时代,使用Flash ActionScript 3.0进行开发,as3提供比较简单和健全的socket API.到了手游时代,基于tcp的socket编程遇到了一些棘手的问题.通常情况下手游都要 ...

  5. 一步一步从原理跟我学邮件收取及发送 4.不同平台下的socket

    既然是面向程序员的文章那当然不能只说说原理,一定要有实际动手的操作.    其实作为我个人的经历来说,对于网络编程,这是最重要的一章! 作为一位混迹业内近20年的快退休的程序员,我学习过很多的开发语言 ...

  6. C#socket通信1

    .net平台下C#socket通信(上) 完全是基础,新手可以随意看看,大牛可以关闭浏览页了,哈哈. 在开始介绍socket前先补充补充基础知识,在此基础上理解网络通信才会顺理成章,当然有基础的可以跳 ...

  7. .net平台下socket异步通讯(代码实例)

    你应该知道的.net平台下socket异步通讯(代码实例) 1,首先添加两个windows窗体项目,一个作为服务端server,一个作为客户端Client 2,然后添加服务端代码,添加命名空间,界面上 ...

  8. Android平台下的TCP/IP传输(客户端)

    在工科类项目中,嵌入式系统与软件系统或后台数据库之间的信息传输是实现“物联网”的一种必要的途径,对已简单概念的物联网,通常形式都是一个单片机/嵌入式系统实现数据的采集及其处理,通过蓝牙,wifi或者是 ...

  9. 基于TCP与UDP协议的socket通信

    基于TCP与UDP协议的socket通信 C/S架构与初识socket 在开始socket介绍之前,得先知道一个Client端/服务端架构,也就是 C/S 架构,互联网中处处充满了 C/S 架构(Cl ...

随机推荐

  1. Android UI开发详解之Fragment

    Fragment是Android自从3.0之后新加入的一个组件,我相信很多人都已经听说过这个组件了,但这个组件到底是个什么,如何去使用他呢,且听我讲来. 以下部分资料来自官网(官网才是王道,其他都是浮 ...

  2. C# winform 若要在加载设计器前避免可能发生的数据丢失,必须纠正以下错误

    winform中有时添加了新控件之后编译会报错: 若要在加载设计器前避免可能发生的数据丢失,必须纠正以下错误,如图: 解决方案: 1.“解决方案”→“批生成”→“清理”→“确定”: 2.“解决方案”→ ...

  3. Delphi读取Word

    Delphi读取Word现在关于往Word中写入数据的方法比较多,现在专门开个贴子,希望大家把自己读取Word内容的心得体会说一下,包括读取word文档中,有几个段落,如何读取第几个段落,读取有拼音的 ...

  4. 解决PHP5.3.x下ffmpeg安装配置问题

    本人的环境: OS : windows 7 64位 WAMP:2.1a PHP:5.3.3(之前是5.3.13) 项目需要用ffmpeg-php实现上传视频转码截图等功能,但是找了很多资料都没有把ff ...

  5. C语言——递归练习

    1.炮弹一样的球状物体,能够堆积成一个金字塔,在顶端有一个炮弹,它坐落在一个4个炮弹组成的层面上,而这4个炮弹又坐落在一个9个炮弹组成的层面上,以此类推.写一个递归函数CannonBall,这个函数把 ...

  6. BootStrap入门教程 (二) :BASE CSS(排版(Typography),表格(Table),表单(Forms),按钮(Buttons))

    上讲回顾:Bootstrap的手脚架(Scaffolding)提供了固定(fixed)和流式(fluid)两种布局,它同时建立了一个宽达940px和12列的格网系统. 基于手脚架(Scaffoldin ...

  7. 一个谷粉和3年的Google Reader重度使用者的碎碎念

    2013-03-14 上午看到Andy Rubin辞去Android业务主管职务.由Chrome及应用高级副总裁继任的新闻,还在想这会给Android带来什么,中午刷微博的时候就挨了当头一棒:Goog ...

  8. [Hive - LanguageManual] Describe

    Describe Describe Database Describe Table/View/Column Display Column Statistics Describe Partition D ...

  9. 【Hadoop学习】Super用户以其他用户的名义执行操作

    Hadoop版本:2.6.0 本文系从官方文档翻译而来,转载请尊重译者的工作,注明以下链接: http://www.cnblogs.com/zhangningbo/p/4146410.html 简介 ...

  10. Firefox 火狐网址生成二维码扩展推荐

    该扩展并未在火狐官方的扩展站中上线,在火狐中国的站点中也几乎很难找到,只是作为火狐中国版的一个集成扩展. 各位想要使用该扩展,但又不愿安装火狐中国版的的浏览迷可以通过以下地址独立安装该扩展. 扩展地址 ...