首先明确大端字节序和小端字节序,看下面一段程序
#include<stdio.h>
void byteorder() {
union {
short value;
char union_bytes[sizeof(short)];
}test;
test.value = 0x0102;
if((test.union_bytes[0] == 1) && (test.union_bytes[1] == 2)) {
printf("big endian\n");
}
else if((test.union_bytes[1] == 1) && (test.union_bytes[0] == 2)) {
printf("little endian\n");
}
else printf("unknown...\n");
}
int main() {
byteorder();
}
联合体,value和2个字节的字符串union_bytes共用一块内存, 像value中写入两个16进制的数
从union_bytes 可以判断出大端模式以及小端模式
下面列出linux C socket API函数
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
int listen(int sockfd, int backlog);
int accept(int sockfd, struct sockaddr* addr, socklen_t *addrlen);
int close(int fd);
int shutdown(int sockfd, int howto);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen);
其中ssize_t 为无符号整数类型
前三个函数较为简单,不再赘述。对于监听函数listen,第二个参数是backlog,表示内核监听队列的最大长度,如果数量超过了这个值,则服务器将不受理新的客户连接,下面我们用一个实验来测试一下这个最大长度和backlog有什么关系:
这里listen和accept还是有区别。
对于接受连接的accept函数,它从监听队列中取出一个连接,与其建立连接,而不管其处于ESTABLISHED或CLOSE_WAIT状态,更不关心任何网络变化。
第九章 I/O复用
I/O复用技术是重要的提高服务器工作效率和性能的手段,Linux下实现I/O复用的系统调用主要有select、poll和epoll。
首先我们来看一下select的函数原型和常用的宏:
#include<sys/select.h>
int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
FD_ZERO(fd_set *fdset); //清除fdset所有位
FD_SET(int fd, fd_set* fdset); //设置fdset的位fd
FD_CLR(int fd, fd_set* fdset); //清除fdset的位fd
int FD_ISSET(int fd, fd_set* fdset); //测试fdset的位fd是否被设置
epoll是Linux特有的I/O复用函数,其实现和select、poll有很大区别。epoll将用户关心的文件描述符上的事件放在内核里的一个事件表中,从而无须像select和poll那样每次调用都要重复传入文件描述符集或事件集,但是epoll需要一个额外的文件描述符来标识内核中的这个事件表。与poll不同的是,epoll如果检测到事件,就将所有就绪时间从内核时间表中复制到events指向的数组中,这样就极大提高了应用程序检索就绪文件描述符的效率,从O(n)的时间复杂度降为了O(1)。我们来看一下epoll的几个函数:
1 #include<sys/epoll.h>
2 int epoll_create(int size);
3 int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
4 int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
epoll对于文件描述符的操作有两种模式:LT和ET,其中LT是默认模式,这种模式下其效率相当于一个稍微改进的poll,效率没有显著提高,而ET模式则是epoll的高效工作模式。对于LT模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,因公程序可以不立即处理该事件,这样当epoll_wait再次被触发,还会再向应用程序通告此事件,知道该事件被处理。而对于ET模式,当epoll_wait检测到其上有事件发生,将其通告应用程序,应用程序必须马上处理,因为后续的epoll_wait将不再向应用程序通知这一事件。可见,ET模式降低了同一个epoll事件被重复触发的次数,所以效率更高。我们用一个实例来看一下: