一、需求

  把https://www.cnblogs.com/soldierback/p/10673345.html中的TCP回射服务器程序重写成使用select来处理任意个客户的单进程

  程序,而不是为每个进程派生一个子进程

二、分析

 (1)服务器有单个监听描述符

     

 (2)服务器只维护一个读描述符集;假设服务器是在前台启动的,那么描述符0、1、2将分别被设置为标准输入、标准输出和标准错误输出;可见监听

     套接字的第一个可用描述符是3

     

 (3)服务器维护一个名为clients的整型数组,它包含每个客户的已连接套接字描述符,该数组的所有元素都被初始化为-1

        

 (4)当第一个客户与服务器建立连接时,监听描述符变为可读,服务器于是调用accept

    

 (5)假设由accept返回的描述符为4,则clients数组和读描述符集如下所示

        

 (6)当第二个客户与服务器建立连接时,假设由accept返回的描述符为5,则clients和都描述符集如下所示

     

 (7)假设第一个客户终止它的连接;该客户的TCP发送一个FIN,使得服务器的描述符4变为可读;当服务器读这个已连接套接字时,read将

     返回0,服务器于是关闭该套接字并相应地更新数据结构:把clients[0]的值置为-1,把描述符集中描述符4的位设置为0;注意:maxfd的

     值没有改变

     

三、源代码

#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <strings.h> #define LISTENQ 1024
#define MAXLINE 4096
#define SERV_PORT 9999
#define SA struct sockaddr ssize_t writen(int, const void*, size_t);
char *sock_ntop(const struct sockaddr*, socklen_t); int main(int argc, char *argv[]) { int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, clients[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr; listenfd = socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); listen(listenfd, LISTENQ); maxfd = listenfd; /* initialize */
maxi = -; /* index into clients[] array */
for (i = ; i < FD_SETSIZE; i++) {
clients[i] = -; /* -1 indicates available entry */
}
FD_ZERO(&allset);
FD_SET(listenfd, &allset); for ( ; ; ) {
rset = allset;          /* structure assignment */
nready = select(maxfd+, &rset, NULL, NULL, NULL); if (FD_ISSET(listenfd, &rset)) { /* new client connection */
clilen = sizeof(cliaddr);
connfd = accept(listenfd, (SA *) &cliaddr, &clilen); for (i = ; i < FD_SETSIZE; i++) {
if (clients[i] < ) {
clients[i] = connfd; /* save descriptor */
printf("new client %d: [%s]\n", i, sock_ntop((SA *)&cliaddr, sizeof(cliaddr)));
break;
}
}
if (i == FD_SETSIZE) {
printf("too many clients\n");
goto ifAnyDescriptorReadable;
} FD_SET(connfd, &allset); /* add new descriptor to set */
if (connfd > maxfd)
maxfd = connfd; /* for select */
if (i > maxi)
maxi = i; /* max index in client[] array */ ifAnyDescriptorReadable:
if (--nready <= )
continue; /* no more readable descriptors */
} for (i = ; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = clients[i]) < ) {
continue;
}
if (FD_ISSET(sockfd, &rset)) {
if ( (n = read(sockfd, buf, MAXLINE)) == ) {
/* connection closed by client */
close(sockfd);
FD_CLR(sockfd, &allset);
clients[i] = -;
printf("client [%d] quit\n", i);
} else {
writen(sockfd, buf, n);
} if (--nready <= ) {
break; /* no more readable descriptors */
}
}
}
}
} 注:sock_ntop和writen两个函数在分类为《UNIX网络编程》的其他随笔中有
存在的问题:某个客户建立连接后不断发送数据,此时会导致服务器拒绝为其他客户服务
解决方法:让每个客户由单独的进程或线程提供服务

TCP回射服务器修订版(ubuntu 18.04)的更多相关文章

  1. 服务器编程入门(10)TCP回射服务器实现 - 并发

    问题聚焦:     在前面我们大概浏览了一下服务器编程需要掌握的一些知识和技术,以及架构思想.        实践,才是检验真理的唯一标准..从这节起我们将在这些技术的基础上,一步步实现以及完善一个服 ...

  2. TCP回射服务器程序:main函数

    TCP回射并发服务器 1.创建套接字,绑定服务器的众所周知端口 创建一个TCP套接字,在待绑定到该TCP套接字的网际网套接字地址结构中填入通配地址(INADDR_ANY) 和服务器的众所知周(SERV ...

  3. UNIX网络编程——TCP回射服务器/客户端程序

    下面通过最简单的客户端/服务器程序的实例来学习socket API. serv.c 程序的功能是从客户端读取字符然后直接回射回去: #include<stdio.h> #include&l ...

  4. TCP回射服务器程序:str_echo函数

    str_echo函数执行处理每个客户的服务: 从客户读入数据,并把它们回射给客户 读入缓冲区并回射其中内容: read函数从套接字读入数据,writen函数把其中内容回射给客户 如果客户关闭连接,那么 ...

  5. UNIX网络编程——使用线程的TCP回射服务器程序

    同一进程内的所有线程除了共享全局变量外还共享: (1)进程指令: (2)大多数数据: (3)  打开的文件(即描述符): (4)信号处理函数和信号处置: (5)当前工作目录: (6)用户ID和组ID. ...

  6. TCP客户/服务器程序实例——回射服务器

    目录 客户/服务器程序源码 POSIX信号处理 POSIX信号语义 处理SIGCHLD信号 处理僵死进程 处理被中断的系统调用 wait和waitpid函数 wait和waitpid函数的区别 网络编 ...

  7. 【Unix网络编程】chapter5TCP回射服务器程序

    chapter5  5.1 概述 5.2 TCP回射服务器程序:main函数 int main(int argc, char **argv) { int listenfd,connfd; pid_t ...

  8. 在 Ubuntu 18.04 LTS 无头服务器上安装 Oracle VirtualBox

    作者: Sk 译者: LCTT qhwdw | 2018-10-12 01:59 本教程将指导你在 Ubuntu 18.04 LTS 无头服务器上,一步一步地安装 Oracle VirtualBox. ...

  9. 【Ubuntu 18.04 搭建VNC服务器】

    https://www.jianshu.com/p/f58fe5cdeb5f 桌面共享 Ubuntu 18.04自带桌面共享,可以将物理桌面共享给VNC.但是无法创建新的桌面. 具体参考 https: ...

随机推荐

  1. Bug预防体系(上千bug分析后总结的最佳实践)

    Bug预防体系(上千bug分析后总结的最佳实践) 原创 2017-08-16俞美玲 光荣之路 吴老的<selenium webdriver 实战宝典>出版了!  web常见产品问题及预防 ...

  2. NOI2017蔬菜(贪心)

    小 N 是蔬菜仓库的管理员,负责设计蔬菜的销售方案. 在蔬菜仓库中,共存放有 n 种蔬菜,小 N 需要根据不同蔬菜的特性,综合考虑各 方面因素,设计合理的销售方案,以获得最多的收益. 在计算销售蔬菜的 ...

  3. 2019 校内赛 RPG的天赋分支(贪心)

    Problem Description 很多游戏都有天赋树的概念,天赋树的不同分支具有不同的属性加成,那么合理选择分支就非常重要了.Luke最近沉迷一款RPG游戏,它的天赋树机制如下:角色具有n个可选 ...

  4. Linux脚本点滴

    1.当硬盘空间不足15%时,循环删除最早的日志文件(ELK): #!/bin/bash for((i=20;i>=5;i-=1)) do code=`df /dev/mapper/ElkGrou ...

  5. Building Microservices with Spring Boot and Apache Thrift. Part 2. Swifty services

    http://bsideup.blogspot.com/2015/04/spring-boot-thrift-part2.html     In previous article I showed y ...

  6. Nginx概述及安装配置

    1.概述 Nginx是一款开源的,高性能的Http和反向代理服务器,同时也是一个IMAP.POP3.SMTP代理服务器:Nginx可以作为一个HTTP服务器进行网站的发布处理,也可以作为反向代理进行负 ...

  7. 老项目用webpack中文乱码问题解决记录

    有个很久(有多久呢,你还记得jquery1.6的年代吗...)的项目需要新加一些功能,又想使用新的生产力工具比如说webpack,es6,vue神马的.原来的项目整体都是用GBK编码的,这特么...坑 ...

  8. 漫谈php框架之中间件

    市面上常见的php框架有很多,最近因为有技术需求,所以对常见的php框架的中间件进行了一些了解.各个框架尽管在目标上对php框架的定义大同小异,但是在实现方式上却各有不同,且看下文: 定义 首先什么是 ...

  9. 高级组件——弹出式菜单JPopupMenu

    弹出式菜单JPopupMenu,需要用到鼠标事件.MouseListener必须要实现所有接口,MouseAdapter是类,只写你关心的方法,即MouseAdapter实现了MouseListene ...

  10. Grunt安装与环境配置

    公司项目还没有前后端分离,而前端是使用node.js搭建起来的,现在需要自己动手开发,故学习下并做为记录防止以后忘记. grunt依赖node.js,所以在安装之前确保你安装了 Node.js.然后开 ...