Table of Contents
1 I/O multiplexing
1.1 Introduction to select function and other interfaces
1.2 Original TCP-socket example:
1.3 Example of implementing select function TCP-socket:
2 exercises
1 I/O multiplexing
Multiplexing implementation
1.1 Introduction to select function and other interfaces
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ };
select
is a system call function used for multiplexed I/O. It can monitor multiple file descriptors for readable, writable and exception events at the same time. (Special usage: It can also be used to block microseconds, and other file descriptor functions are set to NULL and not used).
Parameter description is as follows:
nfds
: The number of monitored file descriptors, that is, the maximum file descriptor value that needs to be checked plus one.readfds
: A collection of file descriptors used to check for readable events. (Commonly used, generally multi-channel monitoring is readable)writefds
: A collection of file descriptors used to check for writable events.exceptfds
: A collection of file descriptors used to check for exception events.timeout
: timeout period. NULL: permanent blocking, 0: non-blocking mode
The select
function will monitor based on the file descriptor set and timeout period in the parameters. When an event that meets the conditions occurs, the select
function will return and describe the corresponding file. character set to modify. The specific return values and collection modifications are as follows:
- If the timeout period is reached, the return value is 0.
- If an error occurs, the return value is -1 and the corresponding error code is set.
- If a readable event occurs, the corresponding file descriptor in
readfds
will be modified, and the return value will be greater than 0. - If a writable event occurs, the corresponding file descriptor in
writefds
will be modified, and the return value will be greater than 0. - If an exception occurs, the corresponding file descriptor in
exceptfds
will be modified, and the return value will be greater than 0.
By continuously calling the select
function, non-blocking I/O monitoring on multiple file descriptors can be implemented to handle readable, writable and exception events in a timely manner.
fd_set structure: Each bit represents a file descriptor, the value is 0 or 1, nfds represents the largest file descriptor + 1.
Supplement: In the standard C library header file
, fd_set
is implemented through a fixed-size array. The size of the array is defined by the macro FD_SETSIZE
. Normally, the default value of FD_SETSIZE
is 1024.
/*Remove the file descriptor from the set*/ void FD_CLR(int fd, fd_set *set); /*Check whether the file descriptor exists in the collection*/ int FD_ISSET(int fd, fd_set *set); /*Add file descriptor*/ void FD_SET(int fd, fd_set *set); /*Initialize collection*/ void FD_ZERO(fd_set *set);
1.2 Original TCP-socket example:
server.c
#include "net.h" int main(int argc, char *argv[]) { /*Check parameters, if less than 3, exit the process directly*/ Argment(argc, argv); /*Create a socket with listening mode set*/ int fd = CreateSocket(argv); /*Receive client connection and generate new file descriptor*/ int newfd = accept(fd, NULL, NULL); if(newfd < 0) perror("accept"); /*Process client data*/ while(DataHandle(newfd) > 0); return 0; }
socket.c
#include "net.h" void Argment(int argc, char *argv[]){ if(argc < 3){ fprintf(stderr, "%s<addr><port>\ ", argv[0]); exit(0); } } int CreateSocket(char *argv[]){ /*Create socket*/ int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) ErrExit("socket"); /*Allow addresses to be reused quickly*/ int flag = 1; if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, & amp;flag, sizeof(flag) ) ) perror("setsockopt"); /*Set communication structure*/ Addr_in addr; bzero( & amp;addr, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_port = htons( atoi(argv[2]) ); /*Bind communication structure*/ if( bind(fd, (Addr *) & amp;addr, sizeof(Addr_in) ) ) ErrExit("bind"); /*Set the socket to listening mode*/ if( listen(fd, BACKLOG) ) ErrExit("listen"); return fd; } int DataHandle(int fd){ char buf[BUFSIZ] = {}; int ret = recv(fd, buf, BUFSIZ, 0); if(ret < 0) perror("recv"); if(ret > 0) printf("data: %s\ ", buf); return ret; }
net.h
#ifndef _NET_H_ #define _NET_H_ #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <unistd.h> #include <strings.h> #include <errno.h> typedef struct sockaddr Addr; typedef struct sockaddr_in Addr_in; #define BACKLOG 5 #define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0) void Argment(int argc, char *argv[]); int CreateSocket(char *argv[]); int DataHandle(int fd); #endif
1.3 Example of implementing select function TCP-socket:
sever.c
#include "net.h" #include <sys/select.h> #define MAX_SOCK_FD 1024 int main(int argc, char *argv[]) { int i, ret, fd, newfd; fd_set set, tmpset; Addr_in clientaddr; socklen_t clientlen = sizeof(Addr_in); /*Check parameters, if less than 3, exit the process directly*/ Argment(argc, argv); /*Create a socket with listening mode set*/ fd = CreateSocket(argv); FD_ZERO( & amp;set); FD_ZERO( & amp;tmpset); FD_SET(fd, & amp;set); while(1){ tmpset = set; //The temp file descriptor set will be modified by the select function to reflect the ready file descriptors. if( (ret = select(MAX_SOCK_FD, & amp;tmpset, NULL, NULL, NULL)) < 0) ErrExit("select"); //The macro defines an error handling if(FD_ISSET(fd, & amp;tmpset) ){ /*Receive client connection and generate new file descriptor*/ if( (newfd = accept(fd, (Addr *) & amp;clientaddr, & amp;clientlen) ) < 0) perror("accept"); printf("[%s:%d]Connection established\ ", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); FD_SET(newfd, & amp;set); //New client file descriptor is added to set }else{ //Process client data for(i = fd + 1; i < MAX_SOCK_FD; i + + ){ //fd is the server, fd + 1 is the first incoming client descriptor if(FD_ISSET(i, & amp;tmpset)){ if(DataHandle(i) <= 0){ if( getpeername(i, (Addr *) & amp;clientaddr, & amp;clientlen) ) perror("getpeername"); printf("[%s:%d] disconnected\ ", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); FD_CLR(i, & amp;set); } } } } } return 0; }
socket.c
#include "net.h" void Argment(int argc, char *argv[]){ if(argc < 3){ fprintf(stderr, "%s<addr><port>\ ", argv[0]); exit(0); } } int CreateSocket(char *argv[]){ /*Create socket*/ int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) ErrExit("socket"); /*Allow addresses to be reused quickly*/ int flag = 1; if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, & amp;flag, sizeof(flag) ) ) perror("setsockopt"); /*Set communication structure*/ Addr_in addr; bzero( & amp;addr, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_port = htons( atoi(argv[2]) ); /*Bind communication structure*/ if( bind(fd, (Addr *) & amp;addr, sizeof(Addr_in) ) ) ErrExit("bind"); /*Set the socket to listening mode*/ if( listen(fd, BACKLOG) ) ErrExit("listen"); return fd; } int DataHandle(int fd){ char buf[BUFSIZ] = {}; Addr_in peeraddr; socklen_t peerlen = sizeof(Addr_in); if( getpeername(fd, (Addr *) & amp;peeraddr, & amp;peerlen) ) perror("getpeername"); int ret = recv(fd, buf, BUFSIZ, 0); if(ret < 0) perror("recv"); if(ret>0){ printf("[%s:%d]data: %s\ ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf); } return ret; }
net.h
#ifndef _NET_H_ #define _NET_H_ #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <unistd.h> #include <strings.h> #include <errno.h> typedef struct sockaddr Addr; typedef struct sockaddr_in Addr_in; #define BACKLOG 5 #define ErrExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while(0) void Argment(int argc, char *argv[]); int CreateSocket(char *argv[]); int DataHandle(int fd); #endif
Explanation:
This code is a simple web server program implemented using the select
function. It can handle multiple client connections simultaneously.
The program mainly includes the following parts:
-
Create and set up a socket in listening mode:
fd = CreateSocket(argv);
The
CreateSocket
function is called here to create a socket and make some necessary settings to put it in listening mode. -
Use the
select
function to listen for readable events on sockets and client connections:tmpset = set; if ((ret = select(MAX_SOCK_FD, & amp;tmpset, NULL, NULL, NULL)) < 0) ErrExit("select");
The
select
function blocks the program until there is a socket or client connection to read from, or an error occurs. In this code, theselect
function is used to wait for the readable event of the socketfd
and the client connection.MAX_SOCK_FD
is the maximum file descriptor value plus 1.tmpset
is a temporary file descriptor set used to store file descriptors where readable events occur. -
Handle socket readable events and client connection readable events:
if (FD_ISSET(fd, & amp;tmpset)) { // Accept client connection // Add new client file descriptor to set } else { // Process client data // Disconnect and remove file descriptor from set}
If the socket
fd
is readable, it means that a new client connection request has arrived. The program will call theaccept
function to accept the connection and generate a new file descriptornewfd
, and then add the file descriptor toset
for subsequent processing.If the socket is not readable, data has arrived from the connected client. The program will iterate through the file descriptors between
fd + 1
andMAX_SOCK_FD - 1
to check whether they are readable intmpset
. If it is readable, call theDataHandle
function to process the data. If the processing result is less than or equal to 0, it means the connection is disconnected. The program will output the disconnection information and remove the file descriptor fromset
. -
Loop through the above steps to continue processing client connections and data.
This code shows a simple network server program framework that uses the select
function to achieve efficient event-driven concurrent processing. Specific business logic needs to be implemented based on actual needs.
Additional:
The getpeername
function is used to obtain the address information of the remote connection associated with the socket. Its function prototype is as follows:
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd
is the socket file descriptor that represents the socket associated with the remote connection.addr
is a pointer to thestruct sockaddr
structure, which is used to receive the address information of the remote connection.addrlen
is a pointer to typesocklen_t
, used to pass the length ofaddr
, and will be updated to the actual address after the function call is completed Structure length.
This function returns 0 if the call is successful, -1 if it fails, and sets the corresponding error code.
The same usage getsocketname is to get the local one.
2 Exercise
Implement the I/O multiplexing server code using the select function and communicate with it using the nc command
#include <stdio.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #define MAX_SOCK_FD 1024 #define BACKLOG 5 #define ErrExit(msg) do{perror(msg); exit(EXIT_FAILURE);} while(0) int DataHandle(int fd) { char buf[BUFSIZ] = {}; int ret; struct sockaddr_in peeraddr; socklen_t peerlen = sizeof(struct sockaddr_in); if(getpeername(fd, (struct sockaddr *) & amp;peeraddr, & amp;peerlen) ) perror("getpeername"); \t ret = recv(fd, buf, BUFSIZ, 0); if(ret < 0) { perror("recv"); } if(ret>0) { printf("[%s:%d]data: %s\ ", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port), buf); } return ret; } int main(int argc,char *argv[]) { int fd, new_fd, i,ret; fd_set set, tmpset; struct sockaddr_in addr, client_addr; socklen_t clientlen = sizeof(client_addr); \t int flag = 1; if(argc < 3) { printf("%s <addr> <port>\ ",argv[0]); exit(0); } //create socket fd = socket(AF_INET, SOCK_STREAM, 0); if(fd < 0) { ErrExit("socket"); } //avoids the error of ports being occupied if( setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, & amp;flag, sizeof(flag) ) ) { perror("setsockopt"); } //init struct sockaddr_in memset( & amp;addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons( atoi(argv[2])); if (inet_aton(argv[1], & amp;addr.sin_addr) == 0) { printf("Invalid address\ "); exit(EXIT_FAILURE); } \t \t \t //bind if(bind(fd, (struct sockaddr *) & amp;addr, sizeof(struct sockaddr_in)) == -1) { ErrExit("bind"); } //listen if(listen(fd, BACKLOG) == -1) { ErrExit("listen"); } //select FD_ZERO( & amp;set); FD_ZERO( & amp;tmpset); FD_SET(fd, & amp;set); while(1) { tmpset = set; if((ret = select(MAX_SOCK_FD, & amp;tmpset,NULL,NULL,NULL)) < 0) { ErrExit("select"); } if(FD_ISSET(fd, & amp;tmpset) > 0) { new_fd = accept(fd,(struct sockaddr *) & amp;client_addr, & amp;clientlen); if(new_fd < 0) { perror("accept"); } printf("[%s:%d]connected\ ",inet_ntoa(client_addr.sin_addr),\ ntohs(client_addr.sin_port)); FD_SET(new_fd, & amp;set); } else //if(FF_ISSET(fd, & amp;tmpset < 0) //handle client { for(i = fd + 1; i < MAX_SOCK_FD; i + + ) { // can read if(FD_ISSET(i, & amp;tmpset)) { if(DataHandle(i) <= 0) { if(getpeername(i,(struct sockaddr *) & amp;client_addr, & amp;clientlen)) perror("getpeername"); printf("[%s:%d]disconnected\ ",inet_ntoa(client_addr.sin_addr),\ ntohs(client_addr.sin_port)); FD_CLR(i, & amp;set); } } } } } return 0; }
The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge. CS entry skill treeLinux introductionFirst introduction to Linux37215 people are learning the system