乐趣区

关于c:Socket编程基础知识

罕用的数据结构及函数

地址转换类

主机字节序与网络字节序的转换

主机序通常为小端,网络字节序为大端。下列函数在 <netinet/in.h> 包中:

  1. usinged long int htonl(unsigned long int hostlong) —— 主机序转网络序(host to network long)
  2. usinged short int htons(unsigned short int hostlong) —— 主机序转网络序(host to network short)
  3. usinged long int ntohl(unsigned long int hostlong) —— 网络序转主机序(network to host long)
  4. usinged short int ntohl(unsigned short int hostlong) —— 网络序转主机序(network to host short)

IP 地址转换函数

计算机可能辨认二进制的 IP 地址,但生存中咱们罕用点分十进制来示意 IPv4 地址,用十六进制示意 IPv6 地址,于是须要一些函数对他们进行一些转换,这些函数位于 <arpa/inet.h> 包中。

  1. in_addr_t inet_addr(const char* strprt) —— 将点分十进制 IPv4 地址转化成网络字节序的地址
  2. int inet_aton(const char cp, struct in_addr inp) —— 同上,将后果存在指针里
  3. char* inet_ntoa(struct in_addr in) —— 将二进制后果转为点分十进制
  4. int inet_pton(int af, const char src, void dst) —— 将十六进制地址转为二进制地址,并存在指针。其中 af 示意地址族,能够是 AF_NET 或 AF_NET6。
  5. const char inet_ntop(int af, const void src, char* dst, socklen_t cnt) —— 将二进制地址转换为十六进制地址,其中 cnt 指定指标存储单元大小

socket 地址构造

通用 socket 地址——sockaddr

#include <bits/socket.h>
struct sockaddr {
    sa_family_t sa_family;
    char sa_data[14];
}

其中,sa_family 是地址族,包含:PF_UNIX(UNIX 本地区协定族)、PF_INET(TCP/IPv4 地址协定族)、PF_INET6(IPv6 地址协定族)。留神:AF_xxx 和 PF_xxx 在 socket.h 中值雷同,因而常混用。
sa_data 用于寄存 socket 地址值,不同协定的地址具备不同含意和长度。PF_UNIX 寄存的是文件的路径名(疑难:在 PF_UNIX 中,socket 是以一个什么样的模式存在的?),长度可达到 108 字节;在 PF_INET 中,寄存 16bit 的端口号和 32bit 的 IPv4 地址;在 AF_INET6 中,寄存的是 128bit 的 IPv6 地址、16bit 端口号以及 32bit 的 ID。
因为 14 字节显然无奈容下所有地址,因而 linux 中 sockaddr 定义为:

#include <bits/socket.h>
struct sockaddr {
    sa_family_t sa_family;
    unsigned long int __ss_align;
    char __ss_padding[128-sizeof(__ss_align)];
}

linux 下各协定的 socket 地址

  • PF_UNIX 的 socket 地址构造:
#include <sys/un.h>
struct sockaddr_un {
    sa_family_t sin_family;
    char sun_path[108];     // 文件路径名
}
  • PF_INET 的 socket 地址构造;
struct sockaddr_in {
    sa_family_t sin_family;
    u_int16_t sin_port;     // 端口号
    struct in_addr sin_addr;    //IPv4 构造体
}

struct in_addr {u_int32_t s_addr;}
  • PF_INET6 的 socket 地址构造;
struct sockaddr_in6 {
    sa_family_t sin_family;
    u_int16_t sin_port;     // 端口号
    u_int32_t sin6_flowinfo;    // 流信息,应设置为 0
    struct in6_addr sin6_addr;    //IPv4 构造体
    u_in32_t sin6_scope_id;
}

struct in_addr {unsigned char sa_addr[16];
}

留神:所有专用 socket 地址在理论应用时都须要强制转换为通用 sockaddr!

socket 通信罕用根底函数

创立 socket

int socket(int domain, int type, int protocol);
其中 domain 为底层协定族,即 PF_UNIX, AF_INET, AF_NET6;
type 为服务类型,次要分为 SOCK_STREAM(流服务)和 SOCK_UGRAM(数据报服务),TCP 面向流,UDP 面向数据报。
protocol 示意在前两个参数形成的汇合下,再抉择一个具体的协定。但因为这个汇合通常只有一个值,个别状况下 protocol 都设置为 0。
该函数设置胜利时会返回一个 socket 文件描述符,失败时会返回 - 1 并设置 errno
留神:若设置失败,能够应用 perror()来打印具体的错误信息

socket 命名(绑定)—— 仅服务端应用

int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
bind 作用是将由 socket()创立后的未经调配的文件描述符与一个 ip 地址绑定。留神:下面提到的须要强制地址转换就是在这里进行。addrlen 间接用 sizeof 即可。

监听 socket —— 仅服务端应用

int listen(int sockfd, int backlog)
此处的 backlog 指所有处于齐全连贯和半连贯状态的客户端 socket 下限。最多可有 backlog+ 1 个 socket 申请连贯。

接管连贯 —— 仅服务端应用

int accept(int sockfd, struct sockaddr addr, socklen_t addrlen)
该函数作用是从 listen()监听的队列中接管一个连贯,接管后返回一个新的 连贯 socket,服务器可通过读写该 socket 与客户端进行通信。
留神:对于客户端来说,accept 仅仅只是从申请连贯的 socket 当选一个进去进行连贯,而不论客户端是否放弃连贯

发动连贯 —— 仅客户端应用

int accept(int sockfd, struct sockaddr serv_addr, socklen_t addrlen)

敞开连贯

int close(int fd)

退出移动版