首先需要了解的是select函数:

select函数

#include<sys/select.h>

#include<sys/time.h>

int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval * timeout);

返回:就绪描述字的正数目,0——超时,-1——出错

参数解释:

maxfd: 最大的文件描述符(其值应该为最大的文件描述符字 + 1)

readset: 内核读操作的描述符字集合

writeset:内核写操作的描述符字集合

exceptionset:内核异常操作的描述符字集合

timeout:等待描述符就绪需要多少时间。NULL代表永远等下去,一个固定值代表等待固定时间,0代表根本不等待,检查描述字之后立即返回。

注意:readset,writeset,exceptionset都是值-结果参数,意思就是他们传进入指针进去,函数根据指针可以修改对应的fd_set

fd_set集合操作

fd_set和名字一样,是一个描述符的集合。有下面几个操作:

void FD_ZERO(fd_set *fdset); /* 将所有fd清零 */

void FD_SET(int fd, fd_set *fdset); /* 增加一个fd */

void FD_CLR(int fd, fd_set *fdset); /* 删除一个fd */

int FD_ISSET(int fd, fd_set *fdset); /* 判断一个fd是否有设置 */

我们现在要做一个select使用的server,server监听两个端口(7778和7779)的socket。再使用两个cli,一个client连接到7778端口,另一个client连接到7779端口。

服务器端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
 
int main(int argc, char *argv[])
{
    //这个服务器同时监听7777和7778两个端口
     
    //绑定监听7779端口的fd
    int listenfd1;
    struct sockaddr_in serv_addr1;
    listenfd1 = socket(AF_INET, SOCK_STREAM, 0);
     
    bzero((char *) &serv_addr1, sizeof(serv_addr1));
    serv_addr1.sin_family = AF_INET;
    serv_addr1.sin_port = htons(7777);
    serv_addr1.sin_addr.s_addr = INADDR_ANY;
     
    bind(listenfd1, (struct sockaddr *) &serv_addr1, sizeof(serv_addr1));
    listen(listenfd1, 5);
     
    //绑定监听7778端口的fd
    int listenfd2;
    struct sockaddr_in serv_addr2;
    listenfd2 = socket(AF_INET, SOCK_STREAM, 0);
     
    bzero((char *) &serv_addr2, sizeof(serv_addr2));
    serv_addr2.sin_family = AF_INET;
    serv_addr2.sin_port = htons(7778);
    serv_addr2.sin_addr.s_addr = INADDR_ANY;
     
    bind(listenfd2, (struct sockaddr *) &serv_addr2, sizeof(serv_addr2));
    listen(listenfd2, 5);
     
     
    int maxfd;
    //为什么这里设置两个fd_set?每次select的时候函数会把没有事件发生的描述字清零,所以需要两个集合
    fd_set allset, rset;
    maxfd = listenfd1;
    if(listenfd2 > maxfd) {
        maxfd = listenfd2;
    }
     
    FD_ZERO(&allset);
    FD_SET(listenfd1, &allset);
    FD_SET(listenfd2, &allset);
     
    int clifd, clilen;
    struct sockaddr_in cli_addr;
    char buffer[256];
    for(;;) {
        rset = allset;
        select(maxfd + 1, &rset, NULL, NULL, NULL);
         
        //如果是listenfd1 获取消息
        if(FD_ISSET(listenfd1, &rset)) {
            clilen = sizeof(cli_addr);
            clifd = accept(listenfd1, (struct sockaddr *) &cli_addr, &clilen);
             
            bzero(buffer, 256);
            read(clifd, buffer, 255);
            printf("Listenfd1 Message is:%s\r\n", buffer);
        }
         
        //如果是listenfd1 获取消息
        if(FD_ISSET(listenfd2, &rset)) {
            clilen = sizeof(cli_addr);
            clifd = accept(listenfd2, (struct sockaddr *) &cli_addr, &clilen);
             
            bzero(buffer, 256);
            read(clifd, buffer, 255);
            printf("Listenfd2 Message is:%s\r\n", buffer);
        }
        close(clifd);
    }
     
    close(listenfd1);
    close(listenfd2);
 
    return 0;
}

客户端1 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
 
int main(int argc, char* argv[])
{
    int socketfd, n;
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
     
    struct sockaddr_in serv_addr;
         
    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(7778);
         
    connect(socketfd,(struct sockaddr *)  &serv_addr, sizeof(serv_addr));
         
    write(socketfd, "client message", 14);
    return 0;
 
}

客户端2代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
 
int main(int argc, char* argv[])
{
    int socketfd, n;
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
     
    struct sockaddr_in serv_addr;
         
    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(7779);
         
    connect(socketfd,(struct sockaddr *)  &serv_addr, sizeof(serv_addr));
         
    write(socketfd, "client message", 14);
    return 0;
 
}

调用步骤:

1 启动服务器端

2 启动客户端1

3 启动客户端2

4 服务器端表

客户端启动:

服务端表现:

这里就是使用select函数对多个socket进行读监听

unix下网络编程之I/O复用(四)的更多相关文章

  1. unix下网络编程之I/O复用(三)

    poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...

  2. unix下网络编程之I/O复用(一)

    什么是I/O复用? What we need is the capability to tell the kernel that we want to be notified if one or mo ...

  3. unix下网络编程之I/O复用(五)

    前言 本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下: 客 ...

  4. unix下网络编程之I/O复用(二)

    select函数 该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或是多个事件发生或经历一段指定的时间后才唤醒它.我们调用select告知内核对哪些描述字(就读.写或异常条件)感兴趣以 ...

  5. TCP/IP网络编程之I/O复用

    基于I/O复用的服务端 在前面章节的学习中,我们看到了当有新的客户端请求时,服务端进程会创建一个子进程,用于处理和客户端的连接和处理客户端的请求.这是一种并发处理客户端请求的方案,但并不是一个很好的方 ...

  6. linux网络编程之socket编程(十四)

    经过令国鸡冻的APEC会之后,北京的冬天终于不冷了,有暖气的日子就是倍儿爽呀~~洗完热水澡,舒服的躺在床上欢乐地敲打着键盘,是件多么幸福的事呀,好了,抒发情感后,正题继续. 上节中已经初步学习了UDP ...

  7. python3网络编程之socketserver

    本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...

  8. GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

  9. 网络编程之socket

    网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...

随机推荐

  1. ORA-01034和ORA-27101的错误

    我本机安装的数据库版本是ORACLE 11G R2,用plsql连接时候,报ora-12514如下错误: 但是在cmd里用sqlplus连接已经创建的用户时候,报如下错误: ORA-01034 - O ...

  2. 20145230java实验报告1

    20145230实验一JAVA开发环境熟悉 使用JDK编译.运行简单的JAVA程序 实验要求 •使用JDK编译.运行简单的Java程序: •使用IDEA 编辑.编译.运行.调试Java程序. 实验内容 ...

  3. 线性代数:Ax=b的解

    n列的矩阵A,当且仅当向量b是列空间C(A)的一个向量时,Ax=b有解. C(A)的零空间是N(A),N(A)正交补是A的行空间C(T(A)), 依据上一章的结论,任何Rn向量可以表示为r+n,其中n ...

  4. MySQL数据库基本操作(二)

    表结构操作 ( ALTER TABLE) 添加单列: ALTER TABLE tb1_name ADD [COLUNM] col_name column_definition [FIRST|AFTER ...

  5. Eclipse Task的使用

    参考链接:http://blog.csdn.net/limb99/article/details/8881891; http://hi.baidu.com/jinxv1987/item/64496f6 ...

  6. 如何隐藏tomcat命令窗口

    有两种方法: 一.修改tomcat中的文件参数,达到隐藏目的: 引用:TOMCAT_HOME\bin\setclasspath.bat 在文件的底部找到以下内容: set _RUNJAVA=" ...

  7. 一言(ヒトコト)Hitokoto API

    『想要成为无论多么悲伤的时候,也能够漂亮微笑的人吧.』 Hitokoto API 更新:2014.02.22 问题/反馈:api # hitokoto.us 数据获取:[ 数据获取 ] 调用举例:[  ...

  8. 【转】Android应用中使用AsyncHttpClient来异步网络数据

    摘要: 首先下载AsyncHttpClient的库文件,可以自行搜索,可以到下面地址下载 http://download.csdn.net/detail/xujinyang1234/5767419 测 ...

  9. tensorflow实现Minist手写体识别

    import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data #下载MINIST数据集mnist ...

  10. substr 方法

    substr 方法 返回一个从指定位置开始,并具有指定长度的子字符串. 参数 start 必选.所需的子字符串的起始位置.字符串中第一个字符的索引为 0. length 可选项.返回的子字符串中包含的 ...