1 套接字概念
Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。
既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。
在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。
套接字通信原理如下图所示:

**在网络通信中,套接字一定是成对出现的。**一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。
TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。

2 网络字节序与主机序转换函数
#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);
3 IP地址转换函数
早期:只能处理IPv4的ip地址
/*
* typedef uint32_t in_addr_t;
*
* struct in_addr {
* in_addr_t s_addr;
* };
*/
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/*
* function: 将点分十进制字符串格式转换为二进制形式(按网络字节顺序),并将其存储在inp指向的结构中
*
* function arguments:
* argv1: 传入参数,点分十进制IP地址
* argv2: 传出参数,二进制形式(按网络字节顺序)IP地址
*
* return value:
* success: 返回非0
* faild: 返回0,传入的IP地址无效
*/
int inet_aton(const char *cp, struct in_addr *inp);
/*
* function: 将一个点分十进制的IP转换成一个长整数型数
*
* function arguments:
* argv1: 传入参数,点分十进制IP地址
*
* return value:
* IP转换成的一个长整数型数
*/
in_addr_t inet_addr(const char *cp);
/*
* function: 将网络地址转换成点分十进制字符串格式
*
* function arguments:
* argv1: 二进制形式(按网络字节顺序)IP地址
*
* return value:
* IP地址在转换的十进制字符串格式
*/
char *inet_ntoa(struct in_addr in);
现在:支持IPv4和IPv6
#include <arpa/inet.h>
/*
* function: 将点分十进制字符串格式转换为二进制形式IP地址(按网络字节顺序)
*
* function arguments:
* argv1: AF_INET/AF_INET6
* argv2: 传入参数,点分十进制的IP地址
* argv3: 传出参数, 二进制形式IP地址(按网络字节顺序)
*
* return value:
* success: 返回1
* abnormal: 返回0,IP地址无效
* faild: 返回-1,设置errno
*/
int inet_pton(int af, const char *src, void *dst);
/*
* function: 将二进制形式IP地址(按网络字节顺序)转换为点分十进制字符串格式
*
* function arguments:
* argv1: AF_INET/AF_INET6
* argv2: 传入参数, 二进制形式IP地址(按网络字节顺序)
* argv3: 传出参数,点分十进制的IP地址
* argv4: dst的大小
*
* return value:
* success: 指向dst的指针
* faild: NULL
*/
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
4 struct sockaddr_in结构体
strcut sockaddr 很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体,为了向前兼容,现在sockaddr退化成了(void *)的作用,传递一个地址给函数,至于这个函数是sockaddr_in还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型。
可以通过 man 7 ip 参看。

struct sockaddr_in {
sa_family_t sin_family; /* 地址族: AF_INET */
u_int16_t sin_port; /* 按网络字节次序的端口 */
struct in_addr sin_addr; /* internet地址 */
};
/* Internet地址. */
struct in_addr {
u_int32_t s_addr; /* 按网络字节次序的地址 */
};
5 相关函数
1 socket函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/*
* function: socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符
*
* function arguments:
* argv1:
* AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
* AF_INET6 与上面类似,不过是来用IPv6的地址
* AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
* argv2:
* SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
* SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
* SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
* SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
* SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序
* argv3:
* 传0 表示使用默认协议。
*
* return value:
* success: 返回指向新创建的socket的文件描述符
* faild: 返回-1,并设置errno
*/
int socket(int domain, int type, int protocol);
2 bind函数
#include <sys/types.h>
#include <sys/socket.h>
/*
* function: 为套接字sockfd指定本地地址my_addr
*
* function arguments:
* argv1: socket文件描述符
* argv2: 地址结构(IP+port)
* argv3: 地址结构的大小
*
* return value:
* success: 返回0
* faild: 返回-1,并设置errno
*/
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
// 如果客户端不是bind函数绑定客户端的地址结构,采用“隐式绑定”
3 listen函数
#include <sys/socket.h>
/*
* function: 设置同时与服务器建立连接的上限次数。(同时进行三次握手的客户端数量)
*
* function arguments:
* argv1: socket文件描述符
* argv2: 上限数值。最大128
*
* return value:
* success: 返回0
* faild: 返回-1,并设置errno
*/
int listen(int s, int backlog);
4 accept函数
#include <sys/types.h>
#include <sys/socket.h>
/*
* function: 阻塞等待客户端建立连接。成功的话,返回一个与客户端成功连接的socket文件描述符
*
* function arguments:
* argv1: socket文件描述符
* argv2: 传出参数,成功与服务器建立连接的那个客户端的地址结构(IP+port)
* argv3: 传入传出参数。入: addr的大小; 出: 客户端addr实际大小
*
* return value:
* success: 返回一个与客户端成功连接的socket文件描述符
* faild: 返回-1,并设置errno
*/
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
5 connect函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
/*
* function: 使用现有的socket与服务端建立连接
*
* function arguments:
* argv1: socket文件描述符
* argv2: 传入参数,服务器的地址结构(IP+port)
* argv3: 服务器地址结构的大小
*
* return value:
* success: 返回0
* faild: 返回-1,并设置errno
*/
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
6 C/S模式流程图(UDP、TCP)

本文详细介绍了套接字概念、网络字节序与主机序转换、IP地址转换、structsockaddr_in结构、socket函数到C/S模式流程图,涵盖TCP/IP协议与socketAPI的实战应用。
652

被折叠的 条评论
为什么被折叠?



