属于linux
系统描述符fd
的一部分:
f d ∈ [ 0 , 1 , 2 … … ] fd\in[0,1,2……] fd∈[0,1,2……]
总之 f d > = 0 fd>=0 fd>=0
listen + TCP的描述符
send(sockfd, buf, 5, 0)
表示的是向文件描述符为sockfd
的目的地址发送以buf
为首地址,5个字节的内容
unsigned short 16bit [0 , 65535]
条件
大端:0x12345678 低位地址 0x12
小端:0x12345678 低位地址 0x78
网络序 :大端
CPU序 :大/小端
htonl()
–“Host to Network Long” 将一个无符号长整型数值转换为网络字节序ntohl()
–“Network to Host Long” 将一个网络字节序转换为无符号长整型数值htons()
–“Host to Network Short” 将一个无符号短整型数值转换为网络字节序ntohs()
–“Network to Host Short” 将一个网络字节序转换为无符号短整型数值socket
、bind
、send
、recv
connect
客户端
listen
、accept
服务器
请求报文 = 请求行+请求头+请求数据
GET
:请求指定页面的信息,并返回实体主体POST
:向指定资源提交数据进行处理请求,数据存在请求体HEAD
: 类似get,但不返回具体内容,用于获取报头PUT
:完整替换更新指定资源数据,没有就新增DELETE
:删除指定资源的数据PATCH
:部分更新指定资源的数据OPTIONS
:允许客户端查看服务器的支持的http请求方法CONNECT
:预留给能将连接改为管道的代理服务器TRACE
:追踪服务器收到的请求,用于测试或诊断netstat
接收到新连接后,单独开辟一个子进程独立处理这个新连接,也就是accept
之后
socket
bind
sendto
recvfrom
通过gethostbyname
函数即可解析
函数原型:
struct hostent *gethostbyname(const char *hostname);
通过输入域名我们可以将其解析,然后返回一个hostent
的封装数据,这个数据的结构如下:
struct hostent{
char *h_name; //official name
char **h_aliases; //alias list
int h_addrtype; //host address type
int h_length; //address lenght
char **h_addr_list; //address list
}
这样我们就能解析域名并得到真正的IP地址
全双工通过一个socket文件描述符允许数据从两边同时发送和接收
这边总结一下其他的知识点:
单工指的是数据只能从一方传输,并不能实现双方通信
例如:电视、广播、收音机等
半双工指的是数据能从两方传输,但是同一时间数据只能在一个方向传输
例如:对讲机
全双工指的是数据可以从两个方向同时传输
例如:电话
socket
不仅仅支持TCP/IP协议和本地进程间通信accept
函数,那么三次握手不会失效ping
通,说明两台电脑的网络层是相通的。epoll
性能比select
性能各有所长sendto
可以 send
可以(connect后)Ctrl+C
组合键实际触发的信号是 SIGINT
OPTIONS
动作代表允许客户端查看服务器的性能sockaddr_in
结构体中,sin_addr
中一般使用INADDR_ANY
宏代表可以接收任意本地设备的网络数据inet_aton
函数可以将主机字节序结构的IP地址转换为网络字节序FD_ISSET()
宏用来判断文件描述符是否在文件集合中通过epoll
多路复用实现高并发服务器
epoll
没有最大并发连接限制,上限是最大可打开文件的数目,一般这个数目和系统内存有关,但是考虑到服务器的内存一般不小,所以可以实现高并发
效率有明显提升,epoll
对于句柄事件的选择和select
、poll
的遍历不同,epoll
是事件相应的,即:句柄事件来到后,立即选择出来,复杂度为常数级别 O ( 1 ) O(1) O(1),不需要遍历,内核将句柄通过红黑树保存,所以随着句柄数目的增多,IO的效率也不会随之线性下降(只是会稍微下降一点)
内存拷贝, select
让内核把 FD 消息通知给用户空间的时候使用了内存拷贝的方式,开销较大,但是epoll
在这点上使用了共享内存的方式,这个内存拷贝也省略了。
epoll
的设计和实现与select完全不同。epoll
通过在Linux内核中申请一个简易的文件系统,把原先的select/poll调用分成了3个部分:
调用epoll_create()
建立一个epoll
对象(在epoll
文件系统中为这个句柄对象分配资源)
调用epoll_ctl
向epoll
对象中添加这上万个连接的套接字
调用epoll_wait
收集发生的事件的连接
只需要在进程启动时建立一个epoll
对象,然后在需要的时候向这个epoll
对象中添加或者删除连接。同时,epoll_wait
的效率也非常高,因为调用epoll_wait
时,并没有直接向操作系统复制这数万个连接的句柄数据,内核也不需要去遍历全部的连接。所以可以应对高并发的情况
socket
struct sockaddr_in self
、 bind
listen
eg:
int init_listen(){
//初始化监听描述符
int listen_fd;
int ret;
struct sockaddr_in server_addr;
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
fprintf(stderr, "fail to socket : %s\n", strerror(errno));
return -1;
}
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
ret = bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));
if (ret < 0) {
perror("fail to bind");
return -1;
}
listen(listen_fd, 5);
return listen_fd;
}
int listen_handler(int listen_fd) {
int new_fd;
new_fd = accept(listen_fd, NULL, NULL);
if (new_fd < 0) {
perror("fail to accept");
return -1;
}
return new_fd;
}
void read_func() {
char buf[1024];
fd = open(xxxx);
ret = read(fd, buf, sizeof(buf));
while (ret) {
if (ret < 0) {
xxxx;break;
}
send(xxx, buf, ret);
ret = read(fd, buf, sizeof(buf));
}
}
void judge()
{
int i = 48;
int* p = &i;
char c = 0;
c = *((char*)p);
if (c == '0')
printf("小端\n");
else
printf("大端\n");
}
select、poll、epoll - IO模型超详解
IO模型之IO多路复用 异步IO select poll epoll 的用法
Linux下的socket编程实践(九) epoll实现高并发的原理及其使用