C#网络编程(Socket编程)

0、写在前面的话

我创建的Unity、C#交流群,有兴趣可加入大家一起学习(人还很少,现在加入就是元老🙀):952914223

1、Socket 介绍

1.1 Socket是什么

Socket的英文原意是“插座”,的意思,通常在计算机编程中称作套接字,是对对于TCP/IP的封装,我们可以将Socket联想成是由两个Socket对象搭建的成的一根通信管道,管道的两端是这两个Socket对象,而这根管道的连接的是两台主机的应用进程(通过IP地址和端口号确定进程)
请添加图片描述

1.2 Socket在网络中的位置

在这里插入图片描述

2、C# 中的Socket参数

2.1 超时控制参数

  1. ReceiveTimeout:接收超时
socket.ReceiveTimeout = 5000; // 5秒(单位:毫秒)
// 超时抛出SocketException,ErrorCode为10060 (TimedOut)
  1. SendTimeout:发送超时
socket.SendTimeout = 3000; // 3秒
// 影响Send/SendTo操作
  1. BeginConnect:连接超时
// 通过Connect方法的异步版本实现
socket.BeginConnect(remoteEP, null, null);
if(!socket.Connected) {
    // 自定义超时逻辑
}

2.2 缓冲区参数

  1. ReceiveBufferSize:接收缓冲区大小
socket.ReceiveBufferSize = 65536; // 64KB
// 影响内核网络栈的接收缓冲区
  1. SendBufferSize:发送缓冲区大小
socket.SendBufferSize = 32768; // 32KB
// 影响内核网络栈的发送缓冲区

2.3 UDP专用参数

  1. EnableBroadcast:启用广播
udpSocket.EnableBroadcast = true;
// 允许发送到广播地址(如255.255.255.255)
  1. 多播设置
// 加入多播组
udpSocket.SetSocketOption(SocketOptionLevel.IP,
                        SocketOptionName.AddMembership,
                        new MulticastOption(IPAddress.Parse("224.0.0.1")));

// 设置多播TTL
udpSocket.SetSocketOption(SocketOptionLevel.IP,
                        SocketOptionName.MulticastTimeToLive,
                        2); // 最多跨越2个路由器

3、C# 中的Socket API

3.1 Socket(构造函数)

public Socket(SocketInformation socketInformation);
public Socket(SocketType socketType, ProtocolType protocolType);
public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);

常见的 Socket 对象创建实例:

// 监控 ip4 地址,套接字类型为 TCP ,协议类型为 TCP
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

3.1.1 SocketType

指定 Socket 类的实例表示的套接字类型。

SocketType对应的ProtocolType描述
UnknownUnknown指定未知的 Socket 类型
Stream(使用字节流)Tcp支持可靠、双向、基于连接的字节流
Dgram(使用数据报)Udp面向无连接
RawIcmp、lgmp支持对基础传输协议的访问
Rdm支持无连接、面向消息、以可靠方式发送的消息,
Seqpacket在网络上提供排序字节流的面向连接且可靠的双向传输

3.1.2 ProtocolType

表示协议类型,是一个 enum 类型。
其中枚举重复值,常见用途:

  • 为旧值提供别名
  • 向后兼容
  • 提供更清晰的命名选择
枚举值协议说明
Unknown-1未知协议类型
Unspecified0未指定的协议
IP0Internet 协议 (IP) - 原始 IP 数据包
IPv6HopByHopOptions0IPv6 逐跳选项头
Icmp1Internet 控制消息协议 (ICMP)
Igmp2Internet 组管理协议 (IGMP)
Ggp3网关到网关协议 (GGP)
IPv44IPv4 协议
Tcp6传输控制协议 (TCP)
Pup12PARC 通用数据包协议 (PUP)
Udp17用户数据报协议 (UDP)
Idp22Internet 数据报协议 (IDP)
IPv641IPv6 协议
IPv6RoutingHeader43IPv6 路由头
IPv6FragmentHeader44IPv6 分片头
IPSecEncapsulatingSecurityPayload50IPv6 封装安全负载 (ESP) 头
IPSecAuthenticationHeader51IPv6 认证头 (AH)
IcmpV658ICMP for IPv6
IPv6NoNextHeader59IPv6 无下一个头
IPv6DestinationOptions60IPv6 目的选项头
ND77Net Disk 协议 (非正式)
Raw255原始 IP 数据包
Ipx1000Internet 数据包交换协议 (IPX)
Spx1256顺序数据包交换协议 (SPX)
SpxII1257SPX 版本 2

3.1.3 AddressFamily

表示使用的网络寻址方案

枚举值协议说明使用场景
Unknown-1未知地址族保留值
Unspecified0未指定的地址族通用情况
Unix1Unix本地通信Unix域Socket
InterNetwork2IPv4地址族最常用的IPv4网络
ImpLink3ARPANET IMP地址历史遗留
Pup4PUP协议地址Xerox PUP网络
Chaos5MIT CHAOS协议历史遗留
NS6Xerox NS协议Xerox网络系统
Ipx6IPX/SPX地址Novell网络
Iso7ISO协议OSI协议族
Osi 7OSI协议同Iso
Ecma8ECMA协议欧洲计算机制造商协会
DataKit9Datakit协议AT&T Datakit
Ccitt 10CCITT协议TU-T协议
Sna11IBM SNA地址IBM系统网络架构
DecNet12 DECnet地址Digital Equipment Corporation网络
DataLink13直接数据链路接口数据链路层访问
Lat14LAT地址Local Area Transport
HyperChannel15NSC HyperchannelNetwork Systems Corporation
AppleTalk16AppleTalk地址Apple网络协议
NetBios17NetBios地址Windows网络基本输入输出系统
VoiceView18VoiceView地址VoiceView音频协议
FireFox19FireFox协议非标准协议
Banyan21Banyan VINESBanyan虚拟网络系统
Atm22本机ATM服务异步传输模式
InterNetworkV623IPv6地址族IPv6网络
Cluster24Microsoft WolfpackMicrosoft集群服务
Ieee1284425IEEE 1284.4工作组打印机共享协议
Irda26IrDA地址红外数据协会
NetworkDesigners28Network Designers OSI网关网络设计者协议
Max29最大地址族保留值

3.2 Bind()

绑定本地IP和端口,你将在在本地创建 IPEndPoint 对象,拥有此 ip:post 的访问权限。目的是绑定本地机器的某个端口,所有经过此端口的数据就归你管了。

public void Bind (System.Net.EndPoint localEP);

使用实例:

Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress iP = IPAddress.Parse("127.0.0.1");

serverSocket.Bind(new IPEndPoint(iP, 2300))

3.3 Connect()

与远程主机建立连接。Connect() 有四个重载方法,不必关注,只需知道,必需提供 IP 和 Post 两个值。

IPAddress iP = IPAddress.Parse("127.0.0.1");
IPEndPoint iPEndPoint = new IPEndPoint(iP, 2300);
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);        //创建与远程主机的连接
serverSocket.Connect(iPEndPoint);

3.4 Listen() (TCP)

监控所有发送到此主机的、特定端口的连接请求。服务端使用,客户端不需要。

public void Listen (int backlog);

使用 Bind() 后,使用 Listen() 方法进行监控,backlog 参数指定可排队等待接受的传入连接的数量,即挂起的连接队列的最大长度。

3.5 Accept() (TCP)

Accept() 以同步方式监听套接字,在连接请求队列中提取第一个挂起的连接请求,然后创建并返回一个新的 Socket 对象。

//创建终结点(EndPoint)
IPAddress ip = IPAddress.Any;             
IPEndPoint ipe = new IPEndPoint(ip, 8000);

//创建 socket 并开始监听
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipe);
serverSocket.Listen(10);//开始监听

//接受到client连接,为此连接建立新的socket,并接受信息
Socket temp = serverSocket.Accept();//为新建连接创建新的socket

注意的是,每次建立连接是一个 Accept() 对象,如果你要进行 服务器-客户端互相通讯,应使用同一个 Accept() 对象。每个 Accept 对象都是 从客户端请求建立开始的,期间只要使用同一个 Accept 对象,都可以进行数据传输。

3.6 Receive() 与 Send()

  • Receive() :接收信息
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = socket.Receive(recvBytes, recvBytes.Length, 0);//从客户端接受信息
recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes);
  • Send() :发送信息
string str = "hello";
byte[] a = Encoding.UTF8.GetBytes(str);
send = socket.Send(a, 0);

3.7 ReceiveFrom()与SendTo()

  • ReceiveFrom()
特性ReceiveReceiveFrom
连接要求需要已建立连接 (面向连接)无需预先连接 (无连接)
适用协议TCP UDP
来源获取无法获取发送方信息可获取发送方的端点信息
方法签名int Receive(byte[] buffer)int ReceiveFrom(byte[] buffer, ref EndPoint remoteEP)
典型用途可靠数据流传输数据报接收,特别是需要回复的场景
  • SendTo()
特性SendSendTo
连接要求需要已建立连接 (面向连接)无需预先连接 (无连接)
适用协议TCPUDP
目标指定在Connect时指定目标每次发送时指定目标
性能略高 (连接已建立)略低 (每次需解析地址)
可靠性高 (TCP保证送达和顺序)低 (UDP不保证送达和顺序)
方法签名Send(byte[] buffer)SendTo(byte[] buffer, EndPoint remoteEP)

3.8 Shutdown()和Close()

释放资源,有 Accept 释放和 Socket 的释放。

  • Shutdown()
    1. 向对方发送FIN包,启动TCP的正常关闭握手流程
    2. 非破坏性操作,Socket对象仍然有效
    3. 允许完成正在进行的数据传输
    4. 遵循TCP协议的四次挥手过程
    5. 对方应用会收到EOF(Receive返回0)
public void Shutdown (System.Net.Sockets.SocketShutdown how);

SocketShutdown:

描述
Send禁止对此发送Socket
Receive禁用对此接收Socket
Both禁用发送和接收对此Socket
  • Close()
    1. 破坏性操作,Socket对象不再可用
    2. 可能丢失发送/接收缓冲区中的数据
    3. 对方可能收到ConnectionReset错误
    4. 立即释放系统资源
  • 最佳实践
void SafeClose(Socket socket)
{
    try
    {
        // 1. 先优雅关闭通信通道
        if(socket.Connected)
        {
            socket.Shutdown(SocketShutdown.Both);
        }
        
        // 2. 设置linger选项确保数据发送
        socket.LingerState = new LingerOption(true, 5); // 等待5秒
        
        // 3. 最终释放资源
        socket.Close();
    }
    catch(Exception ex)
    {
        // 记录日志
        Debug.WriteLine($"关闭Socket异常: {ex.Message}");
        
        // 确保资源释放
        socket?.Close(); 
    }
}

4、IPAddress 和 IPEndPoint

4.1 IPAddress

用来处理IP地址、转换IP地址
IPAddress.Parse() 方法可以把以小数点隔分的十进制 IP 表示转化成 IPAddress 类。

IPAddress ip = IPAddress.Parse("127.0.0.1");//把ip地址字符串转换为IPAddress类型的实例

IPAddress提供4个只读字段:

  • Any 用于代表本地系统可用的任何IP地址
  • Broadcase用于代表本地网络的IP广播地址
  • Loopback用于代表系统的回送地址
  • None用于代表系统上没有网络接口

4.2 IPEndPoint

表示IPAddress对象与端口的绑定

IPAddress ip = IPAddress.Any;              //把ip地址字符串转换为IPAddress类型的实例
IPEndPoint ipe = new IPEndPoint(ip, 8000);//用指定的端口和ip初始化IPEndPoint类的新实例
2025-03-18 10:37:46,425 WARN (Thread-55|96) [InternalSchemaInitializer.run():75] Statistics storage initiated failed, will try again later org.apache.doris.common.DdlException: errCode = 2, detailMessage = System has no available disk capacity or no available BE nodes at org.apache.doris.system.SystemInfoService.checkAvailableCapacity(SystemInfoService.java:785) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.datasource.InternalCatalog.createTable(InternalCatalog.java:1132) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.InternalSchemaInitializer.createTbl(InternalSchemaInitializer.java:128) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.InternalSchemaInitializer.run(InternalSchemaInitializer.java:73) ~[doris-fe.jar:1.2-SNAPSHOT] 2025-03-18 10:37:51,425 INFO (Thread-55|96) [InternalCatalog.createDb():430] create database[default_cluster:__internal_schema] which already exists 2025-03-18 10:37:51,429 WARN (Thread-55|96) [InternalSchemaInitializer.run():75] Statistics storage initiated failed, will try again later org.apache.doris.common.DdlException: errCode = 2, detailMessage = System has no available disk capacity or no available BE nodes at org.apache.doris.system.SystemInfoService.checkAvailableCapacity(SystemInfoService.java:785) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.datasource.InternalCatalog.createTable(InternalCatalog.java:1132) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.InternalSchemaInitializer.createTbl(InternalSchemaInitializer.java:128) ~[doris-fe.jar:1.2-SNAPSHOT] at org.apache.doris.catalog.InternalSchemaInitializer.run(InternalSchemaInitializer.java:73) ~[doris-fe.jar:1.2-SNAPSHOT] 什么原因怎么解决
03-19
<think>好的,我现在需要解决用户提出的Doris数据库中的DdlException错误,错误码是2,提示“系统没有可用的磁盘容量或无可用BE节点”。首先,我得理解这个错误的原因。根据用户提供的引用内容,特别是引用[5]中的BE配置部分,可能涉及存储路径的设置和BE节点的可用性。 首先,磁盘容量不足可能意味着BE节点的存储路径空间不足。用户需要检查每个BE节点的磁盘使用情况,确认storage_root_path配置的目录是否有足够的空间。如果磁盘满了,可能需要清理数据或者扩容磁盘。 其次,没有可用的BE节点可能包括几种情况:BE节点未启动、网络问题导致FE无法连接BE、或者BE的配置不正确,比如priority_networks设置与FE不在同一网段。根据引用[5],BE的priority_networks需要和FE一致,否则可能导致节点不可达。同时,检查BE的日志,看是否有启动错误或者端口被占用的情况。 另外,引用[3]提到SELinux需要关闭,如果处于Enforcing模式可能会限制BE的访问权限,导致存储初始化失败。用户需要确认SELinux的状态,并设置为Disabled后重启系统。 还有,用户提到的错误信息可能和存储模型有关,引用[2]指出Doris使用列式存储,如果存储路径配置错误或权限不足,也可能导致初始化失败。需要检查存储目录的权限是否正确,确保BE进程有读写权限。 最后,可能需要进行集群状态检查,使用Doris提供的命令查看BE节点的状态是否正常,如SHOW BACKENDS命令,确认节点是否存活、磁盘容量是否正常。如果发现异常节点,需要重启BE服务或重新注册节点。 总结下来,解决方案应该包括检查磁盘空间、验证BE配置、关闭SELinux、检查网络和节点状态,以及调整存储路径权限等步骤。用户需要逐一排查这些方面,确保每个环节都符合要求,从而解决DdlException错误。</think>### Doris DdlException错误解决方案 #### 1. 检查BE节点磁盘容量 - **查看存储路径空间**:检查每个BE节点配置的存储目录(`storage_root_path`)是否已满。通过命令`df -h`确认磁盘使用率,若超过阈值(如90%),需清理数据或扩容存储。 - **调整存储策略**:若使用多盘存储,可增加新磁盘并修改`storage_root_path`配置,例如: ```plaintext storage_root_path=/data1,/data2;... ``` 修改后需重启BE节点使配置生效[^5]。 #### 2. 验证BE节点可用性 - **检查BE运行状态**: ```sql SHOW BACKENDS; ``` 查看`Alive`字段是否为`true`,若为`false`,需检查BE进程是否启动、日志是否有报错(如端口冲突、资源不足)。 - **网络连通性**:确保FE与BE的`priority_networks`配置在同一网段(如`10.10.1.0/24`),避免因网络隔离导致节点不可达。 #### 3. 关闭SELinux限制 - **修改SELinux状态**:编辑`/etc/selinux/config`,设置`SELINUX=disabled`,重启服务器生效。避免因权限限制导致存储初始化失败[^3]。 #### 4. 检查存储路径权限 - **目录权限设置**:确保BE进程对`storage_root_path`目录有读写权限,例如: ```bash chown -R doris_user:doris_group /data/doris/storage ``` #### 5. 其他可能原因 - **BE节点未注册**:新加入的BE节点需通过`ALTER SYSTEM ADD BACKEND`命令注册到集群。 - **元数据损坏**:若上述步骤无效,尝试备份数据后重建集群或联系社区支持。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值