主机字节序和网络字节序
大端:(高低低高)高位字节存储在内存地址的低地址处,低位存储在内存地址的高地址处.
小端:(高高低低)高位字节存储在内存的高地址处,低位存储在内存地址的低地址处。
即使是同一台机器上的两个进程(比如一个由c语言编写,另一个由java编写)通信,也要考虑字节序的问题(java 虚拟机采用大端字节序);
accept只是从监听队列中取出连接,而不论连接处于何种变化,更不关心任何网络状况的变化.
#include<unistd.h>
int close(int fd);
fd是待关闭的socket,不过,close系统调用并非总是立即关闭一个连接,而是将fd的引用计数减1,只有当fd的引用计数为0时,才真正关闭连接,在多进程程序中,一次fork系统调用默认将使父进程中打开的socket的引用计数加1,因此我们必须在父进程和子进程中都对该socket执行close调用才能将连接关闭.
立即终止连接
#include<sys/socket.h>
int shutdown(int sockfd,int howto);
howto选项的值:
SHUT_RD:关闭sockfd上读的这一半,应用程序不能再针对socket文件描述符执行读操作,并且该socket接收缓冲区中的数据都将被丢弃.
SHUT_WR:关闭sockfd上写的这一半,sockfd的发送缓冲区中的数据会在真正关闭连接之前全部发出去,应用程序不可对该socket文件描述符执行写操作,这种情况下,连接处于半关闭状态。
SHUT_RDWR:同时关闭sockfd上的读和写。
#include <sys/socket.h>
int sockatmark(int sockfd);
sockatmark判断sockfd是否处于带外标记,即下一个被读取到的数据是否是带外数>>据,如果是,sockmark返回1,此时我们就可以利用带MSG_OOB标志的recv调用来接>>收带外数据,如果是不是,则返回0.
#include<sys/socket.h>
int getscokname(int sockfd,struct sockaddr * address,socklen_t* address_in);
int getpeername(int sockfd,struct sockaddr * address,socklen_t * address_in);
getsockname获取sockfd对应的本端的socket地址,并将其存储于address参数指定的内存,getpeername获取对应的远端的socket的地址。
网络信息 API
#incldue<netdb.h>
struct hostent *gethostbyname(const char* name);
struct hostent *gethostbyaddr(const void * addr,size_t len,int type);
struct hostent
{
char * h_name;
char** h_aliases; //主机别名;
int h_addrtype; //地址类型
int h_length; //地址长度
char **h_addr_list; //按网络字节序列列出主机IP地址列表
}
name:目标主机的主机名.
addr:指定目标主机的IP地址,len参数指定addr所指的IP地址的长度,type参数指定addr所指IP的类型,AF_INET或AF_INET6;
#include<netdb.h>
struct servent* getservbyname(const char* name,const char* proto);
struct servent* getservbuport(int port,const char *proto);
name:目标服务的名字;
port:端口
proto:指定服务类型
eg:'tcp'or'udp';
struct servent
{
char * s_name;
char ** s_aliases;
int s_port;
char* s_proto; //服务类型,通常是TCP或者UDP;
}
pipe 函数
#include<unistd.h>
int pipe(int fd[2]);
- fd[2]是一个包含两个int型整数的数组的指针,该函数成功将一对打开的文件描述符填入其参数指向的数组.
- fd[0]只能用于从管道中读出数据.fd[1]只能用于往管道中写数据.
- 默认情况下,这两个文件描述符是阻塞的.
- 用read读一个 没有数据的管道,read会被阻塞,直到管道中有数据.
- 用write向一个满的管道写数据,则write会阻塞,直到管道有足够多的空间可用.
socketpair函数
作用:创建双向管道.
#include<sys/types.h>
#include<sys/socket.h>
int socketpair(int domain,int type,int protocol,int fd[2]);
domain 是AF_UNIX;
dup 和dup2函数
- dup函数创建一个新的文件描述符,该文件描述符和原有的文件描述符file_descriptor指向相同的文件,管道或者网络连接,返回当前系统最小的可用的整数值.
- dup2 它返回第一个不小于file_descriptor_two的整数值.
- dup和dup2创建的文件描述符并不继承原文件描述符放入属性,比如close-on-exec等;
readv函数和writev函数
readv函数将数据从文件描述符读到分散的内存中,writev函数将多块分散的内存数据一并写入文件描述符.
#include<sys/uio.h>
ssize_t readv(int fd,const struct iovec * vector,int count);
ssize_t writev(int fd,const struct iovec * vector,int count);
struct iovec
{
void * iov_base; //内存的起始块
size_t iov_len; //内存的长度;
}
fd参数是被操作的目标文件描述符,vector参数iovec结构体数组count参数是vector数组的长度,即有多少块内存数据需要从fd读出或写到fd.成功返回写入或读出fd的字节数.
sendfile函数
sendfile函数在两个文件描述符之间传输文件完全在内核中进行,避免了用户缓冲区和内核缓冲区之间的数据拷贝,效率大大提高.
#include<sys/sendfile.h>
ssize_t sendfile(int out_fd,int in_fd,off_t* offset,size_t count);
- in_fd参数是待读出内容的文件描述符.
- out_fd参数是待写入数据的文件描述符.
- offset 参数指定从读入文件流的哪个位置开始读,如果为空,默认从起始位置。
- count 参数指定在文件描述符之间传输的字节数.
- in_fd必须是类似于mmap函数的文件描述符,必须指向真实的文件,不能是socket或管道,out_fd必须是socket.
mmap函数和munmap函数
mmap函数用于申请一段内存空间,我们可以将这块内存空间作为进程间通信的共享内存,也可以将文件直接映射到内存中,munmap函数释放mmap创建的这段空间.
#include<sys/mman.h>
void *mmap(void *start,size_t length,int prot,int flags,int fd,(off_t offset));
int munmap(void *start,size_t length);
- start用于指定起始地址,为空,则系统自动分配一个地址,
- length指定内存段的长度,
- prot参数用来设置内存段的访问权限.
(1)PROT_READ:内存段可读。
(2)PROT_WRITE:内存段可写。
(3)PROT_EXEC:内存段可执行.
(4)PROT_NONE:内存段不可被访问. - flags参数控制内存段内容被修改后程序的行为.它可以被设置
(1)MAP_SHARED 在进程间共享这段内存,对该段内存段的修改将反应到被映射的文件中.
(2)MAP_PRIVATE: 内存段为调用进程所私有,对该段内存段的修改不会反映到被映射的文件中.
(3)MAP_ANONYMOUS:这段内存不是从问价映射而来的,其内容被初始化为全0,这种情况,mmap的最后两个参数被忽略.
(4)MAP_FIXED:内存段必须位于start参数指定start参数指定的地址处,start必须是内存页面的大小(4096)的整数倍。
(5)MAP_HUGETLB:按照”大内存页面”来分配内存空间,”大内存页面’的大小可通过/proc/meminfo 来查看。
*fd参数是被映射文件对应的文件描述符,一般通过open系统调用来获得.
spilce 函数
#include<fcntl.h>
ssize_t spilce(int fd_in,loff_t* off_in,int fd_out,loff_t* off_out,size_t len,unsigned int flags);
- 用于在两个文件描述符之间移动数据,零拷贝.
- fd_in 参数是待输入数据的文件描述符,如果fd_in是一个管道文件描述符,那么off_in参数必须被设置为NULL, off表示从输入流的何处开始读取数据,fd_out/off_out用于输出数据流,flags参数控制如何移动数据。
- fd_in和fd_out必须至少有一个是管道文件描述符.
tee函数
tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作,它不消耗数据,源文件描述符上的数据仍然可以用于后续的读操作.
#include<fcntl.h>
ssize_t tee(int fd_in,int fd_out,size_t len,unsigned int flags);
fd_in和fd_out都必须是管道文件描述符.
fcntl 函数
对文件描述符的各种控制操作.
#include<fcntl.h>
int fcntl(int fd,int cmd...)
非阻塞的文件描述符的设置
int setnonblocking(int fd)
{
int old_option=fcntl(fd,F_GETFL); //获取文件描述符的状态。
int new_option=old_option | O_NONBLOCK; //设置非阻塞标志
fcntl(fd,F_SETFL,new_option);
return old_option; //返回文件描述符旧的状态标志.
}