sokcet常用配置

非阻塞模式

套接字有两种工作模式:

  • 阻塞模式: 阻塞调用是指调用结果返回之前,当前线程会被挂起。该进程被标记为睡眠状态并被调度出去。函数只有在得到结果之后才会返回。当socket工作在阻塞模式的时候, 如果没有数据的情况下调用该函数,则当前线程就会被挂起,直到有数据为止。
  • 非阻塞模式: 非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。epoll工作在非阻塞模式时,才会发挥作用。

正常情况下,socket工作在阻塞模式下,在调用accept,connect,read,write等函数时,都是阻塞方式,直到读到数据才会返回。但是,如果将socket设置为非阻塞状态,那么这么些函数就会立即返回,不会阻塞当前线程。
设置非阻塞socket的方法是:

int flags = fcntl(app_socket->fd, F_GETFL, 0);
flags |= O_NONBLOCK;
flags |= O_NDELAY;
fcntl(app_socket->fd, F_SETFL, flags);

建立连接

客户端调用 connect() 发起对服务端的 socket 连接,
如果客户端的 socket 描述符为阻塞模式,则 connect() 会阻塞到连接建立成功或连接建立超时
(Linux 内核中对 connect 的超时时间限制是 75s, Soliris 9 是几分钟,因此通常认为是 75s 到几分钟不等)。

如果为非阻塞模式,则调用connect()后函数立即返回,如果连接不能马上建立成功(返回 -1),则 errno 设置为 EINPROGRESS,此时 TCP 三次握手仍在继续。此时可以调用 select() 检测非阻塞 connect 是否完成。select 指定的超时时间可以比 connect 的超时时间短,因此可以防止连接线程长时间阻塞在 connect 处。

处理非阻塞 connect 的步骤:

  1. 创建socket,并利用fcntl将其设置为非阻塞。
  2. 调用connect函数,如果返回0,则连接建立;如果返回-1,检查errno ,如果值为 EINPROGRESS,则连接正在建立。
  3. 为了控制连接建立时间,将该socket描述符加入到select的可写集合中,采用select函数设定超时。
  4. 如果规定时间内成功建立,则描述符变为可写;否则,采用getsockopt函数捕获错误信息。当errno == 0表示只可写。

源自Berkeley的实现(和Posix.1g)有两条与select和非阻塞IO相关的规则:
A: 当连接建立成功时,套接字描述符变成可写;
B: 当连接出错时,套接字描述符变成既可读又可写;

所以对于 非阻塞模式connect() 的判断逻辑如下:

 1. connect()返回值为0,连接成功。
 2. connect()返回值不为0,且 errno 的值也不为EINPROGRESS,表示连接失败。
 3. connect()返回值不为0,但是 errno 为EINPROGRESS,则使用 select 进行下一步判断。
 	3.1 如果 select() 返回 0,表示在 select() 超时时间内未能成功建立连接。
 	3.2 如果 select() 小于 0,表示连接失败。
 	3.3 如果 select() 大于 0,表示检测到存在变化套接字描述符。
 		3.3.1 如果套接字描述符不可写,则连接失败。
 		3.3.2 如果套接字描述符变成可写,连接成功。
 		3.3.3 如果套接字描述符变成可读可写,采用getsockopt函数捕获错误信息。当errno == 0表示只可写,连接成功,否则连接失败。
 		3.3.4 [可选]可以再次进行connect()连接,如果返回值为EISCONN,则认为已经连接

断开连接

当select检测到套接字描述符可写,但是接收到数据为0时,认为是断开连接。

if(select(fd_max + 1, &read_set, NULL, NULL, &time)>0) {
	if(FD_ISSET(app_socket->fd, &read_set) ){
		recv_len = read(app_socket->fd, buff, 100);
		if (recv_len == 0){
            // Socket 断开
        }
        else{
            int ret = lwip_get_error(app_socket->fd);
            if (ret != EAGAIN && ret != EINTR){
                // Socket 断开
            }
        }
    }
}

配置

int reuseaddr = 1; // 允许重用本地地址和端口
setsockopt(app_socket->fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&reuseaddr, sizeof(reuseaddr));

int keepalive = 1;     // 开启keepalive属性
int keepidle = 60;     // 如果连接过程中20s没有数据就发送一个探测包
int keepcount = 3;     // 一共发送探测包次数,期间收到回复就不再发送下一次的探测包
int keepinterval = 10; // 探测包的时间间隔为10s
setsockopt(app_socket->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive, sizeof(keepalive));
setsockopt(app_socket->fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&keepidle, sizeof(keepidle));
setsockopt(app_socket->fd, IPPROTO_TCP, TCP_KEEPCNT, (void *)&keepcount, sizeof(keepcount));
setsockopt(app_socket->fd, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&keepinterval, sizeof(keepinterval));
<think>好的,用户现在问的是C# Socket常用API的介绍。之前他们问过Unity Mirror是否使用Socket实现,我解释了Mirror底层确实基于Socket,但进行了封装。现在用户可能想更深入理解C#中的Socket编程,以便自己实现网络功能或者优化现有项目。 首先,我需要确定用户的需求。他们可能是刚开始学习网络编程,或者在使用Unity Mirror时遇到了底层问题,想了解更基础的Socket知识。需要覆盖常用API,但不需要太深入,保持实用性。用户可能需要知道每个API的作用、使用场景以及示例代码,方便理解和应用。 接下来,我应该整理C#中Socket类的关键方法。比如Socket的构造函数,Bind、Listen、Connect、Accept这些基本方法。还有Send和Receive,这两个是数据传输的核心,需要详细说明阻塞和非阻塞模式的区别,以及缓冲区的重要性。Shutdown和Close用于释放资源,也要提到。 另外,异步API也是重点,比如BeginAccept/EndAccept,BeginConnect/EndConnect等,因为现代应用大多采用异步避免阻塞主线程。需要解释异步操作的优势和使用方法,可能还要提到async/await的简化方式。 然后,考虑用户可能的误区。比如忘记处理异常,没有正确关闭连接,或者缓冲区处理不当导致数据丢失。在介绍API时要强调这些注意事项,帮助用户避免常见错误。 示例部分,可以分TCP和UDP给出简单代码,展示服务器和客户端的流程。TCP需要监听、接受连接、收发数据;UDP则更简单,直接发送和接收数据报。代码要简洁,突出API的使用顺序和关键步骤。 最后,要总结各个API的用途,可能以表格形式呈现,方便用户快速查阅。同时提醒在实际开发中使用更高级的框架(比如Mirror)的好处,但了解底层API对调试和优化有帮助。这样既回答了问题,又引导用户合理选择工具。</think>以下是 **C# Socket 常用API** 的核心介绍,分为**基础操作**和**关键方法**两部分,附带代码示例: --- ### 一、基础对象 ```csharp using System.Net.Sockets; using System.Net; // 核心类 Socket socket = new Socket( AddressFamily.InterNetwork, // IPv4地址族 SocketType.Stream, // 流式Socket(TCP) ProtocolType.Tcp // 指定TCP协议 ); ``` --- ### 二、核心API详解 #### 1️⃣ 服务器端操作 | 方法 | 作用 | 示例代码片段 | |--------------------|-----------------------------|----------------------------------| | `Bind()` | 绑定本地IP和端口 | `socket.Bind(new IPEndPoint(IPAddress.Any, 8080));` | | `Listen()` | 启动监听(设置最大连接队列) | `socket.Listen(10);` | | `Accept()` | 阻塞接收客户端连接 | `Socket client = socket.Accept();` | | `BeginAccept()`/`EndAccept()` | 异步接收连接(非阻塞) | 见下方异步示例 | #### 2️⃣ 客户端操作 | 方法 | 作用 | |--------------------|-----------------------------| | `Connect()` | 连接远程服务器 | | `BeginConnect()`/`EndConnect()` | 异步连接 | #### 3️⃣ 数据传输 | 方法 | 返回值说明 | 注意事项 | |--------------------|------------------------------|---------------------------------| | `Send()` | 返回实际发送的字节数 | 可能分多次发送,需检查返回值 | | `Receive()` | 返回实际接收的字节数 | 需循环接收直到数据完整 | | `SendTo()`/`ReceiveFrom()` | UDP专用 | 无需建立连接 | #### 4️⃣ 关闭连接 ```csharp socket.Shutdown(SocketShutdown.Both); // 停止收发 socket.Close(); // 释放资源 ``` --- ### 三、代码示例 #### TCP 服务器基础流程 ```csharp // 创建Socket Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 绑定监听 server.Bind(new IPEndPoint(IPAddress.Any, 8080)); server.Listen(10); // 接受客户端连接(同步) Socket client = server.Accept(); // 接收数据 byte[] buffer = new byte[1024]; int received = client.Receive(buffer); string data = Encoding.UTF8.GetString(buffer, 0, received); // 发送响应 client.Send(Encoding.UTF8.GetBytes("Received: " + data)); // 关闭 client.Close(); server.Close(); ``` #### UDP 客户端示例 ```csharp Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); // 发送数据报 IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080); udpClient.SendTo(Encoding.UTF8.GetBytes("Hello UDP"), serverEP); // 接收响应 byte[] buffer = new byte[1024]; EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); int received = udpClient.ReceiveFrom(buffer, ref remoteEP); ``` --- ### 四、异步编程模式(重点) ```csharp // 异步接收连接 server.BeginAccept(asyncResult => { Socket client = server.EndAccept(asyncResult); // 处理客户端... }, null); // 异步接收数据 byte[] buffer = new byte[1024]; client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, asyncResult => { int received = client.EndReceive(asyncResult); // 处理数据... }, null); ``` --- ### 五、重要注意事项 1. **线程阻塞**:同步方法会阻塞当前线程,推荐使用异步API 2. **缓冲区管理**:需自行处理分包/粘包问题(特别是TCP) 3. **异常处理**:必须捕获`SocketException`和`ObjectDisposedException` 4. **端口复用**:设置`socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true)` --- ### 六、核心参数对照表 | SocketOption | 用途 | |---------------------|--------------------------| | `ReceiveTimeout` | 设置接收超时时间(毫秒) | | `SendBufferSize` | 发送缓冲区大小(默认8KB) | | `NoDelay` | 禁用Nagle算法(立即发送小包) | 掌握这些API后,您可以使用C#实现:TCP/UDP通信、HTTP服务器、游戏联机等网络功能。实际开发中建议结合`NetworkStream`或更高级框架(如SignalR)简化操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盗版摩羯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值