第十三章 UNIX域套接字和并发服务器的预创建技术
·UNIX域套接字
linux操作系统提供了一种UNIX域协议的进程间通信方式,它不能应用在网络中,能使用在本机两进程间的通信中。它能方便的向两个非亲属关系的进程间传递文件描述符,效果类似于在父子进程间传递一样。UNIX域套接字在和本地进程进行交互时候效率更高,因为它不需要处理网络异常可能。
地址结构:
struct sockaddr_un{
short int sun_family;
char sun_path[104];
};
使用示例:
listen_fd = socket(AF_UNIX,SOCK_STREAM,0); //UNIX域套接字
serv_addr.sun_family = AF_UNIX; //UNIX域协议簇
strcpy(serv_addr.sun_path, “/tmp/myfile”); //创建绑定文件
unbind(serv_addr.sun_path); //先解除指定文件的绑定
ret = bind(listen_fd,(struct sockaddr*)&serv_addr,
strlen(serv_addr.sun_path)+sizeof(serv_add.sun_family); //绑定指定文件路径
listen(listen_fd, 倾听队列长度);
·使用sendmsg和recvmsg传递和接收文件描述符
主要使用UNIX域套接字,sendmsg和recvmsg两个函数实现。在发送附加数据之前需要自己定义一个联合体。这样附加数据将在内存中置于struct cmsghdr结构之后,同cmsghdr相关宏定义才能正常工作。
示例代码:
void unix_send_fd(int unix_fd, int conn_fd)
{
struct msghdr msg;
struct iovec iov[1];
char c;
int ret;
union{
struct cmsghdr cm;
char control[CMSG_SPACE(sizeof(int))];
}control_un; //定义联合体,将附加数据置于struct cmsghdr结构之后
struct cmsghdr *cmptr;
//报文套接字使用字段,对于流类型套接字,地址域填为空。
msg.msg_name = NULL;
msg.msg_namelen = 0;
//填写普通数据的缓冲区
iov[0].iov_base = &c;
iov[0].iov_len = 1;
//执行普通数据缓冲区
msg.msg_iov = iov;
msg.msg_iovlen = 1;
//设置附加数据的指针
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
//获取结构struct cmsghdr的指针
cmptr = CMSG_FIRSTHDR(&msg);
//获取该结构的长度
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
//填写控制数据的层次
cmptr->cmsg_level = SOL_SOCKET;
//填写控制数据的类型
cmptr->cmsg_type = SCM_RIGHTS;
//填写控制数据的内容
*(int*)CMSG_DATA(cmptr)=conn_fd;
ret = sendmsg(unix_fd, &msg,0);
if (ret < 0)
error_proc();
//接收就是上面逆向的过程,先预设接收缓冲,然后cmptr= CMSG_FIRSTHDR(&msg);
//最后return *(int*)CMSG_DATA(cmptr);
}
·并发服务器的预创建技术
预创建技术可以为并发服务器解决以下几个显著缺点:
1 为新连接上的客户尽快提供服务,消除新建进程的时延
2 不必为每个客户服务进程都需要建立/释放资源
3 子进程重复利用率高
4 通过传递文件描述符,对子进程动态管理
UNIX套接字对
int socketpari(int family, int type, int protocol, int sockfd[2]);
//建立两个连接好的套接字对,可以像管道一样使用
使用套接字和管道相比有“全双工、可以将文件描述符传递给任何进程”的优点
第十四章 原始套接字
·14.1 原始套接字
原始套接字的使用:
1 ip_fd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); //只允许超级用户建立和使用
//SOCK_RAW创建原始套接字,第三个参数是协议,可以使用0或其他
2 bind和connect函数对原始套接字的影响
原始套接字工作在传输层以下所以没有端口的概念。bind一个原始套接字的地址后,核心只把目的地址是该原始套接字的IP包发送给它,忽略其它。调用connect后,核心将传递源地址是connect地址的IP包给这个原始套接字。如果没有bind和connect原始套接字,核心将把所有协议匹配的IP包传递给它。
IP套接字选项
getsockopt()函数:IP_TTL获取生存期,IP_HDRINCL自行填充IP包头(可伪造)
原始套接字的使用:
1 ip_fd=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP); //只允许超级用户建立和使用
//SOCK_RAW创建原始套接字,第三个参数是协议,可以使用0或其他
2 bind和connect函数对原始套接字的影响
原始套接字工作在传输层以下所以没有端口的概念。bind一个原始套接字的地址后,核心只把目的地址是该原始套接字的IP包发送给它,忽略其它。调用connect后,核心将传递源地址是connect地址的IP包给这个原始套接字。如果没有bind和connect原始套接字,核心将把所有协议匹配的IP包传递给它。
IP套接字选项
getsockopt()函数:IP_TTL获取生存期,IP_HDRINCL自行填充IP包头(可伪造)