Socket编程基础知识

常用的数据结构及函数

地址转换类

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

主机序通常为小端,网络字节序为大端。下列函数在包中:

  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地址,于是需要一些函数对他们进行一些转换,这些函数位于包中。

  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 
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 
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 
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)

你可能感兴趣的:(c服务器)