1、首先说下计算机网络中的TCP/IP参考模型
TCP/IP把网络分为5层,每一层负责完成不同的功能
1)应用层:传输报文,提供各种网络应用,有FTP、SMTP、HTTP等协议
2)运输层:传输报文段,为应用程序的客户机和服务器之间提供传输应用层报文服务,协议有TCP,UDP
3)网络层:传输数据包,协议有IP协议,选路协议
4)链路层:传输数据帧,以太网就属于这个层
5)物理层:在节点之间传输比特流
应用程序是通过套接字访问网络下层的服务的,套接字是网络运输层和应用层的一个编程接口,在程序中通过套接字来进行TCP和UDP传输,运输层以下的层对程序员透明
TCP和UDP是最基本的传输协议,应用层的所有协议都是基于这两个协议进行封装扩展的
TCP:可靠的,面向连接的连接数据传输服务,传输的是字节流,能保证数据的有序性
1)建立连接(三次握手) 2)传输数据 3)断开连接(四次握手)
UDP:不可靠,面向数据报的无连接的数据传输服务,传输的是是数据包,不保证数据的有序性
广播:可以向同一个子网内的所有主机发送广播数据
组播:可以向所有加入组网的主机发送组播数据
2、编程(本节由于篇幅太长,这里只说TCP)
.NET 框架提供了下面几个类对实现网络通信:
Socket:提供最基本的TCP和UDP服务
TcpListener/TcpClient 和 UdpClient:对Socket类进行了基本封装
NetworkStream:网络流,对网络数据的写入和读取
使用套接字的步骤
TCP:服务器:创建套接字 -》 绑定套接字Bind -》 监听套接字Listen -》 接受连接Accept -》 发送/接受Send/Receive -》 关闭套接字CLose
:客户端:创建套接字 -》 连接 -》 发送/接受Send/Receive -》 关闭套接字CLose
UDP:创建套接字 -》 绑定套接字Bind -》 发送/接受SendTo/ReceiveFrom -》 关闭套接字CLose
1)Socket(使用异步方式连接和收发信息,因为等待连接和等待数据的时候会让主线程阻塞)
这里就不贴代码了,跟下面的差不多,后面下载的代码有(我用的VS2012,低版本不能查看可以直接看源文件的代码)
2)TcpListener/TcpClient(这里也使用异步方式来连接和接收信息)
首先创建一个异步用的数据对象
public class StateObject { public NetworkStream networkstream = null; public byte[] buffer = new byte[1024]; public StateObject(NetworkStream netstream) { this.networkstream = netstream; } }
1)服务器端(异步监听,当收到连接,则异步读取数据,注意捕获连接断开时的异常)
TcpListener listener; NetworkStream networkstream; private void btnBind_Click(object sender, EventArgs e) { //绑定,启动异步接受连接 try { int port = int.Parse(tbPort.Text); IPAddress ipaddress = IPAddress.Parse(tbIP.Text); IPEndPoint endpoint = new IPEndPoint(ipaddress, port); listener = new TcpListener(endpoint); listener.Start(10); //监听数位10 //开始异步接收连接请求 listener.BeginAcceptTcpClient(DoAcceptTcpClientCallback, listener); btn.Text = "Close"; } catch (ArgumentNullException) { } catch (SocketException) { } } //异步接收请求函数 public void DoAcceptTcpClientCallback(IAsyncResult ar) { TcpListener listener = (TcpListener)ar.AsyncState; try { TcpClient client = listener.EndAcceptTcpClient(ar); //收到连接 this.BeginInvoke(new ThreadStart(() => { //收到连接请求,这里可以让主线程更新UI })); networkstream = new NetworkStream(client.Client); StateObject so = new StateObject(networkstream); //开始异步读取数据 networkstream.BeginRead(so.buffer, 0, so.buffer.Length, ReadCallback, so); //如果需要继续监听,则继续异步接收 listener.BeginAcceptTcpClient(DoAcceptTcpClientCallback, listener); } catch (ObjectDisposedException) { //Socket关闭 } } //异步读取数据函数 public void ReadCallback(IAsyncResult ar) { StateObject so = (StateObject)ar.AsyncState; try { NetworkStream myNetworkStream = so.networkstream; int length = myNetworkStream.EndRead(ar); string receivemsg = Encoding.UTF8.GetString(so.buffer, 0, length); this.BeginInvoke(new ThreadStart(() => { //收到数据,在这里可以让主线程更新UI })); so.buffer = new byte[1024]; //继续接收数据 myNetworkStream.BeginRead(so.buffer, 0, so.buffer.Length, ReadCallback, so); } catch (IOException) { this.BeginInvoke(new ThreadStart(() => { //连接断开 })); networkstream = null; } }
这样,服务器端就完成了监听
2)客户端(同样通过异步实现)
TcpClient client; NetworkStream networkstream; private void btnConnect_Click(object sender, EventArgs e) { int port = int.Parse(tbPort.Text); IPAddress ipaddress = IPAddress.Parse(tbIP.Text); IPEndPoint endpoint = new IPEndPoint(ipaddress, port); if (client == null) { //开始异步连接 client = new TcpClient(); client.BeginConnect(ipaddress, port, ConnectCallback, client); } } private void ConnectCallback(IAsyncResult ar) { TcpClient t = ar.AsyncState as TcpClient; try { t.EndConnect(ar); networkstream = t.GetStream(); this.BeginInvoke(new ThreadStart(() => { //连接成功 })); //开始异步接受数据 StateObject so = new StateObject(networkstream); networkstream.BeginRead(so.buffer, 0, so.buffer.Length, ReadCallback, so); } catch (SocketException) { this.BeginInvoke(new ThreadStart(() => { //连接失败,让UI更新 })); } } public void ReadCallback(IAsyncResult ar) { StateObject so = (StateObject)ar.AsyncState; try { NetworkStream myNetworkStream = so.networkstream; int length = myNetworkStream.EndRead(ar); string receivemsg = Encoding.UTF8.GetString(so.buffer, 0, length); this.BeginInvoke(new ThreadStart(() => { //更新UI })); //继续异步接受数据 so.buffer = new byte[1024]; myNetworkStream.BeginRead(so.buffer, 0, so.buffer.Length, ReadCallback, so); } catch (IOException) { this.BeginInvoke(new ThreadStart(() => { //连接断开 })); networkstream = null; } }
3)接下来是发送数据
private void btnSend_Click(object sender, EventArgs e) { if (networkstream != null) { byte[] sendbyte = System.Text.Encoding.UTF8.GetBytes("你好:)"); networkstream.Write(sendbyte, 0, sendbyte.Length); } else { tbMsg.AppendText("未连接\n"); } }
具体项目代码 https://files.cnblogs.com/bomo/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B.zip