不同计算机上运行的进程间的通信机制:网络进程间通信
套接字:
(1)创建一个套接字
#include <sys/socket.h>
int socket ( int domain , int type , int protocol);
参数说明:
domain:确定通信的特性,包括地址格式。通常以AF_开头,也指地址族。其中:
AF_INET:IPv4因特网域
type:确定套接字类型。其中比较重要的有:SOCK_STREAM 有序、可靠、双向的面向连接字节流
protocol:通常是0
在AF_INET通信域中套接字类型SOCK_STREAM的默认协议时TCP,传输控制协议
(2)关闭套接字
可以使用close函数,但是使用shutdown函数更好一些。其定义如下:
int shutdown( int sockfd , int how);
参数说明:
how:SHUT_RD(关闭读端),那么将无法从套接字读取数据;
SHUT_WR(关闭写端),SHUT_RDWR(读写端均关闭)
不适用close是因为,close直到最后一个该文件描述符引用结束以后,才会释放相关的系统资源。而shutdown可以直接使一个套接字处于不活动的状态。而且使用shutdown也使得我们在关闭单独的一个端口(读或者写)比较方便。
进程的标识有两个部分:
计算机的网络地址可以帮助标识网络上想与之通信的计算机,而服务可以帮助标识计算机上特定的进程。
字节序:
字节序是一个处理器架构的特性,用于指示像整数这样的大数据类型的内部字节的顺序。
如果处理器架构支持大端字节序,那么最大字节地址对应于数字的最低有效字节(LSB);
小端字节序则相反:数字最低字节对应于最小字节地址
注意点:不管字节如何排序,数字最高为总是在左边,最低位总是在右边。
TCP/IP协议栈采用大端字节序,所以应用程序在处理器的字节序和网络字节序之间进行转换。
对于TCP/IP应用程序,提供了四个通用函数以实施在处理器字节序和网络字节序之间的转换:
#include <arpa/inet.h>
uint32_t htonl ( uint32_t hostint32);
该函数返回值:以网络数据字节序表示的32位整型数
uint16_t htons( uint16_t hostint16);
返回值:以网络数据字节序表示的16位整型数
uint32_t ntohl ( uint32_t netint32);
返回值:以主机字节序表示的32位整型数
unint16_t ntohs( uint16_t netint16)
记忆规则:h表示主机(host)字节序,n表示网络(net)字节序。l 表示长(long)整数,s表示短(short)整数。
地址格式:
为了使不同格式地址能够被传入到套接字函数中,地址呗强制转换成通用的地质结构sockaddr表示。
struct sockaddr {
sa_family_t sa_family ;
char sa_data[];
...
};
在IPv4因特网域中(AF_INET)中,套接字地址用如下结构sockaddr_in表示:
struct in_addr{
in_addr_t s_addr; /*ipv4 address*/
};
struct sockaddr_in
{
sa_family_t sin_family;//address family
in_port_t sin_port; // port number
struct in_addr sin_addr; // ipv4 address
};
有时候需要打印出人能够理解的地址格式。此时需要在二进制地址格式与点分十进制字符串表示的地址之间进行转换。如下:
#include<arpa/inet.h>
const char* inet_ntop( int domain ,const void *restrict addr, char *restrict str, socklen_t size);
返回值:成功则返回地址字符串指针
int inet_pton( int domain, const char* restrict str , void *restrict addr);
返回值:成功返回1
将套接字与地址绑定
int bind( int sockkfd , const struct sockaddr *addr , socklen_t len);
成功返回0,否则,返回-1;
端口号必须不小于1024,除非该进程具有相应的特权。因为端口号小于1024的端口均留作系统特殊功能端口。
对于因特网,如果指定的IP地址为INADDR_ANY,套接字端点可以被绑定到所有的系统网络接口。
可以调用函数getsockname()来发现绑定到一个套接字的地址:
int getsockname( int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp);
其中alenp会被置为地址的大小。
如果套接字已经和对方连接,可以调用getpeername来找到对方的地址:
int getpeername( int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp )
连接
如果处理的是面向连接的网络服务(SOCK_STREAM, SOCK_SEQPACKET),在开始交换之前,需要在请求服务的进程套接字(客户端)和提供服务的进程套接字(服务器)之间建立一个连接。
int connect ( int sockfd , const struct sockaddr *addr , socklen_t len);
宣告可以接收连接请求:
int listen ( int sockfd, int backlog);
参数说明:
backlog:提供一个提示,用于表示该进程所要进入队的连接请求数量。
使用accept()函数获得连接请求并建立连接
int accept ( int sockfd , struct sockaddr *restrict addr , socklen_t *restrict len);
成功则返回套接字描述符
数据传输
虽然可以通过read和write函数进行数据的传输,但是如果想指定选项、从多个客户端接收数据包或者发送带外数据,需要采用专门的函数:
发送数据:
(1)send
ssize_t send ( int sockfd, const void *buf ,size_t nbytes, int flags);
send函数和write函数很相似,但是其多支持了第四个参数-flags,其定义如下:
MSG_DONTROUTE:勿将数据路由出本地网络
MSG_DONTWAIT:允许非阻塞操作
MSG_EOR:如果协议支持,此为结束记录
MSG_OOB:....................,发送带外数据
(2)sendto
sendto允许在无连接的套接字上指定一个目标地址
ssize_t sendto ( int sockfd, const void *buf ,size_t nbytes, int flags,
const struct sockaddr *destaddr, socklen_t destlen)
(3)sendmsg
ssize_t sendmsg( int sockfd , const struct msghdr *msg ,int flags)
接收数据:
(1)recv
ssize_t recv ( int sockfd, const void *buf ,size_t nbytes, int flags)
(2)recvfrom
ssize_t recvfrom( int sockfd, const void *buf ,size_t nbytes, int flags,
const struct sockaddr *destaddr, socklen_t destlen)
(3)recvmsg
类似于sendmsg函数的参数
(更新中..........)