Socket的原意是“插座”。在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。
我们把插头插到插座上就能从电网获得电力供应,同样,为了与远程计算机进行数据传输,需要连接到因特网,而 socket 就是用来连接到因特网的工具。
socket的典型应用,就是web服务器和浏览器:浏览器获取用户输入的URL,向服务器发起请求,服务器分析接受到的URL,将对应的网页内容返回给浏览器,浏览器再经过解析和渲染,就将文字和图片,视频等元素呈现给用户。
1.1UNIX/LINUX中的Socket是什么?
在UNIX/LINUX系统中,为了统一各种硬件的操作,简化接口,不同的硬件设备也被看成一个文件,对这些文件的操作,等同于对磁盘上的普通文件的操作。
为了表示和区别已经打开的文件。UNIX/LINUX会给每个文件分配一个ID,这个ID就是一个整数,被称为文件描述符。例如:
- 通常用0来表示标准输入文件,它对应的硬件设备就是键盘。
- 通常用1来表示标准输出文件,它对应的硬件设备就是显示器。
UNIX/Linux 程序在执行任何形式的I/O操作,都是在读或者写入一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数,它背后可能只是一个键盘上的普通文件,FIFO,管道,终端,键盘,显示器,甚至是一个网络连接。
请注意,网络连接也是一个文件,它也有文件描述符。
我们可以通过socket()函数来创建一个网络连接,或者说打开一个网络文件。socket()的返回值就是文件描述符。有了文件描述符,我们就可以用普通的文件操作函数来传输数据了。例如:
用read()读取从远程计算机传来的数据。
用write()向远程计算机写入数据。
只要用socket()创建了连接,剩下就是文件操作。
Windows系统中的socket是什么?
Windows也有类似“文件操作符”的概念,但通常被称为“”文件句柄“。
与 UNIX/Linux 不同的是,Windows 会区分 socket 和文件,Windows 就把 socket 当做一个网络连接来对待,因此需要调用专门针对 socket 而设计的数据传输函数,针对普通文件的输入输出函数就无效了。
1.2 通用socket地址
- socket网络编程接口中表示socket地址的结构体是sockaddr,定义如下
#include <bits/socket.h>
struct sockaddr{
sa_family_t sa_family; //地址族类型
char sa_data[14]; //存放socket地址值
}
sa_family_t:地址族类型,与协议族类型对应
sa_data用于存放socket地址值
- 对于PF_UNIX和PF_INET6来说,16字节不够用,所以Linux定义了下边责怪新的通用socket地址结构体:
#include <bits/socket.h>
struct sockaddr_storage{
sa_family_t sa_family; //地址族类型
unsigned long int __ss_align;
char __ss_padding[128-sizeof(__ss_align)];
}
这个结构体是内存对齐的,也是就是[128-sizeof(__ss_align)]的作用。编译器创建一个变量时候地址并不是随意取得,而是某一个数的倍数,这样就导致了有一部分内存是用不到的,而上述过程会把申请到的内存刚好用完,这就是内存对齐。
1.3 专用socket地址
通用地址获取IP地址和端口号需要额外操作,Linux提供专门的socket地址结构体
1.3.1 UNIX本地协议族专用socket地址结构体
#include <sys/un.h>
struct sockaddr_un {
sa_family_t sin_family; //地址族
char sun_path[108]; //文件路径名
}
1.3.2 TCP/IP协议族专用socket地址结构体
IPv4
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; //IPv4地址,用网络字节序表示
}
IPv6
struct sockaddr_in6 {
sa_family_t sin6_family; //地址族
u_int16_t sin6_port; //端口号,网络字节序
u_int32_t sint6_flowinfo; //流信息,应设置为0
struct in6_addr sin6_addr; //IPv6地址结构体
u_int32_t sin6_scope_id //scope ID, 处于实验阶段
}
struct in6_addr {
unsigned char sa_addr[16]; //IPv6地址,用网络字节序表示
}
所有专用socket地址以及sockaddr_storage类型的变量在实际使用的时候都需要强制转换
为通用的socket地址类型sockaddr。因为所有socket编程接口使用的地址参数类型都是sockaddr.