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

《TCP/IP网络编程》(第十二章)I/O复用(2)

下面是基于I/O复用的回声服务器端和客户端代码

1.Linux系统

服务器端代码

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> // POSIX标准定义的通用函数,如close()
#include <arpa/inet.h> // 提供inet相关的函数,如inet_addr()
#include <sys/socket.h> // 提供socket相关的函数和数据结构
#include <signal.h>
#include <sys/wait.h>#define BUFF_SIZE 100 //缓冲区大小
void error_handling(char* message);int main(int argc, char *argv[]) 
{int serv_sock; // 服务器套接字int clnt_sock; // 客户端套接字struct sockaddr_in serv_addr; // 服务器地址结构struct sockaddr_in clnt_addr; // 客户端地址结构struct timeval timeout;//超时设置fd_set reads,cpy_reads;//读集合,拷贝读集合socklen_t clnt_addr_size; // 客户端地址结构的大小int fd_max,str_len,fd_num,i;//最大文件描述符,读集合大小,读集合大小,循环次数char opinfor[BUFF_SIZE];if(argc!=2){printf("Usage : %s <port>\n", argv[0]);exit(1); }// 创建一个服务器套接字serv_sock=socket(PF_INET, SOCK_STREAM, 0);//使用SOCK_STREAM创建TCP套接字if(serv_sock==-1) error_handling("socket() error"); // 初始化服务器地址结构memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family=AF_INET; // 地址族设置为IPv4serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); // 服务器地址设置为任意serv_addr.sin_port=htons(atoi(argv[1])); // 设置监听端口为命令行参数指定的端口// 绑定套接字,调用bind()函数分配ip地址和端口号if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)error_handling("bind() error"); // 监听套接字,最多监听5个连接if(listen(serv_sock, 5)==-1) error_handling("listen() error");//设置文件描述符FD_ZERO(&reads);FD_SET(serv_sock, &reads);fd_max=serv_sock;while(1){cpy_reads=reads;//把fd_set复制到cpy_reads//设置超时timeout.tv_sec=5;timeout.tv_usec=5000;if((fd_num=select(fd_max+1,&cpy_reads,0,0,&timeout))==-1){//调用select()函数,如果有文件描述符就绪,返回就绪的文件描述符个数,错误返回-1break;}if(fd_num==0){//超时返回0continue;}for(i=0; i<=fd_max+1; i++){//遍历所有文件描述符if(FD_ISSET(i, &cpy_reads)){//判断是否在cpy_reads中if(i==serv_sock){clnt_addr_size=sizeof(clnt_addr);clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);//接受连接请求FD_SET(clnt_sock, &reads);//将新连接的客户端套接字加入到reads中//更新最大文件描述符if(fd_max<clnt_sock){fd_max=clnt_sock;printf("Connected client: %d\n", clnt_sock);}}else{str_len=read(i, opinfor, BUFF_SIZE);//读取客户端发送的数据if(str_len==0){//如果客户端关闭连接FD_CLR(i, &reads);//从reads中删除该文件描述符close(i);printf("closed client: %d\n", i);}else{write(i, opinfor, str_len);//发送数据给客户端}}}}}// 关闭服务器套接字close(serv_sock);return 0; 
}void error_handling(char *message)
{fputs(message, stderr); fputc('\n', stderr); exit(1); 
}

客户端代码
这里使用的是(第四章)基于TCP的服务器端/客户端(1)中的回声服务器客户端代码

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <arpa/inet.h> 
#include <sys/socket.h> #define BUF_SIZE 1024
void error_handling(char *message); int main(int argc, char *argv[]) 
{int sock; // 客户端套接字struct sockaddr_in serv_addr; // 服务器地址结构char message[BUF_SIZE]; // 用于存储从服务器接收的消息int str_len,i; // 读取的字节数if (argc != 3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1); }// 创建一个客户端套接字sock = socket(PF_INET, SOCK_STREAM, 0);if (sock == -1) error_handling("socket() error"); // 初始化服务器地址结构memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET; // 地址族设置为IPv4serv_addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置服务器IP地址serv_addr.sin_port = htons(atoi(argv[2])); // 设置服务器端口号// 发送连接请求if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)error_handling("connect() error"); else{printf("Connected.....\n");}while(1){printf("Input message(Q to quit): ");fgets(message, BUF_SIZE, stdin);if (!strcmp(message, "Q\n") || !strcmp(message, "q\n")){break;}write(sock, message, strlen(message));str_len = read(sock, message, sizeof(message) - 1);message[str_len] = 0;printf("Message from server: %s \n", message);}// 关闭套接字close(sock);return 0; 
}void error_handling(char* message){fputs(message, stderr); fputc('\n', stderr); exit(1); 
}

运行结果
在这里插入图片描述
回声服务器/客户端可以正常运行,也可以单独关闭某一个客户端的连接

2.Windows系统

Windows系统下的select函数和Linux系统下的完全相同,详情查看(第十二章)I/O复用(1)

服务器端代码

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")// 指定链接到winsock库#define BUFF_SIZE 30 //缓冲区大小
void error_handling(char* message);int main(int argc, const char* argv[])
{WSADATA wsaData;// Windows Sockets API需要的数据结构SOCKET hServSock, hClntSock;SOCKADDR_IN servAddr, clntAddr;TIMEVAL timeout;fd_set reads,cpy_reads;char message[BUFF_SIZE];int strlen,clntAdrSize,fdNum,i;if (argc != 2) {printf("Usage : %s <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)// 初始化Windows Sockets服务error_handling("WSAStartup() error!");//创建套接字	hServSock = socket(PF_INET, SOCK_STREAM, 0);//使用SOCK_STREAM,即创建tcp套接字if (hServSock == INVALID_SOCKET)error_handling("socket() error");memset(&servAddr, 0, sizeof(servAddr));servAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = htonl(INADDR_ANY); servAddr.sin_port = htons(atoi(argv[1]));// 将socket绑定到地址和端口if (bind(hServSock, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)error_handling("bind() error");if (listen(hServSock, 5) == SOCKET_ERROR)error_handling("listen() error");//设置文件描述符集合FD_ZERO(&reads);FD_SET(hServSock, &reads);while (1) {cpy_reads = reads;//将reads复制到cpy_readstimeout.tv_sec = 5;//设置超时时间timeout.tv_usec = 5000;//等待文件描述符集合中的描述符就绪fdNum = select(0, &cpy_reads, NULL, NULL, &timeout);if (fdNum == SOCKET_ERROR){//有错误退出break;}if(fdNum == 0){//超时continue;}for (i = 0; i < fdNum; i++){//遍历所有文件描述符if (FD_ISSET(cpy_reads.fd_array[i], &reads)){//判断是否在cpy_reads中if (cpy_reads.fd_array[i] == hServSock){//有新的客户端连接clntAdrSize = sizeof(clntAddr);hClntSock = accept(hServSock, (SOCKADDR*)&clntAddr, &clntAdrSize);//接受客户端连接if (hClntSock == INVALID_SOCKET){error_handling("accept() error");}else{printf("connected client: %d \n",hClntSock);FD_SET(hClntSock, &reads);//将新的客户端socket添加到文件描述符集合中}	}else{//有客户端发送数据strlen = recv(cpy_reads.fd_array[i], message, BUFF_SIZE, 0);//接收数据if (strlen == 0){//如果客户端关闭连接FD_CLR(cpy_reads.fd_array[i], &reads);//从文件描述符集合中删除closesocket(cpy_reads.fd_array[i]);printf("closed client: %d\n",cpy_reads.fd_array[i]);}else{//发送数据send(cpy_reads.fd_array[i],message, strlen, 0);printf("message from client %d : %s", hClntSock,message);}}}}}closesocket(hServSock);// 关闭服务器socketWSACleanup();// 清理Windows Sockets服务return 0;
}void error_handling(char* message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

客户端代码
这里使用的是(第四章)基于TCP的服务器端/客户端(1)中的回声服务器客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")// 指定链接到winsock库#define BUF_SIZE 1024
void ErrorHandling(const char *message);int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSocket;SOCKADDR_IN servAddr;// 用于存储服务器的地址信息char message[BUF_SIZE];// 用于接收从服务器发送的消息int strLen;// 接收的消息长度if (argc != 3) {printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}// 初始化Windows Sockets服务if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHandling("WSAStartup() error!");//创建套接字hSocket = socket(PF_INET, SOCK_STREAM, 0);if (hSocket == INVALID_SOCKET)ErrorHandling("socket() error");memset(&servAddr, 0, sizeof(servAddr));servAddr.sin_family = AF_INET;// 设置地址族为IPv4servAddr.sin_addr.s_addr = inet_addr(argv[1]);// 设置服务器的IP地址servAddr.sin_port = htons(atoi(argv[2]));// 设置服务器的端口号// 尝试连接到服务器if (connect(hSocket, (SOCKADDR*)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)ErrorHandling("connect() error!");else{printf("Connected......\n");}while(1){fputs("Input message(Q to quit): ",stdout);fgets(message, BUF_SIZE, stdin);if(!strcmp(message, "Q\n") || !strcmp(message, "q\n")){break;}send(hSocket, message, strlen(message), 0);strLen= recv(hSocket, message, sizeof(message)-1, 0);message[strLen]= 0;printf("Message from server: %s \n", message);}closesocket(hSocket);// 关闭套接字WSACleanup();// 清理Windows Sockets服务return 0;
}void ErrorHandling(const char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

运行结果
在这里插入图片描述

相关文章:

  • 如何找出真正的交易信号?Anzo Capital昂首资本总结7个
  • Vue3实战笔记(51)—Vue 3封装带均线的k线图
  • 微信小程序预览图片和H5使用canvas实现图片+蒙层+文字
  • 2019美亚
  • 【面试】谈谈常见的Java虚拟机有哪些
  • JavaScript-JavaWeb
  • 聚观早报 | 哪吒L纯电版开启预售;OPPO Pad 3获3C认证
  • opencl色域变换,处理传递显存数据
  • 【PB案例学习笔记】-13 徒手做个电子时钟
  • fast-lio2代码学习
  • GEC6818开发板的环境配置
  • AI界的“拼夕夕”登场,为上万张GPU寻找新使命
  • 磁盘管理后续——盘符漂移问题解决
  • Java进阶学习笔记21——泛型概念、泛型类、泛型接口
  • 风景园林设计乙级资质被撤销后的恢复途径
  • [ JavaScript ] 数据结构与算法 —— 链表
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • const let
  • Docker容器管理
  • Java,console输出实时的转向GUI textbox
  • JS字符串转数字方法总结
  • MySQL-事务管理(基础)
  • Nacos系列:Nacos的Java SDK使用
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • 欢迎参加第二届中国游戏开发者大会
  • ------- 计算机网络基础
  • 技术:超级实用的电脑小技巧
  • 听说你叫Java(二)–Servlet请求
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • ### RabbitMQ五种工作模式:
  • $ git push -u origin master 推送到远程库出错
  • $(function(){})与(function($){....})(jQuery)的区别
  • (Forward) Music Player: From UI Proposal to Code
  • (SpringBoot)第七章:SpringBoot日志文件
  • (理论篇)httpmoudle和httphandler一览
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (正则)提取页面里的img标签
  • .NET Micro Framework 4.2 beta 源码探析
  • .net6+aspose.words导出word并转pdf
  • ??eclipse的安装配置问题!??
  • @AutoConfigurationPackage的使用
  • @GetMapping和@RequestMapping的区别
  • @SentinelResource详解
  • [20171113]修改表结构删除列相关问题4.txt
  • [Angular] 笔记 8:list/detail 页面以及@Input
  • [Flutter] extends、implements、mixin和 abstract、extension的使用介绍说明
  • [i.MX]飞思卡尔IMX6处理器的GPIO-IOMUX_PAD说明
  • [Java]深入剖析常见排序
  • [JavaEE系列] Thread类的基本用法
  • [Latex学习笔记]数学公式基本命令
  • [LeetCode] Verify Preorder Sequence in Binary Search Tree 验证二叉搜索树的先序序列
  • [Linux] day07——查看及过滤文本
  • [Linux]于Mac在配置Linuxserver安装Nginx+PHP