柳大的Linux遊記·基礎篇(5)select IO複用機制
柳大的Linux遊記·基礎篇(5)select IO複用機制
- Author: 柳大·Poechant(鍾超)
- Blog:blog.CSDN.net/Poechant
- Email: zhongchao.ustc#gmail.com (#->@)
- Date: March 13th, 2012
- Copyright © 柳大·Poechant
1 基本原理
注:select 原理圖,摘自IBM iSeries 資訊中心。
1 資料結構與函式原型
1.1 select
- 函式原型
int select( int nfds, fd_set *readset, fd_set *writeset, fd_set* exceptset, struct timeval *timeout );
- 標頭檔案
-
select
位於:#include <sys/select.h>
-
struct timeval
位於:#include <sys/time.h>
-
- 返回值
返回對應位仍然為1的fd的總數。
- 引數
- nfds:第一個引數是:最大的檔案描述符值+1;
- readset:可讀描述符集合;
- writeset:可寫描述符集合;
- exceptset:異常描述符;
- timeout:select 的監聽時長,如果這短時間內所監聽的 socket 沒有事件發生。
1.2 fd_set
1.2.1 清空描述符集合
FD_ZERO(fd_set *)
1.2.2 向描述符集合新增指定描述符
FD_SET(int, fd_set *)
1.2.3 從描述符集合刪除指定描述符
FD_CLR(int, fd_set *)
1.2.4 檢測指定描述符是否在描述符集合中
FD_ISSET(int, fd_set *)
1.2.5 描述符最大數量
#define FD_SETSIZE 1024
1.3 描述符集合
可讀描述符集合中可讀的描述符,為1,其他為0;可寫也類似。異常描述符集合中有異常等待處理的描述符的值為1,其他為0。
1.4 ioctl
-
函式原型:
int ioctl(int handle, int cmd,[int *argdx, int argcx]);
-
標頭檔案:
#include <sys/ioctl.h>
-
返回值:
- 0 - 成功
- 1 - 失敗
2 示例
程式各部分的解釋在註釋中。
#include <sys/socket.h>
#include <string.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#define TRUE 1
#define FALSE 0
int main(int argc, char *argv[])
{
int i, len, rc, on = TRUE;
int listen_sd, new_sd = 0, max_sd;
int desc_ready;
char buffer[80];
int close_conn, end_server = FALSE;
struct sockaddr_in server_addr;
struct timeval timeout;
struct fd_set master_set, working_set;
// Listen
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sd < 0)
{
perror("socket() failed");
exit(-1);
}
// Set socket options
rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
if (rc < 0)
{
perror("setsockopt() failed");
close(listen_sd);
exit(-1);
}
// Set IO control
rc = ioctl(listen_sd, FIONBIO, (char *) &on);
if (rc < 0)
{
perror("ioctl() failed");
close(listen_sd);
exit(-1);
}
// Bind
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(atoi(argv[1]));
rc = bind(listen_sd, (struct sockaddr *) &server_addr, sizeof(server_addr));
if (rc < 0)
{
perror("bind() failed\n");
close(listen_sd);
exit(-1);
}
// Listen
rc = listen(listen_sd, 32);
if (rc < 0)
{
perror("listen() failed\n");
close(listen_sd);
exit(-1);
}
// Intialize sd set
FD_ZERO(&master_set);
max_sd = listen_sd;
FD_SET(listen_sd, &master_set);
timeout.tv_sec = 3 * 60;
timeout.tv_usec = 0;
// Start
do
{
// Copy master_set into working_set
memcpy(&working_set, &master_set, sizeof(master_set));
printf("Waiting on select()...\n");
rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);
if (rc < 0)
{
perror(" select() failed\n");
break;
}
if (rc == 0)
{
printf(" select() timed out. End program.\n");
break;
}
desc_ready = rc; // number of sds ready in working_set
// Check each sd in working_set
for (i = 0; i <= max_sd && desc_ready > 0; ++i)
{
// Check to see if this sd is ready
if (FD_ISSET(i, &working_set))
{
--desc_ready;
// Check to see if this is the listening sd
if (i == listen_sd)
{
printf(" Listeing socket is readable\n");
do
{
// Accept
new_sd = accept(listen_sd, NULL, NULL);
// Nothing to be accepted
if (new_sd < 0)
{
// All have been accepted
if (errno != EWOULDBLOCK)
{
perror(" accept() failed\n");
end_server = TRUE;
}
break;
}
// Insert new_sd into master_set
printf(" New incoming connection - %d\n", new_sd);
FD_SET(new_sd, &master_set);
if (new_sd > max_sd)
{
max_sd = new_sd;
}
}
while (new_sd != -1);
}
// This is not the listening sd
else
{
close_conn = FALSE;
printf(" Descriptor %d is avaliable\n", i);
do
{
rc = recv(i, buffer, sizeof(buffer), 0);
// Receive data on sd "i", until failure occurs
if (rc < 0)
{
// Normal failure
if (errno != EWOULDBLOCK)
{
perror(" recv() failed\n");
close_conn = TRUE;
}
break;
}
// The connection has been closed by the client
if (rc == 0)
{
printf(" Connection closed\n");
close_conn = TRUE;
break;
}
/* Receiving data succeeded and echo it back
the to client */
len = rc;
printf(" %d bytes received\n", len);
rc = send(i, buffer, len, 0);
if (rc < 0)
{
perror(" send() failed");
close_conn = TRUE;
break;
}
}
while (TRUE);
// If unknown failure occured
if (close_conn)
{
// Close the sd and remove it from master_set
close(i);
FD_CLR(i, &master_set);
// If this is the max sd
if (i == max_sd)
{
// Find the max sd in master_set now
while (FD_ISSET(max_sd, &master_set) == FALSE)
{
--max_sd;
}
} // End of if (i == max_sd)
} // End of if (close_conn)
}
}
}
}
while (end_server == FALSE);
/* Close each sd in master_set */
for (i = 0; i < max_sd; ++i)
{
if (FD_ISSET(i, &master_set))
{
close(i);
}
}
return 0;
}
參考
- http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzab6%2Frzab6xnonblock.htm
-
轉載請註明來自柳大的CSDN部落格:Blog.CSDN.net/Poechant
-
相關文章
- 柳大的Linux講義·基礎篇(4)網路程式設計基礎Linux程式設計
- 柳大的Linux講義·基礎篇(2)Linux檔案系統的inodeLinux
- 柳大的Linux講義·基礎篇(1)磁碟與檔案系統Linux
- IO多路複用機制詳解
- IO多路複用與epoll機制淺析
- 柳大的Linux講義·基礎篇(3)許可權、連結與許可權管理Linux
- 基礎 IO (Linux學習筆記)Linux筆記
- 什麼是IO多路複用?Nginx的處理機制Nginx
- Redis基礎篇(七)哨兵機制Redis
- 基礎篇:深入解析JAVA反射機制Java反射
- [pwn基礎]Linux安全機制Linux
- C IO複用select, epoll 簡單總結
- Redis使用IO多路複用進行事件處理機制Redis事件
- 【Linux】基礎IO(下)!!!Linux
- 【Linux】基礎IO(上)!!!Linux
- 第十六篇:初探IO複用
- Java基礎篇—Java類載入機制Java
- 基礎篇:深入解析JAVA註解機制Java
- RecyclerView的複用機制View
- 詳解 Android 中的 IPC 機制:基礎篇Android
- Linux基礎知識複習之命令篇Linux
- java基礎:記憶體分配機制Java記憶體
- 【轉】linux非同步io機制Linux非同步
- Linux下開發-IO複用Linux
- IO多路複用——深入淺出理解select、poll、epoll的實現
- 全面理解 Android 安全機制(Linux基礎)AndroidLinux
- Redis基礎篇(二)高效能IO模型Redis模型
- Spark IO機制Spark
- Mysql第六講 select查詢基礎篇MySql
- java基礎-複用類-複用方式(2)Java
- 深入js基礎:從記憶體機制、解析機制到執行機制(長文預警)JS記憶體
- golang中基於kevent的IO多路複用實踐Golang
- ListView的複用和快取機制View快取
- 【JavaScript筆記 · 基礎篇(十)】物件導向程式設計之三:繼承機制JavaScript筆記物件程式設計繼承
- JS基礎總結(5)—— JS執行機制與EventLoopJSOOP
- 基於滑動場景解析RecyclerView的回收複用機制原理View
- Go基礎學習記錄之反射(reflect)機制Go反射
- 第十七篇:IO複用之select實現