socketaddr和socketaddr_in的区别于联系

本文介绍了struct sockaddr和struct sockaddr_in在Socket编程中的区别和联系,包括它们的结构定义、用途以及如何在不同函数中进行类型转换。同时,文章提到了主机字节序与网络字节序的概念,以及如何通过htons、ntohs等函数进行字节序转换。

struct   sockaddr   {   
                unsigned   short   sa_family;   /*   地址族,   AF_xxx   */   
                char   sa_data[14];   /*   14字节的协议地址*/   
        };   
  上面是通用的socket地址,具体到Internet   socket,用下面的结构,二者可以进行类型转换   
          
  struct   sockaddr_in   {   
                short   int   sin_family;   /*   地址族,AF_xxx  在socket编程中只能是AF_INET */   
                unsigned   short   int   sin_port;   /*   端口号  (使用网络字节顺序) */   
                struct   in_addr   sin_addr;   /*   存储IP地址  4字节 */   
                unsigned   char   sin_zero[8];   /*   总共8个字节,实际上没有什么用,只是为了和struct  sockaddr保持一样的长度   */   
        };   
        struct   in_addr就是32位IP地址。   
        struct   in_addr   {   
                union {
                        struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
                        struct { u_short s_w1,s_w2; } S_un_w;
                        u_long S_addr;  /*  按照网络字节顺序存储IP地址  */
                } S_un;

                #define s_addr  S_un.S_addr
        };   
   inet_addr()是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001)。

填值的时候使用sockaddr_in结构,而作为函数(如socket, listen, bind等)的参数传入的时候转换成sockaddr结构就行了,毕竟都是16个字符长。

通常的用法是:   
  int   sockfd;   
  struct   sockaddr_in   my_addr;   
  sockfd   =   socket(AF_INET,   SOCK_STREAM,   0);   /*   做一些错误检查!   */   
    
  my_addr.sin_family   =   AF_INET;   /*   主机字节序   */   
  my_addr.sin_port   =   htons(MYPORT);   /*   short,   网络字节序   */   
  my_addr.sin_addr.s_addr   =   inet_addr("192.168.0.1");   
    
  bzero(&(my_addr.sin_zero),   8);   /*   zero   the   rest   of   the   struct   */   
  /*   不要忘了为bind()做错误检查:   */   
  bind(sockfd,   (struct   sockaddr   *)&my_addr,   sizeof(struct   sockaddr));

原文:http://topic.youkuaiyun.com/t/20020226/09/542548.html

可以用C++做个不太准确的假设。   
sockaddr是base   class     
sockaddr_in   等是derived   class   
如此一来,bind,   connect   ,   sendto   ,   recvfrom等函数就可以使用base   class   
来处理多种不同的derived   class了。   
但是实际上,这是没有继承关系数据结构(C嘛),所以需要强制造型来转换数据类型。正因为如此,在sendto的时候需要给出len长度,因为不同的sockaddr_xx实现长度并不相同。

名词解析:

主机字节序:

不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。最常见的有两种 1.Little endian:低字节存高地址,高字节存低地址 2.Big endian:低字节存低地址,高字节存高地址

网络字节序:

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。

为了进行转换bsd socket提供了转换的函数,有下面四个网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)

htons 把unsigned short类型从主机序转换到网络序,htonl 把unsigned long类型从主机序转换到网络序,ntohs 把unsigned short类型从网络序转换到主机序,ntohl 把unsigned long类型从网络序转换到主机序。

在使用little endian的系统中 这些函数会把字节序进行转换 在使用big endian类型的系统中 这些函数会定义成空宏

4.1套接字简介 (1)网络应用程序编程接口(API) 计算机用它来对网络发送或接收信息。 (2)套接字接口(socket interface) 由加州大学伯克利分校UNIX小组开发,目前最为流行。定义了网络上的各种操作(如生成套接字,发送/接收消息等。 (3)常用的套接字接口 Linux/Unix下: Berkeley Socket是最突出的一套接口。 Windows下: Win Socket ,也称winsock,与Berkeley Socket很类似的接口 可认为一个IP与一端口(port)联合在一起形成一个套接字,它是网络上的一个传输接口。在网络的另外一端可有一个对应的套接字与通信。 4.1.1 客户/服务器模式 TCP/IP网络应用中,最常用的通信模式是客户/服务器模式(Client/Server model),即客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务。 客户端与服务器的连接方式主要有两种: 流式套接口连接 流式套接口是可靠的双向通讯的数据流。传送的包会按发送时的顺序到达。 数据报套接口连接 使用这种方式,传送的包不一定会按发送时的顺序到达。当然每个包的内部是无错误的。 (1)服务器端 服务器先要端打开一个通信通道,并告知本地主机它需要在某个端口上(如FTP为21)接收客户请求; 等待客户请求到达该端口; 接收到服务请求,处理该请求并应答。直至交互完成; 返回第二步,等待另一客户请求; 关闭服务器。 (2)客户端 打开一个通信通道,连接到服务器所在主机的特定端口,此时,服务器端已经在这个Socket等待请求; 向服务器发服务请求报文,等待并接收应答; 继续提出请求并等待应答; 请求结束后关闭通信通道并终止。 从上面所描述过程可知: 客户与服务器进程的作用是非对称的,它们各自完成的功能不同,因此编码也不同。 服务进程一般是先于客户请求而启动的,启动后即在相应的Socket监听来自客户端的请求。只要系统运行,该服务进程一直存在,直到正常或强迫终止。 服务器方面初始时需要执行的操作: int socket () 建立一个Socket int bind() 与某个端口绑定 int listen() 开始监听端口 int accept() 等待/接受客户端的连接请 客户端需要执行的操作: int socket () 建立一个Socket int connect() 连接到服务器 4.1.2 使用伯克利套接字 (1)int socket (int domain, int type, int protocol) 功能:创建套接字。 返回值:返回值是新创建套接字的句柄,即以后引用该套接字时使用的标识符。错误时返回-1。 参数 domain 描述将使用的协议族。 AF_INET : 用于表示因特网协议族。 AF_UNIX : 用于表示U n i x 管道功能 参数 type表明通信的语义。 SOCK_STREAM: 字节流服务,可理解为TCP连接 SOCK_DGRAM: 面向消息的服务,可理解为TCP连接 参数 protocol 则指明将要用到的特定协议 IPPROTO_TCP: 指的是使用TCP协议 (2)int bind(int socket, struct socketaddr *address, int addr_len) 功能:将创建的 socket 与 adress (包含 IPport信息)绑定。 返回值:在错误的时候会返回-1 参数 socket 描述将使用的套接字。 参数 addr_len 描述的是参数 adress的长度。 参数 adress 描述将绑定的地址。 参数中用到的数据结构struct socketaddr:在因特网协议中地址描述使用的数据结构 struct sockaddr { unsigned short sa_family; char sa_data[14]; }; sa_family 描述将使用的协议族,一般为AF_INET sa_data 为套接口储存目标地址端口信息。 注意: 有两种字节排列顺序:重要的字节在前面,或者不重要的字节在前面。前一种叫“网络字节顺序 (Network Byte Order)”。不同机器,不同语言之间的字节存放顺序是不一样的,所以在网络上传输数据时,一定要转成网络字节顺序 。 数据必须按照 NBO 顺序,那么你要调用函数(例如 htons() )来将他从“本机字节顺序 (Host Byte Order) ”转换过来。 有以下四个主要的转换函数: htons() 将Short型数据转换为网络字节类型 htonl() 将Long型数据转换为网络字节类型 ntohs() 将Short型数据转换为本地字节类型 ntohl() 将Long型数据转换为本地字节类型 事实上,上面的struct sockaddr并不好用,因它没有明确细化内部结构。于是程序员创造了一个并列的结构sockaddr_in ,它可以与结构sockaddr互相转换。 struct sockaddr_in { short int sin_family; unsigned short int sin_port; struct in_addr sin_addr; unsigned char sin_zero[8]; } 首先,你需要将 IP 地址储存到 struct sockaddr_in ina中。 当IP地址的形式是“numbers-and-dots”时(如“32.41.5.10”),你要用的函数是 inet_addr(): ina.sin_addr.s_addr = inet_addr(“132.241.5.10”); 注意:inet_addr() 返回的地址已经是按照网络字节顺序的,你没有必要再去调用 htonl()。 当IP地址的形式已经是long时,则你需要使用htonl()函数来进行转换。 (3)int listen(int socket, int backlog) 功能:定义在指定的 Socket 上可有多少个待处理的连接。 返回值:在发生错误时返回-1。 参数socket 是调用 socket() 返回的套接口文件描述符。 参数backlog 是 在进入队列中允许的连接数目。 (4)int accept(int socket, struct socketaddr *address , int addr_len) 功能:接收客户端连接请求。 返回值:如果连接成功, 函数将返回一个新的套接口文件描述符。接下来,就可以对这个描述符进行发送 (send()) 接收 (recv()) 操作了。错误时返回-1 参数socket 套接口文件描述符。 参数address struct sockaddr_in 的指针。 参数addrlen 长度,常为 sizeof(struct sockaddr_in) (5)int connect(int socket, struct sockaddr *serv_addr, int addrlen) 功能:在客户端被用于连接到服务器。 返回值:发生错误的时候返回-1 参数socket 套接口文件描述符。 参数serv_addr 包含是服务器的地址端口信息 参数addrlen 长度,常为 sizeof(struct sockaddr_in) (6)int setsockopt(int socket,int level,int optname  ,const void *optval,socklen_t *optlen) 功能:设置套接字行为 参数level 指定控制套接字的层次。 SOL_SOCKET表示通用套接字选项. 参数optname 指定控制的方式(选项的名称)  SO_REUSERADDR表示重用本地地址端口 参数optval 指示相应的行为,如功能的开启或关闭 (7) 其它函数: int read(int filedes, char *buff, unsigned nbytes); int write(int filedes, char *buff, unsigned nbytes); 功能:分别表示对指定的文件描述符进行读定操作 返回值:读定成功时,返回一个表示读出/写入字节数的正数。 返回0表示文件尾,-1表示读/写失败。 int close(int socket); 功能:关闭对应的套接口。 struct  hostent gethostbyname(const char *hostname); 功能:据主机名查找主机的IP 。 Hostname是域名。 返回值:成功时返回一个指向结构体 hostent 的指针 或者是空 (NULL) 指针 void *memset(void *s, char ch, unsigned n); 功能:将已开辟内存空间 s 的首 n 个字节的值设为值 ch void *memcpy(void *destin, void *source, unsigned n); 从源source中拷贝n个字节到目标destinint open(char *pathname, int access[, int permiss]); 打开一个文件用于读或写 void exit(int status); 退出程序 4.2 WinSock简介 (1)概述 Windows Socket 是从 Berkeley Socket扩展而来的,其在继承 Berkeley Socket的基础上,又进行了新的扩充。这些扩充主要是提供了一些异步函数,并增加了符合Windows消息驱动特性的网络事件异步选择机制。 Windows socket的版本: Winsock1.1 Winsock2.0 (2)Windows Socket 与 Berkeley Socket的异同 Win socket保持了与Berkeley Socket的最大程度的兼容。 Berkeley Socket下的大部分的函数在Win socket均有同名的函数,而且调用也基本上是一样的,但Win socket对某些函数进行了扩展,但命名时会加前缀来区别。 windows中需要先使用WSAStartup()函数来初始化Socket才能使用,并且需要使用SACleanup( )函数来关闭。而Linux中则不需要。 (3)Window下其它的Socket CSocket (MFC) CSocket封装了使用socket时的所有细节,包括网络字节顺序,主机名解析地址族等。所以,使用它相对直接使用Winsock要简单一些。当然,涉及底层的操作时,则灵活性不一定有Winsock好。(用C++语言实现)
最新发布
11-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值