linux tcp ip c,Linux下TCP/IP编程--TCP实战(select)

本文参考自徐晓鑫《后台开发》,记录之。

一、为什么要使用非阻塞I/O之select

初学socket的人可能不爱用select写程序,而习惯诸如connect、accept、recv/recvfrom这样的阻塞程序。

当让服务器同时为多个客户端提供一问一答服务时,很多程序员采用多线程/进程模型来解决。但是若同时响应成百上千的连接请求,无论是多进程还是多线程都会严重占据系统资源降低系统对外响应的效率。(“线程池”旨在降低创建和销毁线程的频率,“连接池”旨在尽量重用已有连接,二者都需要考虑面临的响应规模,即池的大小是有限的)。

高级程序员使用select就可以完成非阻塞方式工作的程序,它能够监视被监测文件描述符的变化情况。

使用select的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多CPU资源,同时能为多客户端提供服务。当然select也有缺点如下:

每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大

每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大

select支持的文件描述符数量太小了,默认是1024

后面学习poll、epoll就是解决这个问题的,这个后面会了解到。

二、slect函数原型

int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);

具体解释select的参数:

maxfdp是一个整数值,集合中所有文件描述符的范围,即所有文件描述符的最大值加1。

fd_set *readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读;如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0;若发生错误返回负值。

fd_set *writefds是指向fd_set结构的指针,主要关心文件的写变化,即是否可写。

fd_set *errorfds用来监视文件错误异常

返回值:

正值表示准备就绪的描述符数, 0表示等待超时,负值表示select出错

三、使用select函数循环读取键盘输入

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

int main()

{

int keyboard;

int ret,i;

char c;

fd_set readfd;

struct timeval timeout;

keyboard = open("/dev/tty",O_RDONLY |O_NONBLOCK);

assert(keyboard>0);

while(1)

{

timeout.tv_sec = 5;

timeout.tv_usec = 0;

FD_ZERO(&readfd);

FD_SET(keyboard,&readfd);

ret = select(keyboard+1,&readfd,NULL,NULL,&timeout);

if(ret == -1)

perror("select error\n");

else if (ret) {

if(FD_ISSET(keyboard,&readfd)) {

i = read(keyboard,&c,1);

if('\n'== c)

continue;

printf("The input is %c\n",c);

if('q'==c)

break;

}

}

else if (ret ==0)

printf("time out\n");

}

return 0;

}

只要发现键盘输入字符,程序就输出对应字符。若超过5s不输入,打印time out。

70

四、使用select函数提高服务器处理能力

服务器端:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DEFAULT_PORT 6666

int main( int argc, char ** argv){

int serverfd,acceptfd; /* 监听socket: serverfd,数据传输socket: acceptfd */

struct sockaddr_in my_addr; /* 本机地址信息 */

struct sockaddr_in their_addr; /* 客户地址信息 */

unsigned int sin_size, myport=6666, lisnum=10;

if ((serverfd = socket(AF_INET , SOCK_STREAM, 0)) == -1) {

perror("socket" );

return -1;

}

printf("socket ok \n");

my_addr.sin_family=AF_INET;

my_addr.sin_port=htons(DEFAULT_PORT);

my_addr.sin_addr.s_addr = INADDR_ANY;

bzero(&(my_addr.sin_zero), 0);

if (bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr )) == -1) {

perror("bind" );

return -2;

}

printf("bind ok \n");

if (listen(serverfd, lisnum) == -1) {

perror("listen" );

return -3;

}

printf("listen ok \n");

fd_set client_fdset; /*监控文件描述符集合*/

int maxsock; /*监控文件描述符中最大的文件号*/

struct timeval tv; /*超时返回时间*/

int client_sockfd[5]; /*存放活动的sockfd*/

bzero((void*)client_sockfd,sizeof(client_sockfd));

int conn_amount = 0; /*用来记录描述符数量*/

maxsock = serverfd;

char buffer[1024];

int ret=0;

/*不断的查看是否有新的client连接;已连接的client是否有发送消息过来*/

while(1){

/*初始化文件描述符号到集合*/

FD_ZERO(&client_fdset);

/*加入服务器描述符*/

FD_SET(serverfd,&client_fdset);

/*设置超时时间*/

tv.tv_sec = 30; /*30秒*/

tv.tv_usec = 0;

/*把活动的句柄加入到文件描述符中*/

for(int i = 0; i < 5; ++i){

/*程序中Listen中参数设为5,故i必须小于5*/

if(client_sockfd[i] != 0){

FD_SET(client_sockfd[i], &client_fdset);

}

}

/*printf("put sockfd in fdset!\n");*/

/*select函数,根据返回值判断程序是否有异常*/

ret = select(maxsock+1, &client_fdset, NULL, NULL, &tv);

if(ret < 0){

perror("select error!\n");

break;

} else if(ret == 0){

printf("timeout!\n");

continue;

}

/*轮询各个(已连接上的client的)文件描述符有无可读(接收)数据,有就输出,没有或者异常时,关闭相应的client连接,并在集合里清理掉*/

for(int i = 0; i < conn_amount; ++i){

/*FD_ISSET检查client_sockfd是否可读写,>0可读写*/

if(FD_ISSET(client_sockfd[i], &client_fdset)){

printf("start recv from client[%d]:\n",i);

ret = recv(client_sockfd[i], buffer, 1024, 0);

if(ret <= 0){

printf("client[%d] close\n", i);

close(client_sockfd[i]);

FD_CLR(client_sockfd[i], &client_fdset);

client_sockfd[i] = 0;

}

else{

printf("recv from client[%d] :%s\n", i, buffer);

}

}

}

/*检查是否有新的连接,如果有,接收连接加入到client_sockfd中*/

if(FD_ISSET(serverfd, &client_fdset))

{

/*接受连接*/

struct sockaddr_in client_addr;

size_t size = sizeof(struct sockaddr_in);

int sock_client = accept(serverfd, (struct sockaddr*)(&client_addr), (unsigned int*)(&size));

if(sock_client < 0){

perror("accept error!\n");

continue;

}

/*把连接加入到文件描述符集合中*/

if(conn_amount < 5)

{

client_sockfd[conn_amount++] = sock_client;

bzero(buffer,1024);

strcpy(buffer, "this is server! welcome!\n");

send(sock_client, buffer, 1024, 0);

printf("new connection client[%d] %s:%d\n", conn_amount, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

bzero(buffer,sizeof(buffer));

ret = recv(sock_client, buffer, 1024, 0);

if(ret < 0){

perror("recv error!\n");

close(serverfd);

return -1;

}

printf("recv : %s\n",buffer);

//更新maxsock,因为下一次进入while循环调用时,需要传当前最大的fd值+1给select函数

if(sock_client > maxsock){

maxsock = sock_client;

}

else{

printf("max connections!!!quit!!\n");

break;

}

}

}

}

//最后,把已连接上的clent的fd和server自身的fd都关闭

for(int i = 0; i < 5; ++i){

if(client_sockfd[i] != 0){

close(client_sockfd[i]);

}

}

close(serverfd);

return 0;

}

客户端:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DEFAULT_PORT 6666

int main( int argc, char * argv[]){

int connfd = 0;

int cLen = 0;

struct sockaddr_in client;

if(argc < 2){

printf(" Uasge: clientent [server IP address]\n");

return -1;

}

client.sin_family = AF_INET;

client.sin_port = htons(DEFAULT_PORT);

client.sin_addr.s_addr = inet_addr(argv[1]);

connfd = socket(AF_INET, SOCK_STREAM, 0);

if(connfd < 0){

perror("socket" );

return -1;

}

if(connect(connfd, (struct sockaddr*)&client, sizeof(client)) < 0){

perror("connect" );

return -1;

}

char buffer[1024];

bzero(buffer,sizeof(buffer));

recv(connfd, buffer, 1024, 0);

printf("recv : %s\n", buffer);

bzero(buffer,sizeof(buffer));

strcpy(buffer,"this is client!\n");

send(connfd, buffer, 1024, 0);

while(1){

bzero(buffer,sizeof(buffer));

scanf("%s",buffer);

int p = strlen(buffer);

buffer[p] = '\0';

send(connfd, buffer, 1024, 0);

printf("i have send buffer\n");

}

close(connfd);

return 0;

}

验证:

70

客户端1:

70

客户端2:

70

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/551611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单片机8×8点阵显示简单汉字的程序_干货 | 浅析单片机制作贪吃蛇游戏

为了让大家更深入地了解底层的原理&#xff0c;在讲解时特意选择了51单片机(而非STM系列)&#xff0c;另外16*16点阵由译码器和移位缓存器直接驱动(而非MAX系列芯片)&#xff0c;摇杆也利用ADC功能判断方向。那如何让单片机驱动这256个点呢&#xff1f;直接用IO口驱动显然不够且…

怎样在linux中创建硬盘,在linux中添加新硬盘并创建LVM组

1、以虚拟机为例&#xff0c;给虚拟机添加一块新硬盘&#xff0c;并创建LVM组&#xff0c;将新硬盘用于存放oracle数据库文件。2、fdisk -ll查看新添加的硬盘是否被识别&#xff0c;如图已经识别出sdb。3、# pvcreate /dev/sdb (创建PV&#xff0c;相当于win中将基础磁盘转换…

双代号网络图基础算法_软考网络工程师之系统开发和运行基础(软件分类、测试、模型)...

系统开发和运行基础(软件的分类、软件生存周期、软件开发模型、软件测试、软件项目管理)软件的分类系统软件&#xff0c;如操作系统。支撑软件&#xff0c;如开发工具。应用软件&#xff0c;如office。实时处理软件&#xff0c;一般是工业软件。软件生存周期1、软件定义问题定义…

在学Python前学Linux,Python原来这么好学-1.2节: 在Linux中安装python

这里将告诉您Python原来这么好学-1.2节: 在Linux中安装python,具体操作过程:在Linux系统的主要发行版中&#xff0c;按其软件包格式来进行划分&#xff0c;可分为Deb系以及RPM系操作系统。Linux系统与Windows系统有一个很重要的区别&#xff0c;Linux系统完全免费&#xff0c;开…

python金融大数据分析视频_Python金融大数据分析 PDF 全书超清版

给大家带来的一篇关于Python相关的电子书资源&#xff0c;介绍了关于Python金融、大数据分析方面的内容&#xff0c;本书是由人民邮电出版社出版&#xff0c;格式为PDF&#xff0c;资源大小47.8 MB&#xff0c;希尔皮斯科编写&#xff0c;目前豆瓣、亚马逊、当当、京东等电子书…

kaggle房价预测特征意思_机器学习-kaggle泰坦尼克生存预测(一)-数据清洗与特征构建...

1、背景&#xff1a;1.1 关于kaggle&#xff1a;谷歌旗下的 Kaggle 是一个数据建模和数据分析竞赛平台。该平台是当下最流行的数据科研赛事平台&#xff0c;其组织的赛事受到全球数据科学爱好者追捧。 如果学生能够在该平台的一些比赛中获得较好的名次&#xff0c;不仅可以赢得…

linux docker安装svn,使用docker镜像搭建svn+Apache环境

环境准备虚拟机装好之后&#xff0c;按照官网步骤检查虚拟机内核版本&#xff0c;必须在3.10以上版本&#xff0c;故此处安装redhat_7.2# uname -r3.10.0-327.el7.x86_64安装docker&#xff1a;yum install docker-io有依赖是直接安装具体的依赖软件&#xff0c;解决依赖docker…

springboot 设置ico_Spring Boot--Thymeleaf模板引擎/静态页面

点关注&#xff0c;不迷路&#xff1b;持续更新Java相关技术及资讯&#xff01;&#xff01;&#xff01;1.Spring Boot对静态资源的映射规则如果静态资源文件夹下有 index.html的话,直接访问localhost:8080的话,index就是欢迎页使用自己的图标:在静态资源文件夹放一个 favicon…

linux shell 豆瓣,用Linux shell脚本爬取豆瓣邮箱

前两天总结一下邮箱格式的正则表达式&#xff0c;写了一个脚本来判断输入的邮箱地址是否符合邮箱的格式(有兴趣的朋友可以翻一下我的博客去看一下)。在网上找资料的时候&#xff0c;发现基本都是用Python和Java来做爬虫&#xff0c;当然Python的居多。本着学习的心态&#xff0…

php 获取header_php 输出404状态码

今天在做一个php数据提交的小功能时&#xff0c;需要在判断一个值是如果为空时&#xff0c;就让后台的逻辑页面返回 404 状态码&#xff0c;提示用户页面不存在。那么接下来&#xff0c;飞鸟慕鱼博客要说的是 php 如何返回或输出 html 404 的状态码。php header()函数header()&…

getcwd和pwd为什么不一样_农村医保,为什么每个地区收费不一样?

新农村合作医疗保险的缴费标准&#xff0c;为什么每个地区都不一样呢&#xff1f;我们可能不难发现&#xff0c;就是说你在的地区每年交费可能是280元&#xff0c;但是别的人在的地区可能交费达到了306元或者说320元&#xff0c;这究竟是一种什么样的情况呢&#xff1f;其实每个…

矩阵论思维导图_《实变函数论》 江泽坚 3rd 思维导图与笔记整理

实变函数学十遍实变函数应该是你们本科所有数学课程中最难的了 ————韦老师如是说学期初就知道这个课不好学。话不多说&#xff0c;期末之前把复习导图全部做完了&#xff0c;po上来分享。注&#xff1a;由于制作辛苦&#xff0c;所以预先告知收取些许费用&#xff08;不到一…

jackson 读取多文件_Spring Boot系列之读取配置

使用SpringBoot框架开发&#xff0c;读取配置是少不了的&#xff0c;那么你会读取配置吗&#xff1f;你会写配置吗&#xff1f;List&#xff1f;Map&#xff1f;1 目的本节我们要解决如下几个问题&#xff1a;如何使用Spring Boot读取配置文件&#xff1f;有哪些方式&#xff1…

ab plc编程软件_AB编程软件及所有组态软件授权及安装教程包含FTViewV11

前面发了几个求助的帖子&#xff0c;希望有朋友能提供些Factory Talk View V11的安装和授权&#xff0c;但是一直没有什么回复&#xff0c;今天结合别人的经验&#xff0c;算是把授权安装搞定了&#xff0c;在这里发布出来&#xff0c;希望能帮助到更多的朋友&#xff0c;授权和…

c++ udp多线程 例子_[内附完整源码和文档] 基于udp实现tcp功能进行大文件传输

一.项目要求Please choose one of following programing languages: C, C, Java, Python;本项目采用的是python3.6LFTP should use a client-server service model;本项目使用客户端-服务器的模式LFTP must include a client side program and a server side program; Client s…

C语言中 1%3,算术什么意思啊 算数什么意思

算术什么意思啊 算数什么意思以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;算术是什么意思数学什么是算术和算术和是正数的和&#xff0c;即绝对值的和&#xff0c;例如213.57&#xff0c;…

python 消息框但不影响程序执行_还在用print()查找错误?日志消息不香嘛?| 原力计划...

作者 | 灰小猿责编 | 王晓曼出品 | CSDN博客最近在做项目开发的时候&#xff0c;跟小伙伴聊到修 Bug 这件事。嗯&#xff0c;对于一只没技术的程序猿来说&#xff0c;修 Bug 的确是一件让人头疼的事情&#xff0c;尤其是对于比较大型的项目开发&#xff0c;在变量较多的时候&am…

vue中传值和传引用_vue prop属性传值与传引用示例

vue prop属性传值与传引用示例vue组件在prop里根据type决定传值还是传引用。简要如下&#xff1a;传值&#xff1a;String、Number、Boolean传引用&#xff1a;Array、Object若想将数组或对象类型也以值形式传递怎么办呢&#xff1f;如下方式可以实现&#xff1a;// component-…

.sql文件_Mysql分组排序及Sql文件执行

最近在做一个临时项目&#xff0c;采用的是mysql开发&#xff0c;主要涉及两个问题&#xff1a;1.一个是传参问题&#xff1b;2.另一个是实现对数据分组取前4的数据。个人感觉很有意义&#xff0c;特此记录一下。一.传参问题执行mysql文件&#xff0c;需要从外部传入参数时&…

android 动态库 后缀,Android Robolectric加载运行本地So动态库

前言Robolectric 是 Android 的单元测试框架&#xff0c;运行无需 Android 真机环境直接运行在 JVM 之上&#xff0c;所以在 test case 运行速度效率上有了很大提升&#xff0c;接近于 Java JUnit test(JUnit test > Robolectric ≫ androidTest)。不过框架本身并不支持 so …