目录
[1.1 端口号](#1.1 端口号)
[1.2 IP地址](#1.2 IP地址)
[1.3 套接字类型](#1.3 套接字类型)
[1.4 套接字常见接口](#1.4 套接字常见接口)
[1.5 套接字种类](#1.5 套接字种类)
前言:
网络编程中的套接字(socket)是实现网络通信的关键,换句话来说,套接字像两个端点,而发送方与接收方就是通过这两个端点进行通信的,socket的意思是插座,表示插头和插座连接上后,即发送方和接收方连接上了,然后把电流通过插座流向插头对标发送方与接收方建立了链接。
1、套接字的关键因素
网络通信的本质是进程间通信,只不过这两个进程不在同一个主机上,而socket要让这两个进程进行通信,则socket必须提供某些字段,以便于让这两个进程找到彼此。socket关键字段介绍如下文。
1.1 端口号
端口号属于传输层协议,端口号是一个2字节16bit位的整数 ,他的作用就是找到该主机上的对应进程,并将通信的信息给到该进程。一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定,即一个端口号只能对应一个进程。
1.2 IP地址
IP地址是每台主机每台设备在网络中的唯一标识符,通过IP地址就可以在全球网络中定位唯一一台主机或设备。至此,有了端口号和IP地址,就能让发送进程在茫茫的网络中找到接收进程。
示意图如下:
1.3 套接字类型
套接字有两种类型:1、TCP-流式套接字(SOCK_STREAM),2、UDP-数据报套接字(SOCK_DGRAM)。他们都是传输层的协议,在进行通信时,必须对套接字的类型进行定义。
而TCP和UDP的区别在于:TCP较UDP更加"负责",主要体现在TCP收到应用层的报文后,会对这个报文负责到底,如果该报文在后面的过程中发生传输失败或者传输时乱序,则TCP会重新发送一份。
而udp就不一样了,不管这个报文传输成功了还是失败了udp都不会对其维护,udp只负责把从应用层接收到的报文给到下层。
也正是这样,因此TCP的维护成本会更高。
1.4 套接字常见接口
在Linux下使用套接字时,通常会搭配一些常见接口实现通信,接口如下:
cpp
// 创建 socket 文件描述符 (TCP/UDP, 客户端和服务器都必须调用此接口)
// 返回一个文件描述符
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address,
socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,
socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
1.5 套接字种类
上述接口中有一个叫做struct sockaddr的结构类型,他是套接字种类,种类的不同导致使用的网络层协议的不同,比如sockaddr_in用于IPv4,sockaddr_in6用于IPv6。而上述函数中形参默认使用struct sockaddr*类型,目的就是为了程序的通用性,能够接收IPv4或IPv6以及其他类型的套接字种类。简单来说就是用IPv4协议,则需要定义sockaddr_in类型的变量,用IPv6则定义sockaddr_in6的变量。
套接字种类示意图:
IPv4、IPv6地址类型分别对应上图的常数AF_INET、AF_INET6。
基于IPv4编程时, 使用的数据结构是sockaddr_in,这个结构体里主要有三个信息是值得关注的:1、地址类型, 2、端口号, 3、IP地址,在定义sockaddr_in变量时,这三个信息需要我们手动填写,然后再调用函数bind进行绑定。
2、网络字节序
内存中的数据相对于内存的地址有大小端之分,小端机器指的是低字节内容存在低地址处,大端机器指的是高字节内容存在低地址处。而网络传输数据时,是先传低地址的内容再传高地址的内容,并且规定采用大端字节序的方式传递数据(即使是在小端机器上),所以接收方默认接收的字节是高字节内容,因此如果当前发送主机是小端, 就需要先将数据转成大端模式再进行发送,若是大端则直接发送即可。
而现实中大部分使用的都是小端机器,若在小端机器上手动把数据转成大端模式,是一件很麻烦的事情,因此在Linux下系统提供了可以帮助我们实现小端转大端的接口,接口如下:
cpp
#include <arpa/inet.h>
// 主机序列转网络序列
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
// 网络序列转主机序列
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);
简单的记忆方法:函数名中以to为分隔,h表示host(主机序列),n表示network(网络序列),l表示long类型,s表示short类型。
比如htonl函数是将32位的长整数从当前主机序列转换为网络序列。如果主机是小端字节序,调用上述函数则转成大端字节序,如果主机是大端字节序,这些函数不做处理直接返回原有值。简单来说,ntohs和ntohl默认形参是大端字节序,而htons和htonl默认返回值是大端字节序。
结语
以上就是关于网络套接字socket的讲解,socket是应用层实现通信的关键因素,发送方和接收方若想实现通信则必须使用socket套接字。
最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!