Chinaunix首页 | 论坛 | 博客
  • 博客访问: 581134
  • 博文数量: 104
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1559
  • 用 户 组: 普通用户
  • 注册时间: 2014-08-21 00:58
个人简介

锻炼精神,首先要锻炼肉体

文章分类

全部博文(104)

文章存档

2018年(1)

2016年(1)

2015年(101)

2014年(1)

我的朋友

分类: C/C++

2015-03-26 16:31:16

在网络通信程序中,通过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

点击(此处)折叠或打开

  1. // bindTest.cpp

  2. #include <stdio.h>
  3. #include <string.h> // memset

  4. #include <sys/types.h> // AF_INET , AF_INET6...
  5. #include <sys/socket.h> // socket
  6. #include <netinet/in.h>
  7. #include <unistd.h> // close

  8. int getSocketDone( )
  9. {
  10.   int ret = socket ( AF_INET , SOCK_STREAM, 0 ) ;
  11.   if ( ret < 0 )
  12.   {
  13.     perror ("failed to create sock_fd in getSocketDone method") ;
  14.     return ret ;
  15.   }
  16.   return ret ;
  17. }

  18. int getBindDone ( int sock_fd )
  19. {
  20.   int ret ;
  21.   struct sockaddr_in serv_addr ;

  22.   // initialize serv_addr
  23.    memset ( &serv_addr, 0 , sizeof( struct sockaddr_in )) ;
  24.    serv_addr.sin_family = AF_INET ;
  25.    serv_addr.sin_port = htons (3699) ;
  26.    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY) ;
  27. /**
  28. 在这里值得说明一点: 

  29. 将 struct sockaddr_in 变量 serv_addr.sin_addr.s_addr
  30. 通过 htonl(INADDR_ANY) 来指定是为了避免让某个确定的 IP 地址
  31. 与套接字描述符进行绑定,而是本主机的网络地址与套接字描述符进行绑定
  32. 1 是因为多宿主主机,该主机有多个网卡,每个网卡对应不同IP  
  33. 2 是因为有些主机在网络中的 IP 地址是动态随机分配的
  34. (比如,此次开机分配的IP 为  168.0.2.5 与该IP进行绑定
  35.  下次开机分配的 IP 为 167.5.2.5 ,代码仍旧与 上一个 IP 绑定,无法正确接收客户端消息)

  36. 无论是上述情况中的哪个,都会引起当前主机IP与绑定的 IP 地址无法对应的情况,
  37. 从而引发消息收发错误

  38. 使用 INADDR_ANY 表示当前的主机将会处理来自所有网络接口上(多个IP)相应端口
  39. 的连接请求,即当前主机不同IP,相同port 上面来的所有连接请求
  40. */
  41.   
  42.   // finish initializing ,begin bind
  43.    if ( (ret = bind ( sock_fd , (struct sockaddr*)(&serv_addr) , sizeof(struct sockaddr_in)) )< 0 )
  44.    {
  45.     perror ("failed to bind ") ;
  46.     return ret ;
  47.    }
  48.   return ret ;

  49. }


  50. int main ( int c , char ** v )
  51. {
  52.   int ret ;
  53.  
  54.   ret = getSocketDone () ;
  55.   if ( ret < 0 )
  56.   {
  57.     perror ("failed getSocketDone") ;
  58.     return -1 ;
  59.   }

  60.   ret = getBindDone (ret) ;
  61.   if ( ret < 0 )
  62.   {
  63.     perror ("failed getBindDone") ;
  64.     return -1 ;
  65.   }

  66.   return 0 ;
  67. }
运行结果没有任何错误提示信息,说明方法正常运行

阅读(1342) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~