当前位置:网站首页>Implementing IO multiplexing in UNIX using poll function to realize network socket server
Implementing IO multiplexing in UNIX using poll function to realize network socket server
2022-07-23 06:01:00 【Zhang Tieniu (IOT)】
One , Preface
UNIX There are five network models , Namely : Synchronous blocking IO, Synchronous nonblocking IO, Signal driven IO, asynchronous IO And today's introduction IO Multiplexing .
that IO What is the problem of multiplexing ?
We know that UNIX Many functions under are blocked , Obstruction means IO The operation does not... Until the data is received or the result is obtained Returns the , It needs to be completely completed before returning to user space ; Suppose we now face such a problem : We need to check whether the key should be pressed in a program , At the same time, he has to read data from the serial port for processing , We also need to deal with the data on the network , If it's just ordinary use of three read Call to solve , If the key is not pressed at this time ( That is, the data is not ready )read() The system call will not return there , Even now serial port or network Collateral socket There is no way to deal with the arrival of data , We can use multithreading to solve this problem , But it does not consume memory and cpu, So today we are going to learn a little IO Under the multiplexing model poll Function !
Two , About poll
poll() System call in System V Introduction in ,poll() The mechanism of select() similar , And select() There is not much difference in nature , Managing multiple descriptors is also polling , Process according to the state of the descriptor , however poll() There is no limit on the maximum number of file descriptors ( But when the quantity is too large, the performance will also drop ).poll() and select() There is also a drawback , The array containing a large number of file descriptors is copied between the user state and the address space of the kernel , Whether these file descriptors are ready or not , Its overhead increases linearly with the number of file descriptors .
3、 ... and ,poll Function usage
The function prototype :
#include <poll.h>
struct pollfd
{
int fd; /* File descriptor */
short events; /* Waiting Events */
short revents; /* What actually happened */
} ;
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
POLLIN | POLLPRI Equivalent to select() Reading events ,POLLOUT |POLLWRBAND Equivalent to select() Write events for .POLLIN Equivalent to POLLRDNORM |POLLRDBAND, and POLLOUT Is equivalent to POLLWRNORM. for example , To monitor whether a file descriptor is readable and writable at the same time , We can set events by POLLIN |POLLOUT. stay poll return , We can check revents The logo in , Corresponding to the file descriptor request events Structure . If POLLIN Events are set , The file descriptor can be read without blocking . If POLLOUT Set up , Then the file descriptor can be written and Do not cause blockage . These flags are not mutually exclusive : They can be set at the same time , Indicates that the read and write operations of this file descriptor will return normally without blocking .
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))// The macro defines the size of each array ;
static inline void print_usage(char *progname);// Definition print_usage function ;
int socket_server_init(char *listen_ip, int listen_port);// Definition socket_server_init function ;
int main(int argc, char **argv)
{
int listenfd, connfd;
int serv_port = 0;
int daemon_run = 0;
char *progname = NULL;
int opt;
int rv;
int i, j;
int found;
int max;
char buf[1024];
struct pollfd fds_array[1024];
struct option long_options[] ={
{"daemon", no_argument, NULL, 'b'},
{"port", required_argument, NULL, 'p'},
{"help", no_argument, NULL, 'h'},
{NULL, 0, NULL, 0}
}; progname = basename(argv[0]);
/* Set the parameters to be taken by the program during compilation */
while ((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1)
{
switch (opt)
{
case 'b':
daemon_run=1;
break;
case 'p':
serv_port = atoi(optarg);
break;
case 'h': /* Get help information */
print_usage(progname);
return EXIT_SUCCESS;
default:
break;
}
}
if( !serv_port )// When the program is compiled without ports ;
{
print_usage(progname);
return -1;
}
if( (listenfd=socket_server_init(NULL, serv_port)) < 0 )// Call function socket_server_init When the failure ;
{
printf("ERROR: %s server listen on port %d failure\n", argv[0],serv_port);
return -2;
}
printf("%s server start to listen on port %d\n", argv[0],serv_port);
if( daemon_run )// Whether to throw it into the background ;
{
daemon(0, 0);
}
for(i=0; i<ARRAY_SIZE(fds_array) ; i++)// Traverse each array ;
{
fds_array[i].fd=-1;// Set the file descriptor in each array to 1;
}
fds_array[0].fd = listenfd;// take listenfd Set to... In the first array fd;
fds_array[0].events = POLLIN;// Set... In the first array events Domain events are readable with data
max = 0;
for ( ; ; )// Dead cycle ;
{
rv = poll(fds_array, max+1, -1);// call poll function , Infinite timeout ;
if(rv < 0)
{
printf("poll failure: %s\n", strerror(errno));// Error handling ;
break;
}
else if(rv == 0)
{
printf("poll get timeout\n");// timeout handler ;
continue;
}
/*rv Greater than 0 The time server has two events to handle : The first is listening for connections , The second is to operate the connected client ;*/
if (fds_array[0].revents & POLLIN)// Listening to the connection
{
if( (connfd=accept(listenfd, (struct sockaddr *)NULL, NULL)) < 0)// call accept Function accepts requests from clients ; {
printf("accept new client failure: %s\n", strerror(errno));// Error handling ;
continue;// Jump out of this cycle ;
}
found = 0;
for(i=1; i<ARRAY_SIZE(fds_array) ; i++)// Traversal array ;
{
if( fds_array[i].fd < 0 )// If this array is not occupied ;
{
printf("accept new client[%d] and add it into array\n", connfd );/
fds_array[i].fd = connfd;// take connfd Write this array ;
fds_array[i].events = POLLIN;// Set the events Domain events are readable with data ;
found = 1;
break;
}
}
if( !found )
{
printf("accept new client[%d] but full, so refuse it\n", connfd);
close(connfd);
continue;
}
max = i>max ? i : max;
if (--rv <= 0)
continue;
}
else // Operate on the connected client
{
for(i=1; i<ARRAY_SIZE(fds_array); i++)// Traversal array
{
if( fds_array[i].fd < 0 )
continue;
if( (rv=read(fds_array[i].fd, buf, sizeof(buf))) <= 0)// Read this array fd Of buf The content of ;
{
printf("socket[%d] read failure or get disconncet.\n", fds_array[i].fd);// Write failure ;
close(fds_array[i].fd);
fds_array[i].fd = -1;// And release this array ;
}
else
{
printf("socket[%d] read get %d bytes data\n", fds_array[i].fd, rv);// Print the content read from the client to standard output ;
for(j=0; j<rv; j++)// Traverse every byte read ;
buf[j]=toupper(buf[j]);// Convert lowercase to uppercase ;
if( write(fds_array[i].fd, buf, rv) < 0 )// Write this array fd Medium 1buf;
{
printf("socket[%d] write failure: %s\n", fds_array[i].fd, strerror(errno));
close(fds_array[i].fd);
fds_array[i].fd = -1;
}
}
}
}
}
CleanUp:
close(listenfd);
return 0;
}
static inline void print_usage(char *progname)
{
printf("Usage: %s [OPTION]...\n", progname);
printf(" %s is a socket server program, which used to verify client and echo back string from it\n",progname);
printf("\nMandatory arguments to long options are mandatory for short options too:\n");
printf(" -b[daemon ] set program running on background\n");
printf(" -p[port ] Socket server port address\n");
printf(" -h[help ] Display this help information\n");
printf("\nExample: %s -b -p 8900\n", progname);
return ;
}
int socket_server_init(char *listen_ip, int listen_port)
{
struct sockaddr_in servaddr;
int rv = 0;
int on = 1;
int listenfd;
if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));
return -1;
}
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(listen_port);
if( !listen_ip ) /* Listen all the local IP address */
{
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
}
else /* listen the specified IP address */
{
if (inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0)
{
printf("inet_pton() set listen IP address failure.\n");
rv = -2;
goto CleanUp;
}
}
if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
{
printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
rv = -3;
goto CleanUp;
}
if(listen(listenfd, 13) < 0)
{
printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
rv = -4;
goto CleanUp;
}
CleanUp:
if(rv<0)
close(listenfd);
else
rv = listenfd;
return rv;
}5、 ... and , Results screenshots
client :

Server side :

6、 ... and , summary
poll The most important thing in the function is to pollfd This structure Only by understanding can we adopt specific operations for your needs , Make clear C The concept of structure array in language can be found in poll It's like a fish in water .
边栏推荐
猜你喜欢
随机推荐
配置IP地址
RAID磁盘阵列
Solutions to xftp upload errors
DNS域名解析服务
Shell编程规范与变量
Transplantation de systèmes embarqués
Jmeter上传和下载文件
读刘润《底层逻辑》摘录
Information collection research report
get请求和post请求的区别
数据标注学习总结
Collections的Comparable,Comparator
Basic process of visitors
The difference between get request and post request
读《高效阅读法-最划算的自我投资》有感
洛谷回文质数 Prime Palindromes
Dom4j解析XML文件,处理来至XML的数据信息
详解虚拟机下三种联网模式
Fiddler script personalized configuration display
Test case: QQ login

![[Research Report on the contents, methods, tools and results of information collection]](/img/e2/37606fbd488e55a82c7e6174b892de.jpg)







