Linux 进程间通信(二)(网络IPC:套接字)
2015-09-05 16:58
274 查看
socket描述符
套接字是通信端点的抽象,创建一个套接字使用如下函数:#include <sys/socket.h> int socket(int domain, int type, int protocol); 返回值:若成功,返回套接字描述符;若出错,返回-1 说明: domain: 指定通信的特征,包括地址格式,以AF_开头的常数表示地址族(address family):
|
SOCK_STREAM套接字提供了字节流服务,应用程序不能分辨出报文的界限;SOCK_SEQPACKET套接字提供了基于报文的服务,这意味着接受的数据量和发送的数据量完全一致;SOCK_RAW套接字提供了一个数据报接口,用于直接访问下面的网络层,使用该套接字时,必须有root用户权限,并且需要应用程序自己负责构造协议头。
套接字通信是双向的,可以使用shutdown函数来禁止一个套接字I/O:
#include <sys/socket.h> int shutdown(int sockfd, int how); 返回值:若成功,返回0;若出错,返回-1。 说明: how: SHUT_RD(关闭读),SHUT_WR(关闭写),SHUT_RDWR(关闭读写). 套接字是一个文件描述符,那么就可以使用close释放一个套接字。既然如此,为何还使用shutdown呢?首先,只有最后一个活动引用关闭时,close才释放网络端点。这意味着如果复制了一个套接字(dup),要直到关闭了最后一个引用它的文件描述符才会释放这个套接字。而shutdown允许使一个套接字处于不活动状态,和引用它的文件描述符数目无关。其次,有时可以很方便地关闭套接字双向传输中的一个方向。 补充:套接字本质上是一个文件描述符,如下为适用于文件描述符的函数在套接字中的表现行为:
|
寻址
不同的处理器架构有着不同的字节序,如果需要实现异构通信,必须统一字节序方式。网络协议指定了字节序(网络字节序),TCP/IP协议栈使用大端方式,对于使用TCP/IP的应用程序,有4个函数可以用来在处理器字节序和网络字节序之间进行转换:#include <arpa/inet.h> uint32_t htonl(uint32_t hostint32); // 32位主机-->32位网络 uint16_t htons(uint16_t hostint16); // 16位主机-->16位网络 uint32_t ntohl(uint32_t netint32); // 32位网络-->32位主机 uint16_t ntohs(uint16_t netint16); // 16位网络-->16位主机 说明: h: 主机字节序 n: 网络字节序 l: 4字节的长整型 s: 2字节的短整型 |
int initserver(int type, const struct sockaddr* addr, socklen_t alen, int qlen) { int fd; int err = 0; if((fd = socket(addr->sa_family, type, 0)) < 0) return -1; if(bind(fd, addr, alen) < 0) goto errout; if(type == SOCK_STREAM || type == SOCK_SEQPACKET) if(listen(fd, qlen) < 0) goto errout; return fd; errout: err = errno; close(fd); errno = err; return -1; }
View Code
数据传输
因为一个套接字表示为一个文件描述符,因此只要建立连接,就可以使用read和write来操作套接字了。这意味着可以将套接字传递给处理文件的函数,而该文件处理函数并不需要了解套接字即可工作。除了read和write,还有6个专为套接字设计的函数:3个用于发送数据,3个用于接收数据。
3个用于发送数据的函数如下:
#include <sys/socket.h> ssize_t send(int sockfd, const void* buf, size_t nbytes, int flags); 返回值:成功,返回发送的字节数;失败,返回-1 说明: send类似于write,使用send时套接字必须已经连接。然而,send支持选项flags,如下:
#include <sys/socket.h> ssize_t sento(int sockfd, const void* buf, size_t nbytes, int flags, const struct sockaddr* destaddr, socklen_t destlen); 返回值:成功,返回发送的字节数;失败,返回-1 说明: 与send相比,sendto可以指定目的地址,当然这是对于无连接的情况而言的,对于面向连接的情况,目的地址是被忽略的,因为连接中隐含了地址。在无连接的情况下,可以直接使用sendto,或者先使用connect设置目的地址,然后使用send发送数据。 通过套接字发送数据时,还可以调用带有msghdr结构的sendmsg来指定多重缓冲区传输数据,这与writev类似: #include <sys/socket.h> ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags); 返回值:成功,返回发送的字节数;失败,返回-1 struct msghdr { void* msg_name; /* 目的地址名字,是一个指向结构体struct sockaddr的指针 */ socklen_t msg_namelen; /* 目的地址的长度 */ struct iovec* msg_iov; /* 消息内容,指向struct iovec的指针 */ int msg_iovlen; /* 长度 */ void* msg_control; /* 控制消息 */ socklen_t msg_controllen; int msg_falgs; /* 标记如何接受数据 */ } sendmsg函数可以通过msghdr指定多个缓冲区发送数据,同时可以发送辅助数据。 |
#include <sys/socket.h> ssize_t recv(int sockfd, void* buf, size_t nbytes, int flags); 返回值:成功,返回接收数据的字节长度;若未接收到数据或发送方已结束发送,返回0; 失败,返回-1 说明: flags如下:
可以使用recvfrom来得到数据发送者的地址: #include <sys/socket.h> ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags,struct sockaddr* addr, socklen_t* addrlen); 返回值:成功,返回接收的实际字节数;若无可用数据或对方已经结束传输,返回0;失败,返回-1 说明: recvfrom函数可以用于追踪发送者的地址,将其存储在addr指向的结构体中,addrlen表示其长度。该函数一般用于无连接的套接字通信中,在面向连接的情况下,等同于recv。 为了将接收到的数据送入多个缓冲区,可以使用recvmsg,类似于readv: #include <sys/socket.h> ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags); 返回值:成功,返回接收的实际字节数;若无可用数据或对方已经结束传输,返回0;失败,返回-1 说明: recvmsg可以通过msghdr指定多个缓冲区接收数据,同时也可以接收辅助数据,其flags标志如下:
|
套接字选项
套接字选项提供了两个接口来控制套接字行为:一个接口用来设置选项,另一个接口用于查询选项的状态。可以获取或设置以下3种选项:(1) 通用选项,工作在所有套接字类型上。
(2) 在套接字层次管理的选项,但是依赖于下层协议的支持。
(3) 特定于某协议的选项,每个协议独有。
可以使用setsockopt函数来设置套接字选项:
#include <sys/socket.h> int setsockopt(int sockfd, int level, int option, const void* val, socklen_t len); 返回值:成功,返回0;失败,返回-1 说明: level标识了选项应用的协议。如果选项是通用的套接字层次选项,择level设置成SOL_SOCKET。否则,level设置成控制这个选项的协议编号。对于TCP选项,level为IPPROTO_TCP,对于IP,level为IPPROTO_IP。如下为通用套接字层次选项: val根据选项的不同指向一个数据结构或一个整数,一些选项是on/off开关。如果整数非0,则启动该选项,如果为0,则禁止该选项。 len指定了val指向的对象的大小。 |
#include <sys/socket.h> int getsockopt(int sockfd, int level, int option, void* val, socklen_t* restrict lenp); 返回值:成功,返回0;失败,返回-1 说明: lenp是一个指向整数的指针。在调用getsockopt之前,设置该整数为复制选项缓冲区的长度。如果选项的实际长度大于此值,则选项会被截断;如果实际长度小于此值,那么返回时将此值更新为实际长度。 |
带外数据
与普通数据相比,带外数据拥有更高的传输优先级,即使传输队列中已经有数据了,带外数据也可先行传输。TCP支持带外数据,UDP不支持。TCP将带外数据称为紧急数据(urgent data),TCP仅支持一个字节的紧急数据。可以在send函数中指定MSG_OOB标志来产生紧急数据(指定MSG_OOB标志的send发送的字节数如果超过一个时,则最后一个字节将被视为紧急数据)。如果通过套接字安排了信号的产生,那么紧急数据被接收时,会发送SIGURG信号。
TCP还支持紧急标记(urgent mark)的概念,即在普通数据流中标记紧急数据所在的位置。如果采用套接字选项SO_OOBINLINE,那么可以在普通数据中接受紧急数据,可以使用函数sockatmark判断是否到达了紧急标记处。
#include <sys/socket.h> int sockatmark(int sockfd); 返回值:到达标记处,返回1;没到达,返回0;出错,返回-1 说明: 可以在普通数据流中接收紧急数据,也可以在recv函数中采用MSG_OOB标志在其他队列数据之前接收紧急数据。 TCP队列仅用一个字节接收紧急数据,如果在接收当前紧急数据前又有新的紧急数据到来,那么已有的字节会被丢弃。 |
相关文章推荐
- Socket网络协议简单介绍和使用
- TCP/IP的三次握手
- HDU 4940 - Destroy Transportation system(网络流)
- POJ 3204 Ikki's Story I - Road Reconstruction(最小割+残余网络)
- vuGen回放https录制出错
- TCP/IP(2)链路层
- Shell命令——网络
- sharepoint 2016 学习系列篇(6)-配置网站的备用网络映射
- HDU5007-Post Robot-2014西安网络赛(字符串水题)
- TCP协议疑难杂症解析
- 递归神经网络不可思议的有效性
- 笔试——数据库、网络、操作系统(Linux)
- 新手使用长按手势将网络图片保存至系统相册出现的问题
- linux常用的网络管理命令
- Internet采用哪种网络协议?该协议的主要层次结构?
- Linux命令学习(一)_网络
- TCP与UDP
- java网络通信TCP与UDP
- xutils中configCurrentHttpCacheExpiry()的用法
- 虚拟机完全克隆CentOS后配置网络