问题
1、select、poll、epoll分别是什么?
2、有什么区别?
1.主机之间的通信离不开网络,沟通方式可以用TCP、UDP,广播等
TCP,socket连接:
首先了解下socket是怎么使用的
socket主要有以下函数:socket,listen,connect,bind,accept,send,sendto,recv,recvfrom,close,shutdown
网络中的进程都是由socket进行通信的,在linux和windows环境下的头文件主要是:
#include
下面介绍每个函数的具体使用方法和功能。
1.socket
int socket(int domain, int type, int protocol),这个函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1。
domain,函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族,常用AF_INET。
type,函数socket()的参数type用于设置套接字通信的协议类型,主要有SOCKET_STREAM(流式套接字)(Tcp连接,提供序列化的、可靠的、双向连接的字节流。支持带外数据传输)、SOCK——DGRAM(数据包套接字)(支持UDP连接(无连接状态的消息))等。
protocol,函数socket()的第3个参数protocol用于制定某个协议的特定类型,即type类型中的某个类型。通常某协议中只有一种特定类型,这样protocol参数仅能设置为0;但是有些协议有多种特定的类型,就需要设置这个参数来选择特定的类型。
errno,函数socket()并不总是执行成功,有可能会出现错误,错误的产生有多种原因,可以通过errno获得,头文件#include
返回值,如果成功返回非负描述符,不成功返回-1
2.bind
int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen), 当socket函数返回一个描述符时,只是存在于其协议族的空间中,并没有分配一个具体的协议地址(这里指IPv4/IPv6和端口号的组合),bind函数可以将一组固定的地址绑定到sockfd上。
sockfd是socket函数返回的描述符;
myaddr指定了想要绑定的IP和端口号,均要使用网络字节序-即大端模式;
addrlen是前面struct sockaddr(与sockaddr_in等价)的长度。
为了统一地址结构的表示方法,统一接口函数,使得不同的地址结构可以被bind()、connect()、recvfrom()、sendto()等函数调用。但一般的编程中并不直接对此数据结构进行操作,而使用另一个与之等价的数据结构sockaddr_in。例如:
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr))
通常服务器在启动的时候都会绑定一个众所周知的协议地址,用于提供服务,客户就可以通过它来接连服务器;而客户端可以指定IP或端口也可以都不指定,未分配则系统自动分配。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。sockfd的分配是一个bigmap的做法
3.listen
int listen(int sockfd, int backlog),返回0表示成功,-1表示失败
函数listen仅被tcp服务器调用,做两件事情:
1.当函数socket创建一个套接口时,它被假设为一个主动套接口,也就是说,它是一个将调用connect发起连接的客户套接口,函数listen将未连接的套接口转换成被动套接口,指示内核应接受指向此套接口的连接请求。调用函数listen导致套接口从CLOSED状态转换到LISTEN状态。
2.函数的第二个参数规定了内核为此套接口排队的最大连接个数。
一般来说,此函数应在调用幻术socket和bind之后,调用函数accept之前调用。
对于给定的监听套接口,内核要维护两个队列:
1.未完成连接队列,为每个这样的SYN分别开设一个条目:已由客户发出并到达服务器,服务器正在等待完成相应的TCp三次握手过程,这些套接口都处于SYN-RCVD状态;
2.已完成连接队列:为每个已完成TCP三次握手过程的客户开设一个条目。这些套接口都处于ESTABLISHED状态。
两个队列之和数量不得超过backlog。
4.connect
int connect(int sockfd,conststruct sockaddr *addr, socklen_t addrlen)返回0成功,-1表示失败。
通过此函数建立于TCP服务器的连接,实际是发起三次握手过程,仅在连接成功或失败后返回。参数sockfd是本地描述符,addr为服务器地址,addrlen是socket地址长度。
UDP的connect函数,结果与tcp调用不相同,没有三次握手过程。内核只是记录对方的ip和端口号,他们包含在传递给connect的套接口地址结构中,并立即返回给调用进程。
5.accept
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)返回值非负描述符表示成功,-1表示失败。
6.send
size_t send(int sockfd, const void *buf, size_t len, int flags) 返回值成功返回成功拷贝至发送缓冲区的字节数(可能小于len),-1表示失败;
其中:
sockfd:发送端套接字描述符(非监听描述符)
buf:应用要发送数据的缓存 (#define MAXLNE 4096 char buf[MAXLNE];)
len:实际要发送的数据长度
flag:一般设置为0
每个TCP套接口都有一个发送缓冲区,它的大小可以用SO_SNDBUF这个选项来改变。调用send函数的过程,实际是内核将用户数据拷贝至TCP套接口的发送缓冲区的过程:若len大于发送缓冲区大小,则返回-1;否则,查看缓冲区剩余空间是否容纳得下要发送的len长度,若不够,则拷贝一部分,并返回拷贝长度(指的是非阻塞send,若为阻塞send,则一定等待所有数据拷贝至缓冲区才返回,因此阻塞send返回值必定与len相等);若缓冲区满,则等待发送,有剩余空间后拷贝至缓冲区;若在拷贝过程出现错误,则返回-1。关于错误的原因,查看errno的值。
如果send在等待协议发送数据时出现网络断开的情况,则会返回-1。注意:send成功返回并不代表对方已接收到数据,如果后续的协议传输过程中出现网络错误,下一个send便会返回-1发送错误。TCP给对方的数据必须在对