TCP、UDP、Socket 通信(原)

说明:本随笔主要演示自己给自己发送消息例子,分别使用了TCP协议、UDP协议以及socket套接字通信。使用socket套接字了模拟TCP、UDP通信实现原理。其中有些源码都来自《C#高级编程 第7版》,并附加了自己的理解,有的也进行了一些简单的拓展。

第一次原创随笔,很多地方可能考虑不周或理解有误,希望大家留言指正,与大家共同进步。也希望大家不喜勿喷,给点鼓励还是比较好的~~ 闲话少说,进入主题!

一、TCP 类

  TCP是基于连接的一种通信模式,我们需要创建客户端与服务器端来进行通信。在客户端读取文件,并将文件内容发送到服务器端进行显示。

客户端:

 1 using System;
 2 using System.Windows.Forms;
 3 using System.Net;
 4 using System.Net.Sockets;
 5 using System.IO;
 6
 7 namespace TcpSender
 8 {
 9     public partial class Form1 : Form
10     {
11         public Form1()
12         {
13             InitializeComponent();
14         }
15
16         private void btnSend_Click(object sender, EventArgs e)
17         {
18             TcpClient tcpClient = new TcpClient("127.0.0.1",2112);  // 配置主机号及端口 127.0.0.1 2112
19             NetworkStream nws = tcpClient.GetStream(); // 获取连接流
20             string path = txbFile.Text; // 配置文件路径
21             FileStream fs = new FileStream(path, FileMode.Open);
22             int data = fs.ReadByte();
23
24             while (data != -1)
25             {
26                 nws.WriteByte((byte)data);
27                 data = fs.ReadByte();
28             }
29             fs.Close();
30             nws.Close();
31             tcpClient.Close();
32         }
33     }
34 }

  服务器:

 1 using System;
 2 using System.Net;
 3 using System.Net.Sockets;
 4 using System.IO;
 5 using System.Threading;
 6 using System.Windows.Forms;
 7
 8 namespace TcpReceiver
 9 {
10     public partial class Form1 : Form
11     {
12         public Form1()
13         {
14             InitializeComponent();
15             Thread thread = new Thread(new ThreadStart(Listen)); // 防止主线程在监听到请求之前处于卡死状态
16             thread.Start();
17         }
18
19         // 监听功能
20         public void Listen()
21         {
22             IPAddress localAddr = IPAddress.Parse("127.0.0.1");
23             Int32 port = 2112;
24             TcpListener tcpListener = new TcpListener(localAddr, port); // 也可以仅绑定端口,因为发送者与接收者均是本地
25             tcpListener.Start();
26
27             while (true) // 持续监听
28             {
29                 TcpClient tcpClient = tcpListener.AcceptTcpClient(); // 监听到请求前处于阻塞状态
30                 NetworkStream nws = tcpClient.GetStream(); // 获取监听流
31
32                 StreamReader sr = new StreamReader(nws, System.Text.Encoding.UTF8);
33                 string content = sr.ReadToEnd();
34
35                 // ★★★ very important ★★★
36                 Invoke(new Action<string>(UpdateDisplay), new object[] { content }); // 该线程拥有用户界面的句柄,因此无需从后台线程直接访问对话框
37                 tcpClient.Close();
38             }
39             // tcpListener.Stop();
40         }
41
42         // 将接收到内容显示到控件中
43         public void UpdateDisplay(string text)
44         {
45             txbContent.Clear();
46             txbContent.Text = text;
47         }
48     }
49 }

  运行效果图:

客户端

服务器

二、UDP

  UDP是一个无连接的协议,因此可以在一个程序中实现收发消息。

  情形1:使用同一个 UdpClient 实例实现收发消息

 1 using System;
 2 using System.Net;
 3 using System.Net.Sockets;
 4 using System.Text;
 5 using System.Threading;
 6 using System.IO;
 7
 8 namespace UDPSender
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             #region 模拟自己发送自己 使用UDPClient(通过)
15             UdpClient client = new UdpClient(11000); //相当于给socket实例指定端口,参见socket模拟UDP实现原理(后续代码)
16             IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000); // 服务器网址及端口
17
18             Console.WriteLine("Type in what you want to send :");
19             string sendMsg = Console.ReadLine();
20             byte[] sendBytes = Encoding.ASCII.GetBytes(sendMsg);
21             client.Send(sendBytes, sendBytes.Length, iep); //将消息推送到 127.0.0.1:11000
22
23
24             byte[] rcvBytes = client.Receive(ref iep);   // 接收127.0.0.1:11000 端口消息,非连接的消息需要缓存存放
25             Console.WriteLine("Received the following message:");
26             string rcvMessage = Encoding.ASCII.GetString(rcvBytes, 0, rcvBytes.Length);
27             Console.WriteLine(rcvMessage);
28             Console.ReadKey();
29             #endregion
30           }
31     }
32 }

  情形2:使用一个 UdpClient 实例发送消息, 使用另一个 UdpClient 实例接收消息

 1 using System;
 2 using System.Net;
 3 using System.Net.Sockets;
 4 using System.Text;
 5 using System.Threading;
 6 using System.IO;
 7
 8 namespace UDPSender
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             #region 模拟自己发送自己 使用UDPClient(通过)
15             UdpClient client = new UdpClient(); // 没有为socket指定端口,则在client.Send()时,系统默认为socket分配一个端口,参见Socket模拟UDP(后续代码)
16             // IPEndPoint remote = new IPEndPoint(IPAddress.Any, 0);
17             UdpClient server = new UdpClient(11000); // 在客户端发送之前确保服务器已经进行端口绑定,为客户端发送的消息创建载体
18
19             IPEndPoint iep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 11000); // 192.168.106.231 服务器网址及端口
20
21             Console.WriteLine("Type in what you want to send :");
22             string sendMsg = Console.ReadLine();
23             byte[] sendBytes = Encoding.ASCII.GetBytes(sendMsg);
24             client.Send(sendBytes, sendBytes.Length, iep); // 将消息推送到 127.0.0.1:11000
25
26
27             byte[] rcvBytes = server.Receive(ref iep);   // 接收127.0.0.1:11000 端口消息,也可以使用 remote 端点,表示接收所有端口消息
28             Console.WriteLine("Received the following message:");
29             string rcvMessage = Encoding.ASCII.GetString(rcvBytes, 0, rcvBytes.Length);
30             Console.WriteLine(rcvMessage);
31             Console.ReadKey();
32             #endregion
33         }
34     }
35 }

  运行效果图:

三、Socket 通信

  TCP、UDP 底层通信都是通过 socket 套接字实现。

  1、模拟 TCP 通信实现

  客户端:

 1 using System;
 2 using System.Net;
 3 using System.Net.Sockets;
 4 using System.Text;
 5
 6 namespace SocketSender
 7 {
 8     // 使用socket类模拟TCP发送原理
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             byte[] ReceiveBytes = new byte[1024];
14             IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2112); // 创建远程端点
15
16             Console.WriteLine("Starting: Creating Socket Object ");
17             while (true)
18             {
19                 // 创建socket对象
20                 Socket SocketSender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
21                 // 连接远程端点
22                 SocketSender.Connect(ipEndPoint); // 连接到远程端点,如果服务器端没有响应,则此处会挂起。若超时,则抛出远程服务器拒绝的异常。
23
24                 Console.WriteLine("Successfully Connected to {0}",SocketSender.RemoteEndPoint);
25
26                 Console.WriteLine("Input the Message you want to send :");
27                 string SendingMessage = Console.ReadLine();
28                 byte[] ForwardMessage = Encoding.ASCII.GetBytes(SendingMessage + "[FINAL]");
29
30                 // 发送消息
31                 SocketSender.Send(ForwardMessage);
32
33                 // 接收从服务器传回相应消息,因为是基于连接的,因此使用同一个socket实例直接接收即可
34                 int TotalBytesReceived = SocketSender.Receive(ReceiveBytes);
35
36                 // 打印输出接收到的消息
37                 Console.WriteLine("Message Provided By Server: {0}", Encoding.ASCII.GetString(ReceiveBytes, 0, TotalBytesReceived));
38                 Console.WriteLine(Environment.NewLine);
39                 SocketSender.Shutdown(SocketShutdown.Both); // 关闭该socket的发送和接收功能
40                 SocketSender.Close();
41             }
42             //Console.ReadKey();
43         }
44     }
45 }
46     

  服务器端:

 1 using System;
 2 using System.Text;
 3 using System.Net;
 4 using System.Net.Sockets;
 5
 6 namespace SocketReceiver
 7 {
 8     // 使用socket模拟TCP接收原理
 9     class Program
10     {
11         static void Main(string[] args)
12         {
13             // 本实例是一个基于连接的socket通信,模拟 TCPClient 进行通信
14             Console.WriteLine("Starting: Creating Socket object");
15             Socket Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
16
17             Listener.Bind(new IPEndPoint(IPAddress.Any, 2112)); // 创建了基于本机IP地址的,端口号为任意值的,可用于所有网络接口(网线、电话线及其他形式网络接口)的绑定
18             Listener.Listen(10); // 挂起连接队列的最大长度
19
20             while (true)
21             {
22                 Console.WriteLine("Waiting for connetion on port 2112 ");
23                 Socket socket = Listener.Accept();  // 若无socket,则处于阻塞状态,等待有效的socket实例
24                 string ReceiveValue = string.Empty;
25
26                 while (true)
27                 {
28                     byte[] ReceivedByte = new byte[1024]; // 缓存区
29                     int NumBytes = socket.Receive(ReceivedByte); // 接收到的是二进制数据,将接收到的数据放入到缓存中。也可以直接读到流中,使用 Stream stream = new networkstream(socket)
30                     Console.WriteLine("Receiving . . .");
31                     ReceiveValue += Encoding.ASCII.GetString(ReceivedByte, 0, NumBytes);
32                     if (ReceiveValue.IndexOf("[FINAL]") > -1)
33                     {
34                         break;
35                     }
36                 }
37                 Console.WriteLine("Received Value: {0}", ReceiveValue);
38                 Console.WriteLine(Environment.NewLine);
39                 string ReplyValue = "Message successfully Received.";
40                 byte[] ReplyByte = Encoding.ASCII.GetBytes(ReplyValue);
41                 socket.Send(ReplyByte); // 发送的是二进制数据
42
43                 // 对于面向连接的协议,建议先调用 Shutdown,然后再调用 Close。 这可以确保在已连接的套接字关闭之前,已发送和接收该套接字上的所有数据。
44                 socket.Shutdown(SocketShutdown.Both); // 关闭该socket的关闭和接收功能
45                 socket.Close();
46             }
47             // Listener.Close();
48         }
49     }
50 }

  运行效果图:

客户端

服务器端

  说明:基于链接的通信,应先启动服务器端程序进行监听,后启动客户端程序。否则客户端在尝试连接时,没有服务器进行相应,尝试一定的时间后,若一直无响应,则抛出远程主机拒绝的异常。

  2、模拟 UDP 通信实现

  不基于连接的通信,只需要一个程序端即可。

 1 using System;
 2 using System.Net;
 3 using System.Net.Sockets;
 4 using System.Text;
 5 using System.Threading;
 6 using System.IO;
 7
 8 namespace UDPSender
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             #region 模拟自己发送给自己,使用socket模拟UDP实现原理(通过)
15
16             // Server
17             Thread server = new Thread(new ThreadStart(ReceiveServer));
18             server.Start();
19
20             // ★★★确保服务端已建立网络监听★★★
21             Thread.Sleep(50);
22
23             // Client
24             IPHostEntry ipHostEntry = Dns.GetHostEntry("127.0.0.1");
25             IPAddress ipAddress = ipHostEntry.AddressList[0];
26             IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 2112);
27
28             while (true)
29             {
30                 // UDP 的socket类型不支持stream
31                 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
32                 //socket.Bind(ipEndPoint);
33                 socket.Connect(ipEndPoint); // 若在此之前不给socket手动绑定一个端口,则程序会在此时默认给socket随机绑定一个端口
34                 Console.WriteLine("Type in what you want to send:");
35                 string SendString = Console.ReadLine();
36                 byte[] SendBytes = Encoding.Default.GetBytes(SendString);
37                 //Thread.Sleep(3000);
38                 socket.Send(SendBytes);
39                 // 网络接收有延时,因此需要等待服务器进行接收
40                 socket.Close(100);//等待 timeout 秒以发送所有剩余数据,然后关闭该套接字。
41                 // 等同于socket.Close(100) 的效果
42                 //Thread.Sleep(100);
43                 //socket.Close();
44
45             }
46             #endregion
47         }
48
49         public static void ReceiveServer()
50         {
51             IPHostEntry ipHostEntry = Dns.GetHostEntry("127.0.0.1");
52             IPAddress ipAddress = ipHostEntry.AddressList[0];
53             IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 2112);
54
55             #region 使用缓存接收数据
56             // Receive Message
57             Socket socketListen = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);  // set the protocol
58             socketListen.Bind(ipEndPoint); // binding to the local point which need be listened
59
60             try
61             {
62                 // UDP 协议不需要监听,直接接收消息即可
63                 //socketListen.Listen(5); // 最多监听到5个队列的请求
64                 while (true)
65                 {
66                     string result = string.Empty;
67                     if (socketListen.Available < 1) // 判断是否有数据,若没有数据而直接进行receive,则会使线程阻塞等待数据的到来。return 无:0 有:>0
68                     {
69                         Thread.Sleep(20);
70                     }
71                     else
72                     {
73                         byte[] data = new byte[1024];
74                         int NumBytes = socketListen.Receive(data);  // 如果网络监听不到数据,则该语句会使线程处于阻塞状态而等待消息,因此要添加判断语句判断数据是否有效
75                         Console.WriteLine("Receiving..");
76                         if (NumBytes > 0)
77                         {
78                             result = Encoding.Default.GetString(data, 0, NumBytes);
79                             Console.WriteLine("Server Received the follow message:");
80                             Console.WriteLine(result);
81                             Console.WriteLine(Environment.NewLine);
82                             //Thread.Sleep(50000);
83                         }
84                     }
85                 }
86             }
87             catch (SocketException e)
88             {
89                 string a = e.ErrorCode.ToString();
90                 Console.WriteLine(e.Message + e.ErrorCode);
91             }
92             #endregion
93         }
94     }
95 }

  运行效果图:

  注意:对于非连接的通信,在服务器端socket绑定端口(Bind()方法,而非Receive()方法,注意区分)之前,若客户端发送了消息A,则在服务器端口绑定后收不到该消息A,只能收到在绑定端口后客户端发送来的消息!

时间: 2024-10-29 05:35:56

TCP、UDP、Socket 通信(原)的相关文章

Java TCP/UDP socket 编程流程总结

最近正好学习了一点用java socket编程的东西.感觉整体的流程虽然不是很繁琐,但是也值得好好总结一下. Socket Socket可以说是一种针对网络的抽象,应用通过它可以来针对网络读写数据.就像通过一个文件的file handler就可以都写数据到存储设备上一样.根据TCP协议和UDP协议的不同,在网络编程方面就有面向两个协议的不同socket,一个是面向字节流的一个是面向报文的. 对socket的本身组成倒是比较好理解.既然是应用通过socket通信,肯定就有一个服务器端和一个客户端.

高性能 TCP/UDP/HTTP 通信框架 HP-Socket v4.1.2

HP-Socket 是一套通用的高性能 TCP/UDP/HTTP 通信框架,包含服务端组件.客户端组件和 Agent 组件,广泛适用于各种不同应用场景的 TCP/UDP/HTTP 通信系统,提供 C/C++.C#.Delphi.E(易语言).Java.Python 等编程语言接口.HP-Socket 对通信层实现完全封装,应用程序不必关注通信层的任何细节:HP-Socket 提供基于事件通知模型的 API 接口,能非常简单高效地整合到新旧应用程序中. 为了让使用者能方便快速地学习和使用 HP-S

第二十天 TCP 及socket通信原理、http协议及web服务、httpd核心配置详解

一.TCP及socket通信原理详解 二.http协议及web服务原理(一) 三.http协议及web服务原理(二) 四.httpd核心配置详解 1.tcp.udp是一种传输协议,实现进程地址标记,套接字是一个虚拟设备,用来表明主机上的某个进程      众所周知:0-1023:管理员才有权限使用,永久地分配给某应用使用(由IANA分配)      注册端口:1024-41951:只有一部分被注册,分配原则上非特别严格.      动态端口或私有端口:41952-65535:由内核分配临时端口,

高性能 TCP/UDP/HTTP 通信框架 HP-Socket v4.0.1 发布

HP-Socket 是一套通用的高性能 TCP/UDP/HTTP 通信框架,包含服务端组件.客户端组件和 Agent 组件,广泛适用于各种不同应用场景的 TCP/UDP/HTTP 通信系统,提供 C/C++.C#.Delphi.E(易语言).Java.Python 等编程语言接口.HP-Socket 对通信层实现完全封装,应用程序不必关注通信层的任何细节:HP-Socket 提供基于事件通知模型的 API 接口,能非常简单高效地整合到新旧应用程序中. 为了让使用者能方便快速地学习和使用 HP-S

[Java] Tcp/udp 简单通信

本文转自  我自己的博客guozeyiblog.cn 欢迎来访 效果图: //UDP通信 import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.net.*; import javax.swing.*; class send extends JFrame implements ActionL

How To: Perl TCP / UDP Socket Programming using IO::Socket::INET

http://www.thegeekstuff.com/2010/07/perl-tcp-udp-socket-programming/ In this article, let us discuss how to write Perl socket programming using the inbuilt socket modules in Perl. Perl socket modules provides an object interface that makes it easier

TCP及socket通信原理

一.网络互联模型 因特网在刚面世时,只有同一制造商生产的计算机才能彼此通信,制定网络互联模型的目的就是为异种的计算机互连提供一个共同的基础和标准框架,并为保持相关标准的一致性和兼容性提供共同的参考. 互联参考模型: OSI七层模型(Open System Interconnect):应用层.表示层.会话层.传输层.网络层.数据链路层.物理层 DoD四层模型:是OSI七层模型的浓缩版,包括 进程/应用层.主机到主机层.因特网层.网络接入层 以上两种模型是层次型的,分层模型的优点主要在于: ①将网络

HTTP TCP UDP Socket 关系的几个经典图

  从上图可以看到,TCP/IP是个协议组,可分为三个层次:网络层.传输层和应用层. 在网络层有IP协议.ICMP协议.ARP协议.RARP协议和BOOTP协议. 在传输层中有TCP协议与UDP协议. 在应用层有FTP.HTTP.TELNET.SMTP.DNS等协议. 下图也是描述这个关系的. 参考: https://krystalchisholm.wordpress.com/2010/11/24/chapter-17/    另外我们经常说到的 Socket 的所在位置如下图:   Socke

马哥教育第二十天TCP及socket通信原理详解、http协议、httpd

1.tcp.udp是一种传输协议,实现进程地址标记,套接字是一个虚拟设备,用来表明主机上的某个进程      常用端口:0-1023:管理员才有权限使用,永久地分配给某应用使用      注册端口:1024-41951:只有一部分被注册,只要确保主机上没有进程使用该端口.      动态端口或私有端口:41952-65535:由内核分配临时端口,如果临时端口不够可以通过修改内核参数修改临时端口范围       /proc/sys/net/ipv4/ip_local_port_range:定义两个

TCP/UDP socket

TCP socket:有链接,绑定端口,接着去侦听,若有请求,那么accept(),获得新的socket,并且去接收/发送数据报. UDP socket:无连接,不需要侦听,也不用一个新的socket去接收/发送数据包,但发送时就取决本身绑定的端口.