【linux高级程序设计】(第十五章)UDP网络编程应用 4

socket信号驱动

为了使一个套接字能够使用信号驱动I/O,至少需要以下3步操作。

  • 1.安装SIGIO信号
  • 2.套接字的拥有者设定为当前进程。因为SIGIO信号只会送到socket拥有者进程. 通过fcntl的F_SETOWN
  • 3.套接字必须被允许使用异步I/O。 通过fcntl的F_SETFL,设置为O_ASYNC

在UDP通信中,下面情况会产生SIGIO信号

在TCP通信中,下面情况会产生SIGIO信号

例子:

下面的代码好奇怪,说是UDP的,但是发送接收用的是send, recv 而且客户端还跟服务器连接了;说是TCP,但是socket建立的时候用的是SOCK_DGRAM.

而且代码是可以跑通的。

服务器:

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<netdb.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/ioctl.h>
#define MAX_LENTH 1500

//针对SIGIO信号处理
static int nqueue = 0;
void sigio_handler(int signum)
{
    if(signum == SIGIO)
        nqueue++;
    printf("signum = %d, nqueue = %d\n", signum, nqueue); //打印信号值
    return;
}
static recv_buf[MAX_LENTH];

int main(int argc, char *argv[])
{
    int sockfd, on = 1;
    struct sigaction action;
    sigset_t newmask, oldmask;
    struct sockaddr_in ser_addr;
    if(argc != 3)
    {
        printf("use: %s ip_add port\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;  //使用IPv4
    ser_addr.sin_port = htons(atoi(argv[2]));
    if(inet_aton(argv[1], (struct in_addr *)&ser_addr.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    //创建socket
    if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    //绑定IP地址
    if(bind(sockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)) == -1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    memset(&action, 0, sizeof(action));
    action.sa_handler = sigio_handler;
    action.sa_flags = 0;
    //安装信号
    sigaction(SIGIO, &action, NULL);
    //设置socket拥有者
    if(fcntl(sockfd, F_SETOWN, getpid()) == -1)
    {
        perror("fcntl F_SETOWN");
        exit(EXIT_FAILURE);
    }
    //设置socket为信号驱动型
    if(ioctl(sockfd, FIOASYNC, &on) == -1)
    {
        perror("ioctl FIOASYNC");
        exit(EXIT_FAILURE);
    }
    sigemptyset(&oldmask);
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGIO);
    printf("get ready\n");
    while(1)
    {
        int len;
        //设置当前阻塞的信号
        sigprocmask(SIG_BLOCK, &newmask, &oldmask);
        //等待信号
        while(nqueue == 0)
            sigsuspend(&oldmask);
        memset(recv_buf, ‘\0‘, MAX_LENTH);
        //非阻塞接收数据
        len = recv(sockfd, recv_buf, MAX_LENTH, MSG_DONTWAIT);
        if(len == -1 && errno == EAGAIN)
            nqueue = 0;
        //修改进程阻塞的信号
        sigprocmask(SIG_SETMASK, &oldmask, NULL);
        if(len >= 0)
            printf("recv %d byte, msg is %s\n", len, recv_buf);
    }

}

客户端

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<netinet/tcp.h>
#include<sys/socket.h>
#include<sys/wait.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/time.h>
#include<netdb.h>
#include<fcntl.h>
#include<signal.h>
#include<sys/ioctl.h>
#define MAX_LENTH 1500

int main(int argc, char *argv[])
{
    struct sockaddr_in addr;
    int sock_fd, ret;
    char snd_buf[MAX_LENTH];
    if(argc != 3)  //参数需要服务器的IP和端口
    {
        printf("use: %s ip_add port\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    if(inet_aton(argv[1], (struct in_addr *)&addr.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(EXIT_FAILURE);
    }
    addr.sin_port = htons(atoi(argv[2]));
    //创建socket
    if((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    //向服务器发起连接 ??这不是TCP的么
    if(ret = connect(sock_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
    {
        perror("connect");
        exit(EXIT_FAILURE);
    }
    while(1)
    {
        printf("input msg to send:");
        memset(snd_buf, ‘\0‘, MAX_LENTH);
        fgets(snd_buf, MAX_LENTH - 1, stdin);
        write(sock_fd, snd_buf, MAX_LENTH - 1);
    }
}

时间: 2024-08-03 04:52:43

【linux高级程序设计】(第十五章)UDP网络编程应用 4的相关文章

读书笔记 - js高级程序设计 - 第十五章 使用Canvas绘图

读书笔记 - js高级程序设计 - 第十三章 事件 canvas 具备绘图能力的2D上下文 及文本API 很多浏览器对WebGL的3D上下文支持还不够好 有时候即使浏览器支持,操作系统如果缺缺乏必要的绘图驱动程序,则浏览器即使支持了也没用   <canvas> var drawing = document.getElementById("drawing"); if( drawing.getContext ){ drawing.getContext("2d"

javascript高级程序设计 第十四章--表单脚本

javascript高级程序设计 第十四章--表单脚本 在HTML中表单由<form>元素表示,在js中表单对应的是HTMLFormElement类型,这个类型也有很多属性和方法:取得表单元素的引用还是为它添加id特性,用DOM操作来获取表单元素:提交表单:把<input>或<button>元素的type特性设置为"submit",图像按钮把<input>元素的type特性设置为"image",也可以调用submit(

《javascript高级程序设计》第五章知识点总结

第五章知识点总结 1.object类型 访问对象的方法:①点表示法        (people.name) :      ②方括号表示法         (people[name]). 常用方法:hasOwnProperty()         用于检查给定属性在当前对象实例中是否存在 isPrototypeOf()              用于检测传入的对象是否传入对象原型 toString()                        返回对象的字符串表示 valueOf()    

第十五章、并发编程之线程

目录 第十五章.并发编程之线程 1.什么是线程 2. 进程和线程的区别 3. 开启线程的两种方式 函数开启 类开启 4.子线程与子进程创建速度 5.子线程共享数据的证明 6.线程的join方法 单个子线程 多个子线程 思考 7.了解进程的join 8. 线程的其他相关用法 第十五章.并发编程之线程 1.什么是线程 纠正概念:进程其实不是个执行单位,进程是一个资源单位,每个进程内自带一个线程,线程才是cpu上的执行单位 抽象理解: 进程是指在系统中正在运行的一个应用程序:线程是系统分配处理器时间资

Java学习笔记—第十二章 Java网络编程入门

第十二章  Java网络编程入门 Java提供的三大类网络功能: (1)URL和URLConnection:三大类中最高级的一种,通过URL网络资源表达方式,可以很容易确定网络上数据的位置.利用URL的表示和建立,Java程序可以直接读入网络上所放的数据,或把自己的数据传送到网络的另一端. (2)Socket:又称"套接字",用于描述IP地址和端口(在Internet中,网络中的每台主机都有一个唯一的IP地址,而每台主机又通过提供多个不同端口来提供多种服务).在客户/服务器网络中,当客

Python之路(十五):网络编程(上)

socket编程 本篇介绍socket是基于什么来的,为什么要知道互联网底层实现通信的原理 一.客户端/服务端架构 即C/S架构,包括 1.硬件C/S架构(打印机) 2.软件C/S架构(web服务) 美好的愿望: 最常用的软件服务器是 Web 服务器.一台机器里放一些网页或 Web 应用程序,然后启动 服务.这样的服务器的任务就是接受客户的请求,把网页发给客户(如用户计算机上的浏览器),然 后等待下一个客户请求.这些服务启动后的目标就是"永远运行下去".虽然它们不可能实现这样的 目标,

java学习笔记(十五)java网络编程

OSI模型分为七层(从下到上):物理层.数据链路层.网络层.传输层.会话层.表示层.应用层. 不同主机之间的相同层次称为对等层.对等层之间互相通信需要遵守一定的规则,称之为协议,我们将某个主机上运行的某种协议的集合称为协议栈.主机正是利用这个协议栈来接收和发送数据的. TCP/IP模型:网络接口层.网络互连层.传输层.应用层. 在网络编程中需要注意的问题包括: 1.是如何找到网络上的主机上的要进行通讯的程序: 2.是找到了主机上的程序后如何传输数据. 端口号:是一组16位的无符号二进制数,每个端

鸟哥的Linux私房菜——第十五章:正规表示法

视频链接 土豆: B站: 本章讲的是 目录如下 1. 前言:2. 基础正规表示法:2.1 以 grep 撷取字符串 (grep -iv  i是忽略大小写,v是反向选择显示没有搜寻字符串的 '搜寻字符串' filename)2.2 重要特殊字符(characters) ([] ^ $ *)3. 延伸正规表示法: (+ ? | () )4. 格式化打印: printf ()5. sed 工具简介 (sed '1a ..'   a新增  d删除  c取代)6. awk 工具简介 (awk  NF每一行

《JAVASCRIPT高级程序设计》第五章(1)

引用类型是一种将数据和功能组合到一起的数据结构,它与类相似,但是是不同的概念:ECMAScript虽然是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和结构等基本结构.引用类型也被称为“对象定义”. 一.Object类型 创建实例方式: 1使用new操作符+构造函数 1 var person = new Object(); 2 person.name = "Lillian"; 3 person.age = 29; 2使用对象字面量表示法 2.1属性不加引号 1 var pe

【linux高级程序设计】(第九章)进程间通信-管道 3

有名管道 无名管道和有名管道: 1. 管道是特殊类型的文件,在满足先入先出的原则写可以读写,不能定位读写位置. 2.管道是单向的. 3.无名管道阻塞于读写位置,而有名管道阻塞在创建位置. 4.无名管道一般只用于亲缘关系进程间通信:有名管道以磁盘文件的方式存在,可以实现本机任意两进程间通信. shell创建有名管道 mknod 管道名 p  //创建名为PIPETEST的有名管道 mknod为命令 p是参数,表示有名管道 指令 > 管道名 &   //将指令结果输入到到管道文件中 指令 <