套接字编程


网络字节序

  • TCP/IP协议规定,网络数据流应才使用大端字节序,即低地址高字节。

  • 为使网络程序具有可移植性,使同样的C代码在大小端计算机上编译后都能正常运行,可以调用一下库函数做网络字节序和主机字节序的转换

    •        #include <arpa/inet.h>
      
             uint32_t htonl(uint32_t hostlong);
      
             uint16_t htons(uint16_t hostshort);
      
             uint32_t ntohl(uint32_t netlong);
      
             uint16_t ntohs(uint16_t netshort);
      
    • h表示host。

    • n表示network。

    • l表示32位整数。

    • s表示16位短整数。

测试

#include <stdio.h>
#include <arpa/inet.h>

int main() {

    int a = 0x12345678;

    int b = htonl(a);

    printf("a : %#x\nb : %#x\n", a, b);

    short c = 0x1234;
    short d = htons(c);

    printf("c : %#x\nd : %#x\n", c, d);


    return 0;
}

结果

a : 0x12345678
b : 0x78563412
c : 0x1234
d : 0x3412

socket地址的数据类型

  • socket API是一层抽象的网络编程接口,适用于各种底层网络协议。
  • 如 IPV4、IPV6,以及 UNIX Domain Socket。
  • 然而,各种网络协议的地址格式并不像同,如下图所示:

       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <netinet/ip.h> /* superset of previous */

       tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
       udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
       raw_socket = socket(AF_INET, SOCK_RAW, protocol);


           struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */
               in_port_t      sin_port;   /* port in network byte order */
               struct in_addr sin_addr;   /* internet address */
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };

函数

socket()

  • socket()创建用于通信的端点,并返回引用该端点的文件描述符。
  • 成功调用返回的文件描述符将是进程当前未打开的编号最低的文件描述符。
  • 类似open()函数,返回文件描述符。
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
  • domain:通信的协议族;协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。可以选择以下:

    •    Name         Purpose                                    Man page
         AF_UNIX      Local communication                        unix(7)
         AF_LOCAL     Synonym for AF_UNIX
         AF_INET      IPv4 Internet protocols                    ip(7)
         AF_AX25      Amateur radio AX.25 protocol               ax25(4)
         AF_IPX       IPX - Novell protocols
         AF_APPLETALK AppleTalk                                  ddp(7)
         AF_X25       ITU-T X.25 / ISO-8208 protocol             x25(7)
         AF_INET6     IPv6 Internet protocols                    ipv6(7)
         AF_DECnet    DECet protocol sockets
         AF_KEY       Key  management protocol, originally de‐
                      veloped for usage with IPsec
         AF_NETLINK   Kernel user interface device               netlink(7)
         AF_PACKET    Low-level packet interface                 packet(7)
         AF_RDS       Reliable Datagram Sockets (RDS) protocol   rds(7)
                                                                 rds-rdma(7)
         AF_PPPOX     Generic PPP transport layer, for setting
                      up L2 tunnels (L2TP and PPPoE)
         AF_LLC       Logical  link  control  (IEEE 802.2 LLC)
                      protocol
         AF_IB        InfiniBand native addressing
         AF_MPLS      Multiprotocol Label Switching
         AF_CAN       Controller Area Network  automotive  bus
                      protocol
         AF_TIPC      TIPC, "cluster domain sockets" protocol
         AF_BLUETOOTH Bluetooth low-level socket protocol
         AF_ALG       Interface to kernel crypto API
         AF_VSOCK     VSOCK   (originally  "VMWare  VSockets")   vsock(7)
                      protocol for hypervisor-guest communica‐
                      tion
         AF_KCM       KCM  (kernel connection multiplexor) in‐
                      terface
         AF_XDP       XDP (express data path) interface
      
  • type:指定socket类型;有以下:

    •    SOCK_STREAM     Provides sequenced, reliable, two-way,  connection-based  byte  streams.   An
                         out-of-band data transmission mechanism may be supported.
      
         SOCK_DGRAM      Supports  datagrams  (connectionless,  unreliable messages of a fixed maximum
                         length).
      
         SOCK_SEQPACKET  Provides a sequenced, reliable, two-way  connection-based  data  transmission
                         path for datagrams of fixed maximum length; a consumer is required to read an
                         entire packet with each input system call.
      
         SOCK_RAW        Provides raw network protocol access.
      
         SOCK_RDM        Provides a reliable datagram layer that does not guarantee ordering.
      
         SOCK_PACKET     Obsolete and should not be used in new programs; see packet(7).
      
  • protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等。我们一般默认写0。

注意

并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。

bind()

  • socket() 函数用来创建套接字,确定套接字的各种属性,然后服务器端要用 bind() 函数将套接字与特定的 IP 地址和端口绑定起来,只有这样,流经该 IP 地址和端口的数据才能交给套接字处理。
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);

           struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */
               in_port_t      sin_port;   /* port in network byte order */
               struct in_addr sin_addr;   /* internet address */
           };

           struct sockaddr {
               sa_family_t sa_family;
               char        sa_data[14];
           }
  • sockfd : 用于监听的文件描述符。
  • addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。

connect

  • 参数和bind一样
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

服务端应用层流程

  • 1、分配一个监听描述符 listenFd = socket()
  • 2、绑定监听端口 bind(listenFd, 服务器端口)
  • 3、开始监听listen(listenFd, 连接队列最大长度)
  • 4、阻塞等待客户端连接 connectFd = accect(listenFd, 客户端地址端口),并返回连接成功的新的文件描述符
  • 5、阻塞等待客户端数据请求 read(connectFd, buf, size)
  • 6、处理客户端数据请求
  • 7、write(connectFd, buf, size) 发送数据应答
  • 8、read返回0,close(connectFd)关闭连接。

客户端应用层流程

  • 1、分配一个用于连接的文件描述符cennectFd = socket()
  • 2、发起连接请求 connect(connectFd, 服务器地址端口),并阻塞等待服务器应答
  • 3、发送数据请求 write(connectFd, buf, size)
  • 4、阻塞等待客户端数据应答 read(connectFd, buf, size)
  • 5、关闭连接 close(connectFd)

小细节

INADDR_ANY

转换过来就是0.0.0.0,泛指本机的意思,也就是表示本机的所有IP,因为有些机子不止一块网卡,多网卡的情况下,这个就表示所有网卡ip地址的意思。
比如一台电脑有3块网卡,分别连接三个网络,那么这台电脑就有3个ip地址了,如果某个应用程序需要监听某个端口,那他要监听哪个网卡地址的端口呢?

如果绑定某个具体的ip地址,你只能监听你所设置的ip地址所在的网卡的端口,其它两块网卡无法监听端口,如果我需要三个网卡都监听,那就需要绑定3个ip,也就等于需要管理3个套接字进行数据交换,这样岂不是很繁琐?

所以出现INADDR_ANY,你只需绑定INADDR_ANY,管理一个套接字就行,不管数据是从哪个网卡过来的,只要是绑定的端口号过来的数据,都可以接收到。

文章作者: Axieyun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Axieyun !
评论
评论
  目录