Network programming concerns
Connection establishment
It is divided into two types: the server handles the connection of the receiving client, and the server connects to the third-party service as the client;
int clientfd = accept(listenfd, addr, sz); int connectfd = socket(AF_INET, SOCK_STREAM, 0); connect(connectfd, (struct sockaddr *)&addr, sizeof(addr));
Disconnection of connection
There are two types: active disconnection and passive disconnection;
// Active shutdown close(fd); shutdown(fd, SHUT_RDWR); // Actively close the local read end and the peer write segment shutdown(fd, SHUT_RD); // Actively close the local write end and the peer read segment shutdown(fd, SHUT_WR); // Passive: read end off int n = read(fd, buf, sz); if (n == 0) { close_read(fd); // close(fd); } // Passive: write side off int n = write(fd, buf, sz); if (n == -1 && errno == EPIPE) { close_write(fd); // close(fd); }
close_read and close_write is a function written by ourselves. Generally, we can close it directly
Arrival of message
Reading data from the read buffer;
int n = read(fd, buf, sz); if (n < 0) { //n == -1 if (errno == EINTR || errno == EWOULDBLOCK) break; close(fd); } else if (n == 0) { close(fd); } else { // Processing buf }
Message sent
Write data to the write buffer;
int n = write(fd, buf, sz); if (n == -1) { if (error == EINTR || errno == EWOULDBLOCK) { return; } }
Note the difference between read and write. In non blocking IO, if read is not completed at one time, the number of characters written will be returned. If write is not completed, no data will be written. If write can be completed, all data will be written.
Network IO charge
Detect IO
The io function itself can detect the state of io; However, only one fd corresponding state can be detected;
io multiplexing can detect the status of multiple IOS at the same time;
The difference is that the io function can detect the specific state;
io multiplexing can only detect readable, writable, error, disconnection and other general events;
Operation IO
Only io functions can be used for operation; There are two operation modes: blocking io and non blocking io;
Blocking IO and non blocking IO
- Blocking threads on the network;
- The fd blocking attribute of the connection determines whether the io function is blocked;
- The specific difference is: whether the io function returns immediately when the data does not arrive;
// By default, fd is blocked. The method to set non blocking is as follows:; int flag = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flag | O_NONBLOCK);
IO multiplexing
epoll
Structure and interface
Red black tree – save all fd and events monitored, and a data (fd or ptr) pointing to the user space
Two way linked list – save the fd that listens to the event and copy it to epoll_ In the user queue (array) provided by wait
Connection establishment
//Processing requests struct epoll_event ev = {0}; ev.events |= EPOLLIN; epoll_ctl(efd, EPOLL_CTL_ADD, listenfd, &ev); ... clientfd = accept(...) ev.events |= EPOLLIN; epoll_ctl(efd, EPOLL_CTL_ADD, clientfd, &ev); //Handling non blocking links int connectfd = socket(AF_INET, SOCK_STREAM, 0); connect(connectfd, (struct sockaddr *)&addr, sizeof(addr)); struct epoll_event ev; ev.events |= EPOLLOUT; epoll_ctl(efd, EPOLL_CTL_ADD, connectfd, &ev); //When the write event is triggered, the connection is established successfully if (status == e_connecting && e->events & EPOLLOUT) { status == e_connected; }
Disconnected
if (e->events & EPOLLRDHUP) close_read(fd); //Read end off if (e-e->events & EPOLLHUP) close(fd); //Both read and write are turned off
Data arrival
if (e->events & EPOLLIN) { int n = read(fd, buf, sz); if (n<0) { if(errno == EINTR) continue; //read again if (errno == EWOULDBLOCK) break; //read next time close(fd); } else if (n == 0) { close_read(fd); } }
Data transmission completed
if (e->events & EPOLLOUT) { int n = write(fd, buf, dz); if (n == -1) { if(errno == EINTR) continue; //try again if (errno == EWOULDBLOCK) { struct epoll_event ev; ev.events = EPOLLOUT; epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); } else { close(fd); } } else { // n > 0, success epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); } }
reactor application
The reactor design pattern is an event handling pattern for handling
service requests delivered concurrently to a service handler by one or more inputs
The service handler then demultiplexes the
incoming requests and dispatches them synchronously to the associated request
handlers.
Single reactor
redis
Multiple reactor
Multithreading
memcache,nginx
reference material
[1] Zero sound education c/c++linux background development 2.2.2