当前位置: 首页 > article >正文

(9)下:学习与验证 linux 里的 epoll 对象里的 EPOLLIN、 EPOLLHUP 与 EPOLLRDHUP 的不同。小例子的实验

(4)本实验代码的蓝本,是伊圣雨老师里的课本里的代码,略加改动而来的。
++以下是 服务器端的代码:

在这里插入图片描述

++ 每当收到客户端的报文时,就测试一下对应的 epoll 事件里的事件标志,不读取报文内容,所以设置为 ET 边缘触发模式。
++ 对应的代码版本 :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <fcntl.h> //增加这俩头文件
#include <errno.h>

#define EPOLL_SIZE  50

int main(int argc,char * argv[])
{   //验证 EPOLLHUP 等标志的服务器端, argc = 2
    int serv_sock, clnt_sock, str_len, i, epfd, event_cnt;
    struct sockaddr_in serv_adr, clnt_adr;
    socklen_t adr_sz;  
    struct epoll_event event, * ep_events;

    if(argc != 2) { printf("参数不是2个\n");exit(1); }

    serv_sock = socket(PF_INET,SOCK_STREAM,0);
    printf("创建了监听套接字,描述符为: %d\n",serv_sock);

    memset(&serv_adr,0,sizeof(serv_adr));
    serv_adr.sin_family = AF_INET; // 协议
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址
    serv_adr.sin_port = htons(atoi(argv[1])); //端口号

    bind( serv_sock,
        (struct sockaddr *)&serv_adr, sizeof(serv_adr) ) ;
    listen(serv_sock,5);

    epfd = epoll_create(EPOLL_SIZE); // EPOLL_SIZE = 50
    event.events  = EPOLLIN; // 监听套接字仍为水平触发模式
    event.data.fd = serv_sock;
    epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);

    ep_events = malloc(sizeof(struct epoll_event) * EPOLL_SIZE);
    while (1) // 此循环在正常情况下是不会退出的。
    {   event_cnt = epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);       
        if(-1 == event_cnt) {  break; } // 出错则结束循环,进程退出   

        puts("从 epoll_wait() 返回了");//统计epoll_wait()的返回次数
        for(i = 0 ; i < event_cnt ; i++) //依次处理所有发生了事件的套接字
        {   if(ep_events[i].data.fd == serv_sock)//监听套接字
            {   adr_sz = sizeof(clnt_adr);
                clnt_sock = accept( serv_sock,
                                (struct sockaddr *)&clnt_adr,&adr_sz );
                event.events = EPOLLIN | EPOLLHUP |EPOLLRDHUP | EPOLLET ;
                    // 通信套接字用边缘触发是因为不准备读取报文
                event.data.fd = clnt_sock; 
                epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event); 
                printf("创建了通信套接字 id: %d\n", clnt_sock);
            } else { // 依次测试通讯套接字上有事件时具有的 epoll标志。
                uint32_t revents = ep_events[i].events; 
                int fd = ep_events[i].data.fd;

                if( revents & EPOLLIN    ) 
                    printf("通信套接字 %d 上有 EPOLLIN    事件\n", fd);
                if( revents & EPOLLHUP   ) 
                    printf("通信套接字 %d 上有 EPOLLHUP   事件\n", fd);
                if( revents & EPOLLRDHUP ) 
                    printf("通信套接字 %d 上有 EPOLLRDHUP 事件\n", fd);
            }
        } // for(...)  
    } // while(...)
    
    return 0;  
}

(5) 接着给出客户端的版本,这是一个 linux 版本的客户端,很简单的小程序

在这里插入图片描述

++ 记录其源代码版本:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

int main(int argc,char * argv[])
{   // 回声客户端,三个参数,argc = 3
    int  sock, str_len;  
    struct sockaddr_in  serv_adr;
    if(argc != 3) { printf("参数不是3个\n");exit(1); }

    sock = socket(PF_INET,SOCK_STREAM,0);

    memset(&serv_adr,0,sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    // serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
    // inet_addr() 的语义不明,不好
    // 处理文本地址,只需使用 inet_pton() 与 inet_ntop() 即可。
    // int inet_pton(int af, const char *src, void *dst);
    inet_pton(AF_INET, argv[1], &serv_adr.sin_addr.s_addr);
    serv_adr.sin_port = htons(atoi(argv[2]));

    if(connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) != -1)
        puts("客户端套接字连接至服务器成功\n");

    sleep(60); // 延迟 60s 以观察实验结果

    close(sock); // 此行可注释,以验证 四次握手与 RST 报文的出现时机
    exit(0);
}

(6)后来实验中发现,客户端采用 linux 版本,会导致 wireshark 无法抓包,只好再编写 windows 版本的客户端,源代码如下:

在这里插入图片描述

++ 代码版:

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>

#pragma warning(disable : 4996) // 禁用关于 inet_addr 的过时警告

int main() 
{
	WSADATA m_wsadata; // 在 windows平台使用 socket前,须做一下初始化,
	WSAStartup(0x0202, &m_wsadata); // 最后用 WSACleanup() 释放库。

	SOCKET sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

	SOCKADDR_IN       server_in;
	memset(&server_in, 0, sizeof(SOCKADDR_IN));  //连接服务器
	server_in.sin_family = AF_INET;
	server_in.sin_port = htons(9000); // 连接至 80 端口
	server_in.sin_addr.s_addr = inet_addr("192.168.1.126");

	connect(sClient, (struct sockaddr*)&server_in, sizeof(SOCKADDR_IN));

	for (int i = 0; i < 1000000000; i++); // 延时一下

	closesocket(sClient); // 关闭套接字的函数
	WSACleanup();         // 用这个函数关闭库
	return 0; 
}

++ 使用 windows 的套接字,还要设置一下 vs2019 ,要不然上面的代码会找不到外部函数

在这里插入图片描述

++ 以及:

在这里插入图片描述

(7) 以下给出实验结果,配合 wireshark 抓包

在这里插入图片描述

(8)

谢谢


http://www.kler.cn/a/528950.html

相关文章:

  • pytorch实现循环神经网络
  • HarmonyOS NEXT:保存应用数据
  • SARIMA介绍
  • Python 列表(使用列表时避免索引错误)
  • SQL server 数据库使用整理
  • 图论——最小生成树
  • happytime
  • (即插即用模块-特征处理部分) 二十、(TPAMI 2022) Permute-MLP 置换MLP模块
  • LeetCode题练习与总结:种花问题--605
  • C基础寒假练习(6)
  • 【数据采集】案例01:基于Scrapy采集豆瓣电影Top250的详细数据
  • 解决istoreos无法拉取青龙镜像
  • Java小白入门教程:HashSet
  • ZZNUOJ(C/C++)基础练习1031——1040(详解版)
  • 【JAVA】循环语句
  • 工作中使用到的单词(软件开发)_第一、二、三版汇总
  • TensorFlow 示例摄氏度到华氏度的转换(一)
  • 作者新游戏1.0
  • Linux中 端口被占用如何解决
  • rust跨平台调用动态库
  • 设计模式Python版 组合模式
  • DRM系列六:Drm之KMS
  • 线程的状态转换和调度
  • 深入理解Spring框架:从基础到实践
  • python学opencv|读取图像(五十三)原理探索:使用cv.matchTemplate()函数实现最佳图像匹配
  • 996引擎 -地图-添加安全区