使用bind
函数绑定ip和端口时有如下几种可能:
ip地址 | 端口 | 结果 |
---|---|---|
通配地址 | 0 | 内核选择ip地址和端口 |
通配地址 | 非0 | 内核选择ip地址,进程指定端口 |
本地ip地址 | 0 | 进程指定ip地址,内核选择端口 |
本地ip地址 | 非0 | 进程指定ip地址和端口 |
使用规则:
- 服务器进程一般在启动时都会
bind
某个端口(例如 http的80端口),而客户端进程都不会指定端口。若未调用bind
接口,则内核会为其指定一个临时端口。
服务端进程也有不指定端口的case,比如rpc服务。服务端进程在
listen
后向rpc服务注册进程
注册自己的地址和端口。rpc客户端只需要向rpc服务注册进程
请求对应的服务,即可返回rpc服务的ip和port信息。
- 若指定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.1
,192.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
- 启动绑定在通配ip和1234端口的服务端程序
- 使用
telnet 127.0.0.1 1234
命令连接服务端 - 服务端显示
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
- 使用
telnet 192.168.0.102 1234
连接服务端 - 服务端显示
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
- 反注释以下代码,启动服务端程序
// char *host = "127.0.0.1";
// inet_pton(AF_INET, host, &server_addr.sin_addr);
- 使用
telnet 127.0.0.1 1234
命令连接服务端 - 服务端显示
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
- 使用
telnet 192.168.0.102 1234
连接服务端 - 客户端显示
telnet: connect to address 192.168.0.102: Connection refused
telnet: Unable to connect to remote host
注:telnet 操作相当于connect指定了服务端的ip和port