使用bind函数绑定ip和端口时有如下几种可能:

ip地址

端口

结果

通配地址

0

内核选择ip地址和端口

通配地址

非0

内核选择ip地址,进程指定端口

本地ip地址

0

进程指定ip地址,内核选择端口

本地ip地址

非0

进程指定ip地址和端口

使用规则:

  1. 服务器进程一般在启动时都会bind某个端口(例如 http的80端口),而客户端进程都不会指定端口。若未调用bind接口,则内核会为其指定一个临时端口。

服务端进程也有不指定端口的case,比如rpc服务。服务端进程在listen后向 rpc服务注册进程 注册自己的地址和端口。rpc客户端只需要向 rpc服务注册进程 请求对应的服务,即可返回rpc服务的ip和port信息。

  1. 若指定ip地址,则该ip地址必须是属于其主机的网络接口之一(127.0.0.1,网卡eth0地址 192.168.0.102,其他网卡地址)。

对于tcp客户端来说,其指定发送ip数据报的源地址
对于tcp服务端来说,其限定了该socket只接收目的地址为该ip的connect请求。

若tcp服务端不指定ip,内核会把客户端发送的syn的目的地址作为服务端的源地址(该connect socket的源地址)
注:一个listen socket可以accept多个socket连接,accept返回的connect socket的源地址可能为127.0.0.1192.168.0.102,或者其他本机ip

下面用代码证实下:
一般,启动服务器监听的代码如下:

// 服务端代码
    int listenfd, connfd;
    struct sockaddr_in server_addr;
    listenfd = Socket(AF_INET, SOCK_STREAM, 0);

    bzero(&server_addr, sizeof(server_addr));
    // 必须是网络序
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(1234);
// 指定ip
//    char *host = "127.0.0.1";
//    inet_pton(AF_INET, host, &server_addr.sin_addr);
    Bind(listenfd, (SA*)&server_addr, sizeof(server_addr));
    Listen(listenfd, LISTENQ);

    while (1) {
        len = sizeof(cli_addr);
        connfd = Accept(listenfd, (SA*)&cli_addr, &len);
        printf("connect from %s, port:%d\n",
               inet_ntop(AF_INET, &cli_addr.sin_addr, buffer, sizeof(buffer)),
               ntohs(cli_addr.sin_port));

     struct sockaddr_in peer_addr, local_addr;
    // 获取connect fd 的本机ip和port
        int err = getsockname(connfd, (SA *)&local_addr, &len);
        
        if (err >=0 ) {
            printf("sock name %s, port:%d\n",
                   inet_ntop(AF_INET, &local_addr.sin_addr, buffer, sizeof(buffer)), ntohs(local_addr.sin_port));
        }
    // 获取客户端的本机ip和port
        err = getpeername(connfd, (SA*) &peer_addr, &len);

        if (err >=0 ) {
            printf("peer sock name %s, port:%d\n",
                   inet_ntop(AF_INET, &peer_addr.sin_addr, buffer, sizeof(buffer)), ntohs(peer_addr.sin_port));
        }
     }

服务端程序绑定在通配ip

  1. 启动绑定在通配ip和1234端口的服务端程序
  2. 使用 telnet 127.0.0.1 1234命令连接服务端
  3. 服务端显示

connect from 127.0.0.1, port:58271
sock name 127.0.0.1, port:1234

peer sock name 127.0.0.1, port:58271

  1. 使用 telnet 192.168.0.102 1234 连接服务端
  2. 服务端显示

connect from 192.168.0.102, port:58282
sock name 192.168.0.102, port:1234

peer sock name 192.168.0.102, port:58282

服务端程序绑定在 127.0.0.1

  1. 反注释以下代码,启动服务端程序
//    char *host = "127.0.0.1";
//    inet_pton(AF_INET, host, &server_addr.sin_addr);
  1. 使用 telnet 127.0.0.1 1234命令连接服务端
  2. 服务端显示

connect from 127.0.0.1, port:58271
sock name 127.0.0.1, port:1234

peer sock name 127.0.0.1, port:58271

  1. 使用 telnet 192.168.0.102 1234 连接服务端
  2. 客户端显示

telnet: connect to address 192.168.0.102: Connection refused
telnet: Unable to connect to remote host

注:telnet 操作相当于connect指定了服务端的ip和port