-
C#中关于网络通信原理(IP地址、端口、协议)
第一部分:网络编程基础
第1章 网络编程核心概念
1.2 网络通信原理(IP地址、端口、协议)
一、为什么要懂这三个概念?
之前做C# TCP服务器时,客户端总是连不上,查了半天发现是把“127.0.0.1”写成了“127.0.0.2”——这就是不懂IP地址的坑。还有一次,服务器监听端口“80”,结果被系统的HTTP服务占用了,导致启动失败——这就是不懂端口的坑。IP地址、端口、协议是网络通信的“三要素”,就像寄快递需要“收件人地址”“门牌号”“快递类型”,缺一个都不行。这节就带你把这三个概vb.net教程C#教程python教程SQL教程access 2010教程念掰碎了讲,结合C#代码实战,让你不仅“知道是什么”,更能“知道怎么用”。
二、IP地址:网络世界的“门牌号”
IP地址是网络设备在网络中的唯一标识,就像你家的地址,别人只有知道你的地址才能给你寄快递。IP地址分为IPv4和IPv6两种。
-
IPv4:32位的“老门牌”
核心结构:32位二进制数,分成4个8位段,用十进制表示(如192.168.1.100)。
你需要知道:
范围:0.0.0.0到255.255.255.255,共约43亿个地址(已经用完了);
分类:A类(0.0.0.0-127.255.255.255)、B类(128.0.0.0-191.255.255.255)、C类(192.0.0.0-223.255.255.255);
特殊地址:127.0.0.1(回环地址,用于本地测试)、0.0.0.0(表示所有本地地址)、255.255.255.255(广播地址)。
C#实战:解析IPv4地址
csharp
using System.Net;
// 字符串转IPAddress
string ipStr = "192.168.1.100";
if (IPAddress.TryParse(ipStr, out IPAddress? ipAddress))
{
Console.WriteLine($"IP地址:{ipAddress}");
Console.WriteLine($"是否IPv4:{ipAddress.AddressFamily == AddressFamily.InterNetwork}");
Console.WriteLine($"二进制表示:{Convert.ToString(ipAddress.Address, 2).PadLeft(32, '0')}");
// 输出:11000000101010000000000101100100
}
// 获取本地IPv4地址
var localIpv4 = Dns.GetHostEntry(Dns.GetHostName())
.AddressList
.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
Console.WriteLine($"本地IPv4地址:{localIpv4}");
代码逐行讲:
1.IPAddress.TryParse:把字符串转换成IPAddress对象,避免无效IP地址抛出异常;
2.AddressFamily.InterNetwork:判断是否为IPv4地址(InterNetworkV6是IPv6);
3.Convert.ToString(ipAddress.Address, 2):把IPv4地址转换成32位二进制字符串,直观展示IP地址的底层结构;
4.Dns.GetHostEntry:通过主机名获取本地IP地址,避免硬编码127.0.0.1。
2. IPv6:128位的“新门牌”
核心结构:128位二进制数,分成8个16位段,用十六进制表示(如2001:0db8:85a3:0000:0000:8a2e:0370:7334)。
你需要知道:
范围:几乎无限(约3.4×10³⁸个地址),解决了IPv4地址耗尽的问题;
简化写法:连续的0可以省略,用::代替(如2001:db8::8a2e:370:7334);
特殊地址:::1(回环地址,对应IPv4的127.0.0.1)、::(表示所有本地地址)。
C#实战:解析IPv6地址
csharp
using System.Net;
// 字符串转IPAddress
string ipv6Str = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
if (IPAddress.TryParse(ipv6Str, out IPAddress? ipv6Address))
{
Console.WriteLine($"IPv6地址:{ipv6Address}");
Console.WriteLine($"是否IPv6:{ipv6Address.AddressFamily == AddressFamily.InterNetworkV6}");
Console.WriteLine($"简化写法:{ipv6Address.ToString()}");
// 输出:2001:db8:85a3::8a2e:370:7334
}
// 获取本地IPv6地址
var localIpv6 = Dns.GetHostEntry(Dns.GetHostName())
.AddressList
.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetworkV6);
Console.WriteLine($"本地IPv6地址:{localIpv6}");
代码逐行讲:
1.IPAddress.TryParse:同样支持IPv6地址解析,自动处理简化写法;
2.AddressFamily.InterNetworkV6:判断是否为IPv6地址;
3.ipv6Address.ToString():自动输出简化后的IPv6地址,避免手动处理连续的0。
3. 子网掩码与网关:IP地址的“配套设施”
子网掩码:用于区分IP地址的“网络部分”和“主机部分”(如255.255.255.0表示前24位是网络部分,后8位是主机部分);
网关:连接不同网络的“出入口”(如你家路由器的IP地址192.168.1.1,就是你家网络和互联网的网关)。
C#实战:获取子网掩码与网关
csharp
using System.Net;
using System.Net.NetworkInformation;
var interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (var iface in interfaces)
{
if (iface.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
iface.OperationalStatus == OperationalStatus.Up)
{
var properties = iface.GetIPProperties();
foreach (var unicast in properties.UnicastAddresses)
{
if (unicast.Address.AddressFamily == AddressFamily.InterNetwork)
{
Console.WriteLine($"网卡名称:{iface.Name}");
Console.WriteLine($"IP地址:{unicast.Address}");
Console.WriteLine($"子网掩码:{unicast.IPv4Mask}");
Console.WriteLine($"网关:{properties.GatewayAddresses.FirstOrDefault()?.Address}");
Console.WriteLine("------------------------");
}
}
}
}
代码逐行讲:
1.GetIPProperties:获取网络接口的IP属性,包括子网掩码、网关、DNS服务器;
2.UnicastAddresses:获取单播IP地址(区别于广播、组播地址);
3.IPv4Mask:获取IPv4的子网掩码,IPv6没有子网掩码,用前缀长度表示(如/64);
4.GatewayAddresses:获取网关地址,通常是路由器的IP地址。
三、端口:网络程序的“门牌号”
端口是传输层的标识,用于区分同一台设备上的不同网络程序。就像你家的“房间号”,快递员(传输层)根据房间号把快递送到对应的房间(程序)。
-
端口的基本概念
范围:0-65535,共65536个端口;
分类:
o知名端口(0-1023):预留给常用服务(如HTTP用80,HTTPS用443,FTP用21);
o注册端口(1024-49151):用于用户程序(如Tomcat默认用8080,MySQL默认用3306);
o动态端口(49152-65535):系统自动分配给临时网络连接(如浏览器访问网页时的本地端口);
特点:同一台设备上,同一协议(TCP/UDP)的端口不能重复使用。
C#实战:检测端口是否被占用
csharp
using System.Net;
using System.Net.Sockets;
public static bool IsPortInUse(int port, AddressFamily addressFamily = AddressFamily.InterNetwork)
{
try
{
// 尝试绑定端口,如果绑定失败说明端口被占用
using var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(addressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, port));
return false;
}
catch (SocketException ex)
{
// 错误码10048表示端口被占用
if (ex.SocketErrorCode == SocketError.AddressAlreadyInUse)
{
return true;
}
// 其他错误(如权限不足)
throw;
}
}
// 调用示例
bool is80InUse = IsPortInUse(80);
Console.WriteLine($"端口80是否被占用:{is80InUse}");
代码逐行讲:
1.Socket.Bind:尝试绑定指定端口,如果端口已被占用,会抛出SocketException;
2.SocketError.AddressAlreadyInUse:错误码10048表示端口被占用,这是最常见的情况;
3.IPAddress.Any:表示绑定所有本地IPv4地址,IPAddress.IPv6Any表示绑定所有本地IPv6地址;
4.为什么用Socket而不是TcpListener?因为Socket更底层,能同时检测TCP和UDP端口(把SocketType.Stream改成SocketType.Dgram,ProtocolType.Tcp改成ProtocolType.Udp即可)。
2. 端口的常见问题
端口被占用:解决方法是换一个端口,或者关闭占用端口的程序(用netstat -ano | findstr :80命令找到占用端口的进程ID,再用taskkill /pid 1234 /f杀死进程);
权限不足:绑定知名端口(0-1023)需要管理员权限,普通用户只能绑定1024以上的端口;
端口耗尽:如果设备上有大量临时网络连接,可能会耗尽动态端口,解决方法是调整动态端口范围(Windows下用netsh int ipv4 set dynamicport tcp start=49152 num=16384命令)。
四、协议:网络通信的“规则手册”
协议是网络设备之间通信的“规则手册”,规定了数据的格式、传输方式、错误处理等。就像你和外国人说话需要用同一种语言(协议),否则对方听不懂。
-
协议的基本概念
分层设计:协议是分层的,每层协议只负责自己的功能(如IP协议负责网络层路由,TCP协议负责传输层可靠传输);
协议族:一组相关的协议组成协议族(如TCP/IP协议族包含IP、TCP、UDP、HTTP等);
知名协议:HTTP(网页浏览)、HTTPS(安全网页浏览)、TCP(可靠传输)、UDP(实时传输)、IP(路由)、ICMP(网络检测)。
C#实战:解析HTTP协议(应用层协议)
csharp
using System;
using System.Net.Sockets;
using System.Text;
public static async Task ParseHttpProtocol(string host, int port = 80)
{
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(host, port);
// 发送HTTP GET请求(严格按照HTTP协议格式)
string request = $"GET / HTTP/1.1
Host: {host}
Connection: close
";
byte[] requestBytes = Encoding.ASCII.GetBytes(request);
await socket.SendAsync(requestBytes, SocketFlags.None);
// 接收HTTP响应
byte[] buffer = new byte[4096];
int bytesRead = await socket.ReceiveAsync(buffer, SocketFlags.None);
string response = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine("HTTP响应:");
Console.WriteLine(response);
// 解析HTTP响应头
string[] lines = response.Split(new[] { "
" }, StringSplitOptions.RemoveEmptyEntries);
string statusLine = lines[0];
Console.WriteLine($"
状态行:{statusLine}");
foreach (string line in lines.Skip(1))
{
if (line.Contains(":"))
{
string[] parts = line.Split(new[] { ":" }, 2);
Console.WriteLine($"响应头:{parts[0]} = {parts[1].Trim()}");
}
}
}
// 调用示例
await ParseHttpProtocol("www.baidu.com");
代码逐行讲:
1.Socket.ConnectAsync:根据主机名和端口号建立TCP连接(传输层协议);
2.HTTP请求格式:严格按照HTTP协议要求,第一行是方法 路径 协议版本,然后是请求头,最后是两个换行( )表示请求头结束;
3.Socket.SendAsync:发送HTTP请求(应用层协议),底层自动处理TCP协议的可靠传输;
4.解析HTTP响应:把响应内容按 分割,第一行是状态行(如HTTP/1.1 200 OK),后面是响应头,最后是响应体。
2. 协议的核心要素
数据格式:规定了数据的结构(如HTTP请求的状态行、请求头、请求体);
传输方式:规定了数据的传输方式(如TCP是面向连接的,UDP是无连接的);
错误处理:规定了错误的处理方式(如TCP的重传机制,HTTP的状态码);
交互流程:规定了通信的交互流程(如TCP的三次握手,HTTP的请求-响应)。
五、三要素的关系:网络通信的“铁三角”
IP地址、端口、协议是网络通信的“三要素”,缺一不可:
IP地址:确定通信的“目标设备”;
端口:确定设备上的“目标程序”;
协议:确定通信的“规则方式”。
类比:你要给朋友发快递,IP地址是“朋友家的地址”,端口是“朋友家的房间号”,协议是“快递类型(如顺丰、京东)”。只有同时知道这三个信息,快递才能准确送到朋友手里。
六、基础知识拓展
-
为什么需要NAT(网络地址转换)?
IPv4地址耗尽后,NAT技术应运而生,它能把多个私有IP地址转换成一个公网IP地址,让多个设备共享一个公网IP地址上网。
私有IP地址范围:10.0.0.0-10.255.255.255、172.16.0.0-172.31.255.255、192.168.0.0-192.168.255.255;
公网IP地址:全球唯一的IP地址,能直接访问互联网。 -
为什么HTTPS比HTTP安全?
HTTP是明文传输,数据在传输过程中可能被窃听、篡改;HTTPS在HTTP基础上添加了SSL/TLS加密层,数据在传输过程中是加密的,只有客户端和服务器能解密,防止窃听和篡改。 -
TCP和UDP的选择依据?
TCP:适合对可靠性要求高的场景(如文件传输、网页浏览、电商支付);
UDP:适合对实时性要求高的场景(如视频通话、游戏、实时监控);
记住一句话:可靠用TCP,实时用UDP。
七、总结:三要素是网络编程的“地基”
IP地址:找到目标设备,分为IPv4和IPv6,要懂子网掩码和网关;
端口:找到设备上的目标程序,分为知名端口、注册端口、动态端口,要懂端口占用的解决方法;
协议:遵守通信规则,分为应用层、传输层、网络层、数据链路层、物理层协议,要懂常见协议的用途;
三要素缺一不可,就像盖房子需要地基,网络编程的所有代码都是基于这三个要素的。
比如你用C#写TCP服务器,需要指定IP地址(0.0.0.0表示所有本地地址)、端口(如8888)、协议(TCP);写TCP客户端,需要指定服务器的IP地址、端口、协议,才能建立连接。下一节我们就用C#实战TCP和UDP编程,把这三个要素落地成代码。
本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49509.html










