TCP、UDP、Socket 通信(原)
2015-08-21 16:55
477 查看
说明:本随笔主要演示自己给自己发送消息例子,分别使用了TCP协议、UDP协议以及socket套接字通信。使用socket套接字了模拟TCP、UDP通信实现原理。其中有些源码都来自《C#高级编程 第7版》,并附加了自己的理解,有的也进行了一些简单的拓展。
第一次原创随笔,很多地方可能考虑不周或理解有误,希望大家留言指正,与大家共同进步。也希望大家不喜勿喷,给点鼓励还是比较好的~~ 闲话少说,进入主题!
一、TCP 类
TCP是基于连接的一种通信模式,我们需要创建客户端与服务器端来进行通信。在客户端读取文件,并将文件内容发送到服务器端进行显示。
客户端:
服务器:
运行效果图:
客户端
服务器
二、UDP
UDP是一个无连接的协议,因此可以在一个程序中实现收发消息。
情形1:使用同一个 UdpClient 实例实现收发消息
情形2:使用一个 UdpClient 实例发送消息, 使用另一个 UdpClient 实例接收消息
运行效果图:
三、Socket 通信
TCP、UDP 底层通信都是通过 socket 套接字实现。
1、模拟 TCP 通信实现
客户端:
服务器端:
运行效果图:
客户端
服务器端
说明:基于链接的通信,应先启动服务器端程序进行监听,后启动客户端程序。否则客户端在尝试连接时,没有服务器进行相应,尝试一定的时间后,若一直无响应,则抛出远程主机拒绝的异常。
2、模拟 UDP 通信实现
不基于连接的通信,只需要一个程序端即可。
运行效果图:
注意:对于非连接的通信,在服务器端socket绑定端口(Bind()方法,而非Receive()方法,注意区分)之前,若客户端发送了消息A,则在服务器端口绑定后收不到该消息A,只能收到在绑定端口后客户端发送来的消息!
第一次原创随笔,很多地方可能考虑不周或理解有误,希望大家留言指正,与大家共同进步。也希望大家不喜勿喷,给点鼓励还是比较好的~~ 闲话少说,进入主题!
一、TCP 类
TCP是基于连接的一种通信模式,我们需要创建客户端与服务器端来进行通信。在客户端读取文件,并将文件内容发送到服务器端进行显示。
客户端:
using System; using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.IO; namespace TcpSender { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void btnSend_Click(object sender, EventArgs e) { TcpClient tcpClient = new TcpClient("127.0.0.1",2112); // 配置主机号及端口 127.0.0.1 2112 NetworkStream nws = tcpClient.GetStream(); // 获取连接流 string path = txbFile.Text; // 配置文件路径 FileStream fs = new FileStream(path, FileMode.Open); int data = fs.ReadByte(); while (data != -1) { nws.WriteByte((byte)data); data = fs.ReadByte(); } fs.Close(); nws.Close(); tcpClient.Close(); } } }
服务器:
using System; using System.Net; using System.Net.Sockets; using System.IO; using System.Threading; using System.Windows.Forms; namespace TcpReceiver { public partial class Form1 : Form { public Form1() { InitializeComponent(); Thread thread = new Thread(new ThreadStart(Listen)); // 防止主线程在监听到请求之前处于卡死状态 thread.Start(); } // 监听功能 public void Listen() { IPAddress localAddr = IPAddress.Parse("127.0.0.1"); Int32 port = 2112; TcpListener tcpListener = new TcpListener(localAddr, port); // 也可以仅绑定端口,因为发送者与接收者均是本地 tcpListener.Start(); while (true) // 持续监听 { TcpClient tcpClient = tcpListener.AcceptTcpClient(); // 监听到请求前处于阻塞状态 NetworkStream nws = tcpClient.GetStream(); // 获取监听流 StreamReader sr = new StreamReader(nws, System.Text.Encoding.UTF8); string content = sr.ReadToEnd(); // ★★★ very important ★★★ Invoke(new Action<string>(UpdateDisplay), new object[] { content }); // 该线程拥有用户界面的句柄,因此无需从后台线程直接访问对话框 tcpClient.Close(); } // tcpListener.Stop(); } // 将接收到内容显示到控件中 public void UpdateDisplay(string text) { txbContent.Clear(); txbContent.Text = text; } } }
运行效果图:
客户端
服务器
二、UDP
UDP是一个无连接的协议,因此可以在一个程序中实现收发消息。
情形1:使用同一个 UdpClient 实例实现收发消息
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.IO; namespace UDPSender { class Program { static void Main(string[] args) { #region 模拟自己发送自己 使用UDPClient(通过) UdpClient client = new UdpClient(11000); //相当于给socket实例指定端口,参见socket模拟UDP实现原理(后续代码) IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000); // 服务器网址及端口 Console.WriteLine("Type in what you want to send :"); string sendMsg = Console.ReadLine(); byte[] sendBytes = Encoding.ASCII.GetBytes(sendMsg); client.Send(sendBytes, sendBytes.Length, iep); //将消息推送到 127.0.0.1:11000 byte[] rcvBytes = client.Receive(ref iep); // 接收127.0.0.1:11000 端口消息,非连接的消息需要缓存存放 Console.WriteLine("Received the following message:"); string rcvMessage = Encoding.ASCII.GetString(rcvBytes, 0, rcvBytes.Length); Console.WriteLine(rcvMessage); Console.ReadKey(); #endregion } } }
情形2:使用一个 UdpClient 实例发送消息, 使用另一个 UdpClient 实例接收消息
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.IO; namespace UDPSender { class Program { static void Main(string[] args) { #region 模拟自己发送自己 使用UDPClient(通过) UdpClient client = new UdpClient(); // 没有为socket指定端口,则在client.Send()时,系统默认为socket分配一个端口,参见Socket模拟UDP(后续代码) // IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0); UdpClient server = new UdpClient(11000); // 在客户端发送之前确保服务器已经进行端口绑定,为客户端发送的消息创建载体 IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000); // 192.168.106.231 服务器网址及端口 Console.WriteLine("Type in what you want to send :"); string sendMsg = Console.ReadLine(); byte[] sendBytes = Encoding.ASCII.GetBytes(sendMsg); client.Send(sendBytes, sendBytes.Length, iep); // 将消息推送到 127.0.0.1:11000 byte[] rcvBytes = server.Receive(ref iep); // 接收127.0.0.1:11000 端口消息,也可以使用 remote 端点,表示接收所有端口消息 Console.WriteLine("Received the following message:"); string rcvMessage = Encoding.ASCII.GetString(rcvBytes, 0, rcvBytes.Length); Console.WriteLine(rcvMessage); Console.ReadKey(); #endregion } } }
运行效果图:
三、Socket 通信
TCP、UDP 底层通信都是通过 socket 套接字实现。
1、模拟 TCP 通信实现
客户端:
using System; using System.Net; using System.Net.Sockets; using System.Text; namespace SocketSender { // 使用socket类模拟TCP发送原理 class Program { static void Main(string[] args) { byte[] ReceiveBytes = new byte[1024]; IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2112); // 创建远程端点 Console.WriteLine("Starting: Creating Socket Object "); while (true) { // 创建socket对象 Socket SocketSender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 连接远程端点 SocketSender.Connect(ipEndPoint); // 连接到远程端点,如果服务器端没有响应,则此处会挂起。若超时,则抛出远程服务器拒绝的异常。 Console.WriteLine("Successfully Connected to {0}",SocketSender.RemoteEndPoint); Console.WriteLine("Input the Message you want to send :"); string SendingMessage = Console.ReadLine(); byte[] ForwardMessage = Encoding.ASCII.GetBytes(SendingMessage + "[FINAL]"); // 发送消息 SocketSender.Send(ForwardMessage); // 接收从服务器传回相应消息,因为是基于连接的,因此使用同一个socket实例直接接收即可 int TotalBytesReceived = SocketSender.Receive(ReceiveBytes); // 打印输出接收到的消息 Console.WriteLine("Message Provided By Server: {0}", Encoding.ASCII.GetString(ReceiveBytes, 0, TotalBytesReceived)); Console.WriteLine(Environment.NewLine); SocketSender.Shutdown(SocketShutdown.Both); // 关闭该socket的发送和接收功能 SocketSender.Close(); } //Console.ReadKey(); } } }
服务器端:
using System; using System.Text; using System.Net; using System.Net.Sockets; namespace SocketReceiver { // 使用socket模拟TCP接收原理 class Program { static void Main(string[] args) { // 本实例是一个基于连接的socket通信,模拟 TCPClient 进行通信 Console.WriteLine("Starting: Creating Socket object"); Socket Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); Listener.Bind(new IPEndPoint(IPAddress.Any, 2112)); // 创建了基于本机IP地址的,端口号为任意值的,可用于所有网络接口(网线、电话线及其他形式网络接口)的绑定 Listener.Listen(10); // 挂起连接队列的最大长度 while (true) { Console.WriteLine("Waiting for connetion on port 2112 "); Socket socket = Listener.Accept(); // 若无socket,则处于阻塞状态,等待有效的socket实例 string ReceiveValue = string.Empty; while (true) { byte[] ReceivedByte = new byte[1024]; // 缓存区 int NumBytes = socket.Receive(ReceivedByte); // 接收到的是二进制数据,将接收到的数据放入到缓存中。也可以直接读到流中,使用 Stream stream = new networkstream(socket) Console.WriteLine("Receiving . . ."); ReceiveValue += Encoding.ASCII.GetString(ReceivedByte, 0, NumBytes); if (ReceiveValue.IndexOf("[FINAL]") > -1) { break; } } Console.WriteLine("Received Value: {0}", ReceiveValue); Console.WriteLine(Environment.NewLine); string ReplyValue = "Message successfully Received."; byte[] ReplyByte = Encoding.ASCII.GetBytes(ReplyValue); socket.Send(ReplyByte); // 发送的是二进制数据 // 对于面向连接的协议,建议先调用 Shutdown,然后再调用 Close。 这可以确保在已连接的套接字关闭之前,已发送和接收该套接字上的所有数据。 socket.Shutdown(SocketShutdown.Both); // 关闭该socket的关闭和接收功能 socket.Close(); } // Listener.Close(); } } }
运行效果图:
客户端
服务器端
说明:基于链接的通信,应先启动服务器端程序进行监听,后启动客户端程序。否则客户端在尝试连接时,没有服务器进行相应,尝试一定的时间后,若一直无响应,则抛出远程主机拒绝的异常。
2、模拟 UDP 通信实现
不基于连接的通信,只需要一个程序端即可。
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.IO; namespace UDPSender { class Program { static void Main(string[] args) { #region 模拟自己发送给自己,使用socket模拟UDP实现原理(通过) // Server Thread server = new Thread(new ThreadStart(ReceiveServer)); server.Start(); // ★★★确保服务端已建立网络监听★★★ Thread.Sleep(50); // Client IPHostEntry ipHostEntry = Dns.GetHostEntry("127.0.0.1"); IPAddress ipAddress = ipHostEntry.AddressList[0]; IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 2112); while (true) { // UDP 的socket类型不支持stream Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); //socket.Bind(ipEndPoint); socket.Connect(ipEndPoint); // 若在此之前不给socket手动绑定一个端口,则程序会在此时默认给socket随机绑定一个端口 Console.WriteLine("Type in what you want to send:"); string SendString = Console.ReadLine(); byte[] SendBytes = Encoding.Default.GetBytes(SendString); //Thread.Sleep(3000); socket.Send(SendBytes); // 网络接收有延时,因此需要等待服务器进行接收 socket.Close(100);//等待 timeout 秒以发送所有剩余数据,然后关闭该套接字。 // 等同于socket.Close(100) 的效果 //Thread.Sleep(100); //socket.Close(); } #endregion } public static void ReceiveServer() { IPHostEntry ipHostEntry = Dns.GetHostEntry("127.0.0.1"); IPAddress ipAddress = ipHostEntry.AddressList[0]; IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 2112); #region 使用缓存接收数据 // Receive Message Socket socketListen = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); // set the protocol socketListen.Bind(ipEndPoint); // binding to the local point which need to be listened try { // UDP 协议不需要监听,直接接收消息即可 //socketListen.Listen(5); // 最多监听到5个队列的请求 while (true) { string result = string.Empty; if (socketListen.Available < 1) // 判断是否有数据,若没有数据而直接进行receive,则会使线程阻塞等待数据的到来。return 无:0 有:>0 { Thread.Sleep(20); } else { byte[] data = new byte[1024]; int NumBytes = socketListen.Receive(data); // 如果网络监听不到数据,则该语句会使线程处于阻塞状态而等待消息,因此要添加判断语句判断数据是否有效 Console.WriteLine("Receiving.."); if (NumBytes > 0) { result = Encoding.Default.GetString(data, 0, NumBytes); Console.WriteLine("Server Received the follow message:"); Console.WriteLine(result); Console.WriteLine(Environment.NewLine); //Thread.Sleep(50000); } } } } catch (SocketException e) { string a = e.ErrorCode.ToString(); Console.WriteLine(e.Message + e.ErrorCode); } #endregion } } }
运行效果图:
注意:对于非连接的通信,在服务器端socket绑定端口(Bind()方法,而非Receive()方法,注意区分)之前,若客户端发送了消息A,则在服务器端口绑定后收不到该消息A,只能收到在绑定端口后客户端发送来的消息!
相关文章推荐
- 网络爬虫:分离生产者和消费者来优化爬虫程序
- 网络爬虫:分离生产者和消费者来优化爬虫程序
- 复杂网络 有关节点
- 【网络编程】之十三、ping程序实现
- 【网络编程】之十二、wsaeventselect+线程池 服务器实现
- 【网络编程】之十一、重叠IO Overlapped IO 完成例程
- 【网络编程】之十、重叠IO Overlapped IO
- 【网络编程】之九、事件选择WSAEventSelect
- 【网络编程】之八、异步选择WSAAsyncSelect
- 【网络编程】之七、select聊天室
- 【网络编程】之六、选择select
- 【网络编程】之五、异步模型
- 【网络编程】之四、socket网络编程例解
- 【网络编程】之三、socket网络编程
- 【网络编程】之二、socket API学习
- 【网络编程】之一、初识WinSocket
- python通过get方式,post方式发送http请求和接收http响应-urllib urllib2
- WebSocket(4)-- WebSocket与TCP、Http的关系
- TCP/IP-IP
- TransferResult处理中用MvcHttpHandler在mvc3和mvc5区别