Skip to content

connect、listen和accept

connectlistenaccept 是 Linux 网络编程中用于 TCP 通信的三个关键系统调用,它们的作用和使用场景各不相同。以下是它们的区别、异同以及为什么 connect 不能替代 listenaccept 的详细解释。


1. connectlistenaccept 的区别

函数 作用 使用场景 调用者
connect 客户端主动连接到服务端。 客户端 客户端
listen 服务端将套接字设置为监听状态,等待客户端的连接请求。 服务端 服务端
accept 服务端从监听套接字的连接队列中取出一个已完成的连接请求,并创建新套接字。 服务端 服务端

2. connectlistenaccept 的异同

2.1 相同点

  • 都是用于 TCP 网络通信的系统调用。
  • 都需要一个已创建的套接字(通过 socket 创建)。
  • 都涉及客户端和服务端之间的连接。

2.2 不同点

特性 connect listen accept
调用者 客户端 服务端 服务端
作用 主动连接到服务端 设置套接字为监听状态 接受客户端的连接请求
参数 服务端地址和端口 连接队列的最大长度 客户端地址信息(可选)
返回值 成功返回 0,失败返回 -1 成功返回 0,失败返回 -1 成功返回新套接字,失败返回 -1
阻塞行为 阻塞直到连接成功或失败 非阻塞,仅设置监听状态 阻塞直到有连接请求到达
使用顺序 客户端在 socket 后调用 服务端在 bind 后调用 服务端在 listen 后调用

3. connect 能否替代 listenaccept

不能,原因如下:

3.1 功能不同

  • connect 是客户端主动连接到服务端。
  • listenaccept 是服务端被动接受客户端的连接请求。

3.2 角色不同

  • connect 是客户端的行为。
  • listenaccept 是服务端的行为。

3.3 通信方向不同

  • connect 是单向的(客户端 → 服务端)。
  • listenaccept 是双向的(服务端 ← 客户端)。

3.4 实现机制不同

  • connect 直接发起 TCP 三次握手。
  • listen 设置套接字为监听状态,并创建连接队列。
  • accept 从连接队列中取出已完成的连接请求,并创建新的套接字。

4. 为什么需要 listenaccept

4.1 listen 的作用

  • 将套接字设置为监听状态,使其能够接受客户端的连接请求。
  • 创建连接队列,用于存放未处理的连接请求。

4.2 accept 的作用

  • 从连接队列中取出一个已完成的连接请求。
  • 创建一个新的套接字,用于与客户端进行数据通信。

4.3 如果没有 listenaccept

  • 服务端无法接受客户端的连接请求。
  • 客户端即使调用 connect,也无法成功连接到服务端。

5. 示例对比

5.1 客户端使用 connect

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8080);

connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

5.2 服务端使用 listenaccept

int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(8080);

bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
listen(sockfd, 10);

struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int connfd = accept(sockfd, (struct sockaddr *)&client_addr, &client_len);

6. 总结

  • connectlistenaccept 是 TCP 网络编程中不可或缺的三个系统调用,分别用于客户端连接、服务端监听和接受连接。
  • connect 不能替代 listenaccept,因为它们的功能、角色和使用场景完全不同。
  • 服务端必须使用 listenaccept 来接受客户端的连接请求,而客户端必须使用 connect 来主动连接到服务端。

什么是已连接的socket

在Linux网络编程中,已连接的套接字是指已经成功建立了通信链路(如TCP连接)的套接字。这种套接字可以用于数据的发送和接收。以下是判断套接字是否已连接的几种情况:


1. TCP套接字

  • 客户端
    • 当客户端调用 connect() 成功连接到服务器后,客户端的套接字就变成了已连接的套接字
    • 此时,客户端可以使用 send()recv() 与服务器通信。
  • 服务器
    • 服务器调用 accept() 接受客户端的连接请求后,会返回一个新的套接字描述符。
    • 这个新的套接字是已连接的套接字,服务器可以通过它与客户端通信。

总结: - 客户端的套接字在 connect() 成功后变为已连接的套接字。 - 服务器的套接字在 accept() 返回后,新的套接字是已连接的套接字。


2. UDP套接字

  • UDP 是无连接的协议,因此严格来说,UDP 套接字没有“已连接”的概念。
  • 但是,UDP 套接字可以通过 connect() 绑定一个默认的对端地址。
    • 调用 connect() 后,UDP 套接字可以使用 send()recv(),而不需要每次指定对端地址。
    • 这种套接字可以称为“已连接的UDP套接字”,但它并不建立真正的连接,只是绑定了默认的对端地址。

3. 已连接套接字的特征

  • 可以使用 send()recv() 进行数据传输。
  • 对于 TCP 套接字,连接是双向的,双方都可以发送和接收数据。
  • 对于 UDP 套接字,connect() 只是绑定了默认的对端地址,数据传输仍然是单向的。

4. 如何判断套接字是否已连接

  • 对于 TCP 套接字:
    • 客户端:调用 connect() 成功后,套接字已连接。
    • 服务器:调用 accept() 返回的新套接字是已连接的。
  • 对于 UDP 套接字:
    • 调用 connect() 后,套接字绑定了默认对端地址,可以视为“已连接”。
  • 使用 getsockopt() 检查套接字状态:
    int is_connected;
    socklen_t len = sizeof(is_connected);
    getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &is_connected, &len);
    if (is_connected == 0) {
        // 套接字已连接
    } else {
        // 套接字未连接
    }
    

5. 已连接套接字的使用场景

  • TCP
    • 客户端和服务器通过已连接的套接字进行双向通信。
  • UDP
    • 已连接的UDP套接字可以简化通信,避免每次调用 sendto()recvfrom() 时指定地址。

6. 未连接的套接字

  • 对于 TCP 套接字:
    • 客户端未调用 connect(),或者 connect() 失败。
    • 服务器未调用 accept() 接受连接。
  • 对于 UDP 套接字:
    • 未调用 connect() 绑定默认对端地址。

总结

  • 已连接的套接字是指已经建立通信链路(TCP)或绑定了默认对端地址(UDP)的套接字。
  • TCP 套接字通过 connect()accept() 变为已连接。
  • UDP 套接字通过 connect() 绑定默认对端地址后,可以视为“已连接”。
  • 已连接的套接字可以使用 send()recv() 进行数据传输。