`
raojl
  • 浏览: 208946 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

linux socket Select(笔记)

阅读更多
select系统调用是用来让我们的程序监视多个文件句柄(file descrīptor)的状态变化的。程序会停在select这里等待,直到被监视的文件句柄有某一个或多个发生了状态改变。

文件在句柄在Linux里很多,如果你man某个函数,在函数返回值部分说到成功后有一个文件句柄被创建的都是的,如man socket可以看到“On success, a file descrīptor for the new socket is returned.”而man 2 open可以看到“open() and creat() return the new file descrīptor”,其实文件句柄就是一个整数,看socket函数的声明就明白了:
int socket(int domain, int type, int protocol);
当然,我们最熟悉的句柄是0、1、2三个,0是标准输入,1是标准输出,2是标准错误输出。0、1、2是整数表示的,对应的FILE *结构的表示就是stdin、stdout、stderr,0就是stdin,1就是stdout,2就是stderr。
比如下面这两段代码都是从标准输入读入9个字节字符:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char ** argv)
{
        char buf[10] = "";
        read(0, buf, 9); /* 从标准输入 0 读入字符 */
        fprintf(stdout, "%s\n", buf); /* 向标准输出 stdout 写字符 */
        return 0;
}
/* **上面和下面的代码都可以用来从标准输入读用户输入的9个字符** */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char ** argv)
{
        char buf[10] = "";
        fread(buf, 9, 1, stdin); /* 从标准输入 stdin 读入字符 */
        write(1, buf, strlen(buf));
        return 0;
}
继续上面说的select,就是用来监视某个或某些句柄的状态变化的。select函数原型如下:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
函数的最后一个参数timeout显然是一个超时时间值,其类型是struct timeval *,即一个struct timeval结构的变量的指针,所以我们在程序里要申明一个struct timeval tv;然后把变量tv的地址&tv传递给select函数。struct timeval结构如下:

struct timeval {
             long    tv_sec;         /* seconds */
             long    tv_usec;        /* microseconds */
         };
第2、3、4三个参数是一样的类型: fd_set *,即我们在程序里要申明几个fd_set类型的变量,比如rdfds, wtfds, exfds,然后把这个变量的地址&rdfds, &wtfds, &exfds 传递给select函数。这三个参数都是一个句柄的集合,第一个rdfds是用来保存这样的句柄的:当句柄的状态变成可读的时系统就会告诉select函数返回,同理第二个wtfds是指有句柄状态变成可写的时系统就会告诉select函数返回,同理第三个参数exfds是特殊情况,即句柄上有特殊情况发生时系统会告诉select函数返回。特殊情况比如对方通过一个socket句柄发来了紧急数据。如果我们程序里只想检测某个socket是否有数据可读,我们可以这样:
fd_set rdfds; /* 先申明一个 fd_set 集合来保存我们要检测的 socket句柄 */
struct timeval tv; /* 申明一个时间变量来保存时间 */
int ret; /* 保存返回值 */
FD_ZERO(&rdfds); /* 用select函数之前先把集合清零 */
FD_SET(socket, &rdfds); /* 把要检测的句柄socket加入到集合里 */
tv.tv_sec = 1;
tv.tv_usec = 500; /* 设置select等待的最大时间为1秒加500毫秒 */
ret = select(socket + 1, &rdfds, NULL, NULL, &tv); /* 检测我们上面设置到集合rdfds里的句柄是否有可读信息 */
if(ret < 0) perror("select");/* 这说明select函数出错 */
else if(ret == 0) printf("超时\n"); /* 说明在我们设定的时间值1秒加500毫秒的时间内,socket的状态没有发生变化 */
else { /* 说明等待时间还未到1秒加500毫秒,socket的状态发生了变化 */
    printf("ret=%d\n", ret); /* ret这个返回值记录了发生状态变化的句柄的数目,由于我们只监视了socket这一个句柄,所以这里一定ret=1,如果同时有多个句柄发生变化返回的就是句柄的总和了 */
    /* 这里我们就应该从socket这个句柄里读取数据了,因为select函数已经告诉我们这个句柄里有数据可读 */
    if(FD_ISSET(socket, &rdfds)) { /* 先判断一下socket这外被监视的句柄是否真的变成可读的了 */
        /* 读取socket句柄里的数据 */
        recv(...);
    }
}
注意select函数的第一个参数,是所有加入集合的句柄值的最大那个值还要加1。比如我们创建了3个句柄:

int sa, sb, sc;
sa = socket(...); /* 分别创建3个句柄并连接到服务器上 */
connect(sa,...);
sb = socket(...);
connect(sb,...);
sc = socket(...);
connect(sc,...);

FD_SET(sa, &rdfds);/* 分别把3个句柄加入读监视集合里去 */
FD_SET(sb, &rdfds);
FD_SET(sc, &rdfds);
在使用select函数之前,一定要找到3个句柄中的最大值是哪个,我们一般定义一个变量来保存最大值,取得最大socket值如下:
int maxfd = 0;
if(sa > maxfd) maxfd = sa;
if(sb > maxfd) maxfd = sb;
if(sc > maxfd) maxfd = sc;
然后调用select函数:
ret = select(maxfd + 1, &rdfds, NULL, NULL, &tv); /* 注意是最大值还要加1 */
同样的道理,如果我们要检测用户是否按了键盘进行输入,我们就应该把标准输入0这个句柄放到select里来检测,如下:
FD_ZERO(&rdfds);
FD_SET(0, &rdfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = select(1, &rdfds, NULL, NULL, &tv); /* 注意是最大值还要加1 */
if(ret < 0) perror("select");/* 出错 */
else if(ret == 0) printf("超时\n"); /* 在我们设定的时间tv内,用户没有按键盘 */
else { /* 用户有按键盘,要读取用户的输入 */
    scanf("%s", buf);

分享到:
评论

相关推荐

    Linux网络编程笔记(修订版)

    ### Linux网络编程笔记(修订版) #### 基本概念 网络编程是在计算机网络中实现数据交换和资源共享的重要技术手段。对于Linux环境下的网络编程,理解基础概念是至关重要的第一步。 1. **OSI参考模型**:开放系统...

    Linux网络编程socket编程学习

    自学Linux网络编程关于socket的编写,包括 server.c 和 client.c 的编写;很详细的介绍了网络套接字socket的C/S模型TCP协议的服务器端和客户端的程序函数以及编写过程;重点介绍多路I/O转接服务器的实现,包括select...

    Linux网络编程详细笔记

    Linux通过提供套接字(Socket)接口支持网络编程,这些函数可以帮助开发者建立网络连接并进行数据交换。 **2.1 Socket 函数** - **语法**:`int socket(int domain, int type, int protocol);` - **参数说明**: - ...

    linux系统编程及网络编程笔记

    在Linux中,使用socket API是实现网络通信的主要方式,包括创建套接字(socket函数)、绑定地址(bind函数)、监听连接(listen函数)、接受连接(accept函数)以及数据的发送和接收(send和recv函数)。同时,熟悉...

    Linux系统编程学习笔记

    ### Linux系统编程学习笔记 #### 一、IO **1.1 标准I/O (stdio)** - **fopen/fclose**: `fopen` 用于打开或创建一个文件,并返回一个指向该文件的 `FILE *` 类型的指针。`fclose` 用于关闭由 `FILE *` 指向的文件...

    网络编程课堂笔记

    本篇笔记主要围绕Unix系统下的TCP/IP分层协议模型以及如何在Linux环境中进行socket套接字编程展开,同时也涉及到了进程间通信(IPC)的相关知识。 首先,TCP/IP协议族是互联网的基础,它将复杂的网络通信过程分解为...

    达内JAVA培训综合笔记

    Java技术基础部分主要涵盖了Java开发环境的搭建、开发工具的使用以及Linux系统的基本命令和相关知识。在这部分中,首先了解了Java这门编程语言的核心特点,包括跨平台性、面向对象等。随后,讲解了Java开发环境的...

    Linux 初级学习的基本概念和资料

    提供的压缩包文件包含一系列关于Linux系统编程的学习笔记,如线程、信号、文件I/O、进程管理、内存管理、进程间通信等主题,这些都是Linux初级学习的关键点。通过阅读这些笔记,可以深入理解Linux底层机制,为后续的...

    LinuxUNIX系统编程手册

    4. **套接字网络编程**:涵盖TCP/IP和UDP协议,讲解socket()、bind()、listen()、accept()、connect()、send()、recv()等函数,以及多路复用I/O模型如select()、poll()和epoll()。 5. **内存管理**:理解动态内存...

    Linux系统TCP/IP编程

    关于Linux下的函数使用笔记,可能会涵盖更多细节,如地址结构(sockaddr_in)的使用、端口复用(SO_REUSEADDR)、套接字选项(如TCP_NODELAY,禁用Nagle算法)等。 总结一下,Linux系统TCP/IP编程涉及创建和管理套...

    Linux C 系统编程

    `socket`, `bind`, `listen`, `accept`, `connect`, `send`, `recv`等函数用于创建、连接和交换数据。TCP和UDP是两种常见的传输协议,理解它们的工作原理和差异是必要的。 6. **系统调用**: C语言通过系统调用来...

    LINUX环境高级编程整理

    本笔记主要涵盖了三个关键领域:文件系统操作、进程管理和高级IO(输入/输出)。 首先,让我们深入探讨文件系统操作。在Linux中,一切皆为文件,包括硬件设备。文件系统是组织这些文件的方式,它定义了如何存储、...

    Linux网络编程

    5. **并发处理**:Linux提供了select、poll和epoll等I/O多路复用技术,用于高效地管理多个套接字的读写事件,减少阻塞等待,提升系统资源利用率。 6. **错误处理**:网络编程中,错误处理至关重要,如超时、连接...

    linux_wlbc(jb51.net).rar

    另一个文件“jb51.net.txt”可能包含了更多关于Linux网络编程的学习资源链接、笔记或其他辅助材料,帮助读者扩展学习和实践。 通过这份资料包,读者可以系统地学习Linux网络编程,理解网络服务的工作原理,掌握高...

    20121021_libevent笔记1

    Libevent是一个基于事件触发的网络库,适用于Windows、Linux、BSD等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。下面是对Libevent的详细介绍和解释: 1. 事件驱动,高性能:Libevent采用事件...

    linux-note:《 LinuxUnix系统编程手册》笔记

    掌握`socket()`, `bind()`, `listen()`, `accept()`, `connect()`, `send()`, `recv()`等函数的用法,以及套接字选项、TCP连接管理、UDP广播和多路复用I/O(select, poll, epoll)。 5. **系统调用与库函数**:区别...

    课堂笔记_c++应用1

    在Linux中,由于所有设备被视为文件,因此可以用`read/write`来读写socket数据,但在某些场景下,`recv/send`提供了更高级的功能和控制。 2.2 IO复用机制 IO复用允许单个进程监视多个文件描述符,等待数据就绪后再...

    多路复用的课堂笔记1

    3. `epoll`:Linux内核为了解决`select`和`poll`的局限性而引入了`epoll`。`epoll`使用`epoll_create`创建一个epoll实例,通过`epoll_ctl`添加、删除或修改FD及其关联的事件。`epoll_wait`函数会阻塞,直到有事件...

    java学习笔记

    3. Linux命令与相关知识:Linux操作系统基础,常用的命令及操作。 4. Eclipse/Myeclipse程序结构:Eclipse和Myeclipse是常见的Java开发IDE,需要熟悉其项目结构和开发功能。 二、Java语言基础 1. 基础语言要素:...

Global site tag (gtag.js) - Google Analytics