socket是用于通信的工具。
套接字其实是一个广义上的进程间通信的信道。就像pipe一样,在GUN环境下socket也被用一个文件表示。不同的socket文件可以用于不同的进程间通信,甚至可以用来在网络通信中不同主机之间的进程间通信(比如,我们的浏览器进程与某台服务器上监听80端口的进程通信完成web请求)。socket是与其他主机通信的主要手段,相关软件有telnet
, rlogin
, ftp
, talk
等。
并不是所有的机器都支持socket通信,不过在GUN C库中,头文件<sys/socket.h>无论是否存在,socket函数总是可用的,不过在机器不支持时,这些函数总会失败。
1:socket套接字概念
创建套接字时,必须指定通讯方式和通讯类型。创建socket时的协议决定了在套接字上发送和接收数据时的用户级别的语义。socket通讯类型回答了一下问题:
数据的传输单位是什么?一些通信方式将数据视为较大结构的字节序列;其他人将字节分组成数据包
数据在正常运行时可能丢失吗? 一些通信方式保证发送的所有数据按照发送的顺序到达(禁止系统或网络崩溃); 其他样式偶尔会丢失数据作为正常操作部分,有时可能会传送数据包不止一次或错误的顺序。设计使用不可靠通信方式的程序通常需要采取预防措施来检测丢失或错误的数据包,并根据需要重新发送数据。
是否需要啊建立连接?一些通讯方式要求必须建立起连接通道才可以通信,有的则不需要。
当创建一个套接字时,还必须制定一个NAMESPACE,socket("address")也只有在特定命名空间的上下文中才有意义。事实上,甚至数据类型也可能取决于命名空间。命名空间也称为“域”,也就是domain。但是我们应该避免使用这个词,因为它可能会与其他感念混淆。每一个命名空间都有一个以“PF_”开头的符号名称。以“AF_”开头的相应符号名称指定该命名空间的地址格式。
最后,在建立连接时必须制定使用的协议。该协议确定用于发送和接收数据的低级机制。每个协议对于特定的命名空间和通信风格是有效的;命名空间有时被称为协议族,因为这是为什么命名空间名称以“PF_”开头。
协议的规则适用于两个程序之间的数据传递,也许在不同的计算机上;大多数这些规则由操作系统处理,您不需要知道它们。需要了解的协议是什么?
为了在两个socket之间进行通信,它们必须指定相同的协议。
每个协议对于特定的样式/命名空间组合是有意义的,不能与不适当的组合一起使用。例如,TCP协议仅适用于通信的字节流风格和Internet命名空间。
对于风格和命名空间的每个组合,都有一个默认协议,您可以通过指定0作为协议号来请求。这就是通常应该做的 - 使用默认值。
在以下各个地方的描述中,需要用于表示尺寸的变量/参数。这里麻烦开始了。在第一个实现中,这些变量的类型只是int。在当时的大多数机器上,一个int是32位宽,这创建了一个事实上的标准,需要32位变量。这是重要的,因为这种类型的变量的引用被传递给内核。
然后,POSIX组织来到并统一了界面,其中的“所有大小的值都是size_t”。在64位机器上,size_t是64位宽,所以不能再指向变量。
Unix98规范通过引入一个类型socklen_t来提供一个解决方案。在POSIX更改为使用size_t的所有情况下都使用此类型。这种类型的唯一要求是它是至少32位的无符号类型。因此,需要传递对32位变量的引用的实现可以像使用64位值的实现。
2:通讯方式
GUN C库包括对不同套接字的支持,每个套接字有不同的属性。以下符号常量在<sys/socket.h>中定义
宏:int SOCK_STREAM:这种方式就像是Pipes和FIFOs
宏:int SOCK_DGRAM:用于不可靠的发送单独寻址的数据包,与SOCK_STREAM相反。每次向这种套接字写入数据时,该数据都会被打包成一个数据包。由于SOCK_DGRAM套接字没有连接,因此必须在每个数据包指定收件人地址。
系统对要传输的数据唯一的保证是,它会尽力传递每一个数据包。如果在第四个,第五个数据包出现故障之后,它可能成功发送第六个第七个数据包,第七个数据包可能在第六个数据包之前到达。SOCK_DGRAM的典型用途是在合理的时间内没有看到响应的情况下简单地重新发送数据包是可以接受的。点此查看详情
宏:int SOCK_RAW:这种风格提供对低级网络协议和接口的访问。普通用户程序通常不需要使用这种风格。
3:socket地址
套接字的名称通常称为地址。用于处理套接字地址的函数和符号名称不一致。有事使用术语“名称”,有时称为“地址”。可以讲这些视为同义词。
使用socket函数创建一个套接字时没有地址,其他进程只有给它一个地址时才可以找到与它通信。我们将这个过程称为绑定到套接字,在C用使用bind函数实现。
只需要关心套接字的地址,如果其他进程要找到它并开始与其通信。可以指定其他套接字的地址,但这通常是无意义的;首次从套接字发送数据,或者使用它来启动连接时,如果没有指定一个地址,系统会自动分配一个地址。
偶尔,客户端需要指定地址,因为服务器基于地址进行区分;例如,rsh和rlogin协议查看客户端的套接字地址,只有在小于IPPORT_RESERVED(请参阅端口)时才会旁路密码检查。
关于命名空间可以查阅Local Namespace和Internet Namespace。无论命名空间如何,都是用相同的bind、getsocketname来设置一个套接字的地址。在实际中,地址格式存在一些特定的数据结构中,当bind时将其转换为struct sockaddr *。