Bootstrap

linux网络编程中bind函数和accept函数的作用以及它们的第一次参数描述符的联系以及返回值的区别

1. bind 函数与描述符

   bind 函数用于将一个套接字(socket)与特定的地址和端口绑定,这样客户端就可以通过该地址和端口找到并连接到服务器。其函数原型如下:

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

  • 参数说明
    • sockfd:是一个套接字描述符,它是通过 socket 函数创建的。socket 函数会返回一个非负整数,作为这个新创建套接字的标识。
    • addr:指向一个 struct sockaddr 类型的结构体,该结构体包含了要绑定的地址和端口信息。
    • addrlen:表示 addr 所指向结构体的长度。

2. accept 函数与描述符

accept 函数用于从监听队列中取出一个客户端的连接请求,并创建一个新的套接字来与该客户端进行通信。其函数原型如下:

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

  • 参数说明
    • sockfd:同样是一个套接字描述符,它是用于监听客户端连接请求的套接字描述符。
    • addr:指向一个 struct sockaddr 类型的结构体,用于存储客户端的地址信息。
    • addrlen:指向一个 socklen_t 类型的变量,传入时表示 addr 所指向结构体的长度,返回时表示实际存储的客户端地址信息的长度。

3. 二者描述符相同的原因

在服务器端的网络编程流程中,一般会按以下步骤进行操作:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define PORT 8888
#define BACKLOG 5

int main() {
    int listen_fd, conn_fd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len;

    // 创建套接字
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1) {
        perror("socket creation failed");
        return -1;
    }

    // 准备服务器地址信息
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(PORT);

    // 绑定套接字到指定地址和端口
    if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind failed");
        close(listen_fd);
        return -1;
    }

    // 开始监听客户端连接请求
    if (listen(listen_fd, BACKLOG) == -1) {
        perror("listen failed");
        close(listen_fd);
        return -1;
    }

    // 接受客户端连接
    client_addr_len = sizeof(client_addr);
    conn_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (conn_fd == -1) {
        perror("accept failed");
        close(listen_fd);
        return -1;
    }

    // 后续可以使用 conn_fd 与客户端进行通信
    // ...

    // 关闭套接字
    close(conn_fd);
    close(listen_fd);
    return 0;
}

  • 首先,使用 socket 函数创建一个套接字,返回一个套接字描述符 listen_fd
  • 然后,使用 bind 函数将这个套接字描述符 listen_fd 与服务器的地址和端口绑定,这样服务器就可以在该地址和端口上监听客户端的连接请求。
  • 接着,使用 listen 函数将该套接字设置为监听状态,开始监听客户端的连接请求。
  • 最后,使用 accept 函数从监听队列中取出一个客户端的连接请求。这里 accept 函数的第一个参数仍然是 listen_fd,因为它需要知道从哪个监听套接字的队列中取出连接请求。当 accept 函数成功返回时,会创建一个新的套接字描述符 conn_fd,用于与该客户端进行实际的数据通信。

  bind 函数的第一个参数描述符和 accept 函数的第一个参数描述符通常是一样的,它们都指向用于监听客户端连接请求的套接字。而 accept 函数返回的新套接字描述符则用于与具体的客户端进行数据交互。

  这俩个函数返回值的区别

bind函数和accept函数返回值的区别主要体现在含义、取值及错误处理等方面,以下是具体介绍:

返回值含义

  • bind函数:用于将套接字与特定的网络地址和端口号绑定。其返回值表示绑定操作是否成功。
  • accept函数:用于从服务器的连接请求队列中接受一个客户端连接,并创建一个新的套接字来与客户端进行通信。其返回值表示接受连接操作的结果,若成功则返回一个新的套接字描述符,用于与该客户端进行后续的数据交互。

取值情况

  • bind函数:成功时返回 0,表示套接字与指定地址和端口绑定成功,服务器可以开始在该地址和端口上监听连接请求。失败时返回 - 1,同时会设置errno变量来表示具体的错误原因,如EACCES(权限不足,无法绑定到指定端口)、EADDRINUSE(地址已被使用)等。
  • accept函数:成功时返回一个非负整数,即新的套接字描述符,这个新的描述符用于与接受的客户端进行通信,后续可以使用这个描述符进行readwrite等操作来与客户端交换数据。失败时返回 - 1,同时errno会被设置为相应的错误码,如EAGAINEWOULDBLOCK(在非阻塞模式下,没有可用的连接且没有设置超时)、ECONNABORTED(连接被对方中止)等。

错误处理影响

  • bind函数:如果bind失败,通常意味着服务器无法在指定的地址和端口上进行监听,后续的listenaccept操作也就无法正常进行,服务器需要根据错误原因进行相应的处理,如更换端口、检查权限等,然后重新尝试bind操作。
  • accept函数accept失败时,可能只是当前没有可用的连接请求,或者连接出现了一些问题。服务器可以根据错误类型决定是继续等待连接(例如在非阻塞模式下EAGAIN错误时),还是进行一些错误处理和清理操作,而不会影响服务器继续监听新的连接请求。
;