在网络通信程序中,通过ip:port 来确定网络中的唯一一台主机上面的运行的进程,
对于客户端而言,通过向指定主机上面的指定端口来发送信息来请求对应的服务。
服务器在网络编程中指代运行服务器程序的进程,这个是从狭义上来说的,
从广义上来说也可以是运行服务器进程(程序)的主机。
前者是通过端口号(port)来指定的,而后者是通过IP地址来确定的。
那么,socket 套接字与运行服务器进程的端口号之间有什么联系呢?
这个问题的答案便是 bind 存在的作用
我的理解是这样的:
服务器程序运行主要是为了接收来自各个端点的客户端的请求,然后根据请求给予不同的回复。
对于服务器程序来说,接收信息也好(需要调用的方法的参数、方法签名),
回复结果也罢(进程调用具体的函数返回值),都需要一定的缓冲区来存放这些信息和数据。
这个缓冲区则是 socket 函数的返回值所指定的系统所分配的空间的数组下标
具体请看
http://blog.chinaunix.net/uid-28595538-id-4918416.html 的讲解。
而IP:port 则是标定程序运行的主机与进程号码
那么通过函数 bind 将 运行服务器程序的主机:进程号 与缓冲区进行绑定的含义便是将通过 socket
函数向系统申请的系统空间与运行在当前系统上面的程序进行绑定。
也就是为该进程分配用来存放网络通信所需要的缓冲空间。
将socket 返回的数值理解为空间可以这样来想:
socket 返回的数值与系统中的文件描述符 fd 是等价的, fd 指向的是系统为文件在系统中开辟的空间,
所以通过 socket 返回的 socket_fd 也对应着系统为其开辟的一块缓冲区。
只不过,文件描述符的 fd 可以通过某种方式写入到文件中,在用户确认写入之后,将会把缓冲区中的
数据写入到系统的磁盘中作为永久保存。而 socket 对应的 sock_fd 仅仅是一块缓冲区,当服务器与客户端
之间的通信结束之后,该缓冲区通过 close (sock_fd) ; 方法调用之后将会被系统所回收,没有写到磁盘上的机会。
下面来看一下 bind 函数的描述
int bind ( int sockfd , struct sockaddr *my_addr , socklen_t addrlen ) ;
参数:
sockfd : 该参数便是 socket(...) 函数的返回值
my_addr : 套接字描述字 要绑定的目标主机上的目标进程描述变量
addrlen : 参数 2 的长度,字节为单位
返回值:
如果将 socket 方法返回的 sock_fd 成功的与IP:port地址 绑定,则会返回 0
否则返回非零负数
在这里稍微总结一下,对于 C/S 通信模型中
client 端使用的函数顺序通常是 : socket -> connect -> send/recv
socket 端使用的函数顺序通常是 : socket -> bind -> listen -> send/recv
代码实例
下面的代码用来展示,如何使用bind 来绑定目标主机进程和套接字描述符
代码编译命令
g++ bindTest.cpp -o bindTest
-
// bindTest.cpp
-
-
#include <stdio.h>
-
#include <string.h> // memset
-
-
#include <sys/types.h> // AF_INET , AF_INET6...
-
#include <sys/socket.h> // socket
-
#include <netinet/in.h>
-
#include <unistd.h> // close
-
-
int getSocketDone( )
-
{
-
int ret = socket ( AF_INET , SOCK_STREAM, 0 ) ;
-
if ( ret < 0 )
-
{
-
perror ("failed to create sock_fd in getSocketDone method") ;
-
return ret ;
-
}
-
return ret ;
-
}
-
-
int getBindDone ( int sock_fd )
-
{
-
int ret ;
-
struct sockaddr_in serv_addr ;
-
-
// initialize serv_addr
-
memset ( &serv_addr, 0 , sizeof( struct sockaddr_in )) ;
-
serv_addr.sin_family = AF_INET ;
-
serv_addr.sin_port = htons (3699) ;
-
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY) ;
-
/**
-
在这里值得说明一点:
-
-
将 struct sockaddr_in 变量 serv_addr.sin_addr.s_addr
-
通过 htonl(INADDR_ANY) 来指定是为了避免让某个确定的 IP 地址
-
与套接字描述符进行绑定,而是本主机的网络地址与套接字描述符进行绑定
-
1 是因为多宿主主机,该主机有多个网卡,每个网卡对应不同IP
-
2 是因为有些主机在网络中的 IP 地址是动态随机分配的
-
(比如,此次开机分配的IP 为 168.0.2.5 与该IP进行绑定
-
下次开机分配的 IP 为 167.5.2.5 ,代码仍旧与 上一个 IP 绑定,无法正确接收客户端消息)
-
-
无论是上述情况中的哪个,都会引起当前主机IP与绑定的 IP 地址无法对应的情况,
-
从而引发消息收发错误
-
-
使用 INADDR_ANY 表示当前的主机将会处理来自所有网络接口上(多个IP)相应端口
-
的连接请求,即当前主机不同IP,相同port 上面来的所有连接请求
-
*/
-
-
// finish initializing ,begin bind
-
if ( (ret = bind ( sock_fd , (struct sockaddr*)(&serv_addr) , sizeof(struct sockaddr_in)) )< 0 )
-
{
-
perror ("failed to bind ") ;
-
return ret ;
-
}
-
return ret ;
-
-
}
-
-
-
int main ( int c , char ** v )
-
{
-
int ret ;
-
-
ret = getSocketDone () ;
-
if ( ret < 0 )
-
{
-
perror ("failed getSocketDone") ;
-
return -1 ;
-
}
-
-
ret = getBindDone (ret) ;
-
if ( ret < 0 )
-
{
-
perror ("failed getBindDone") ;
-
return -1 ;
-
}
-
-
return 0 ;
-
}
运行结果没有任何错误提示信息,说明方法正常运行
阅读(1347) | 评论(0) | 转发(0) |