笔记说明: 本笔记属于linux程序设计第四版。第15章关于套接字的内容。貌似之前对于TCP的工作方式有些误解。现在了解了套接字之后才开始明白自己错在哪里了。
套接字是一种通信机制。在这种机制下面C/S系统的开发工作即可以在本地单位工作,也可以跨网络进行。
这个定义并不确切的原因是我从Windows下面开始接触。把UNIX套接字与网络套接字放在一起的做法还是觉得难以接受。毕竟在用途上相差太多了。但是我想事物自有它存在的道理吧。虽然我们都想给用的十分普遍的事物起一个恰当的名称,但是很多时候是不能如愿的。套接字服务就是这样。我们不知道它们可以用到哪些方面,只是觉得它是一种通信方法,至于为什么要这么设计,就不清楚了。
套接字的创建过程
首先服务器使用系统调用socket来创建一个套接字。它是一个系统分配给该服务器进程的一个类似于文件描述符的资源,它不能与其他进程共享。
在实现当中由于都是文件,所以套接字实际上相当于创建一个特殊文件并打开它,这与虚拟设备文件从创建到使用是一个道理。
其中,本地套接字是linux文件系统当中的文件名。一般放在/tmp目录下面。对于网络套接字,则是与客户连接的特定网络有关的服务标识符(端口号或者访问点)。这个标识符允许我们进入针对特殊端口号的连接转到正确的服务器进程。
系统调用bind用于给套接字命名。然后服务器进程就开始等待客户连接这个命名的套接字。系统调用listen的作用是创建一个队列并将其用于存放来自客户的进入连接。服务器通过accept系统调用来接受客户连接。
当服务器调用accept的时候,它就会创建一个与原来的命名套接字不同的新的套接字。这个新的套接字只用于同这个客户进行通信。而命名套接字则留下来继续处理来自其它客户的连接。一旦连接建立,我们就可以像底层的文件描述符一样利用套接字实现双向通信。
第二次创建新的套接字是自己以前没有想到的。现在想来,大概在网络环境下创建套接字的时候,由套接字函数库对于连接进行了封装,以致于在服务器调用accept的时候,只能得到连接到它的套接字,这也就是说,在内核当中已经处理好来自不同IP+PORT的请求。从而自己实现了数据的分流,最后才把数据交到socket函数库。
创建一个套接字的函数原型是:
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);其中domain是套接字的类型,它可以是AF_UNIX,AF_INET,AF_INTE6,AF_IPX,AF_PACKET等等。type则是套接字的封包格式,比如SOCK_STREAM,SOCK_DGRAM,SOCK_RAW等。我们使用的IP中TCP封包格式为AF_INET加SOCK_STREAM.而UNIX套接字则选择AF_UNIX加SOCK_STREAM.最后一个protocol通常取0.而返回的整型值是套接字描述符。
绑定一个套接字的函数原型是:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);函数的作用是将一个名称与套接字相绑定。之所以要进行绑定,是因为刚开始创建套接字的时候只是指定了在套接字命名空间中注册了这个事物。但是还没有被注册到一个确定的地址。所以我们通过bind函数向该套接字命名。名称由结构体addr指定,后面的addrlen是结构体的长度。在sockaddr当中,包含有关于套接字的一系列说明。
连接一个套接字的函数原型是:
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);其中的各项参数应当与前面所创建的套接字相匹配。返回动作的状态。
接受一个套接字的函数原型是:
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);它返回一个新的针对于该客户端的套接字。
之后对套接字的读写使用read,write或者close调用就可以了。(因为套接字描述符属于文件描述符。)