`
memorymyann
  • 浏览: 267615 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

6.并发服务器 以及 与之对应的客户端

阅读更多

该代码大致过程,客户端连接服务器,服务器接收链接后,将创建子进程。客户端从终端输入字符,字符会被送到服务器的子进程,子进程得到字符后,再将字符回馈给客户端,客户端将其显示出来。代码比较长因为加入了信号对死亡子进程的处理。

[root@liumengli net]# cat echo_server.c
#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "signal.h"
#include "sys/types.h"
#include "unistd.h"

#define LISTENQ 10

void str_echo(int);
void install();//安装对由子进程死亡发出的SIGCHLD信号的处理,如果不做处理子进程会变成僵尸进程
void my_op(int, siginfo_t *, void *);
//IP地址被我硬编码到了代码中,端口由输入参数指定
int main(int argc, char ** argv) {
        int listenfd, connfd;
        pid_t   child_pid;
        socklen_t child_len;
        struct sockaddr_in child_socket, serv_socket;


        listenfd = socket(AF_INET, SOCK_STREAM, 0);

        bzero(&serv_socket, sizeof(serv_socket));
        serv_socket.sin_family = AF_INET;
        serv_socket.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_socket.sin_port = htons(atoi(argv[1]));
        bind(listenfd, (struct sockaddr *)&serv_socket, sizeof(serv_socket));
        install();
        listen(listenfd, LISTENQ);
        for(;;) {
                child_len = sizeof(child_socket);
                connfd = accept(listenfd, (struct sockaddr *)&child_socket, &child_len);
                if(connfd == -1) //值得注意的地方
                        continue;
                if((child_pid = fork()) == 0) {
                        printf("my pid is:%d\n", getpid());
                        close(listenfd);/值得注意的地方
                        str_echo(connfd);
                        close(connfd);/值得注意的地方
                        exit(0);
                }
                close(connfd);
        }
}

void str_echo(int connfd) {
        ssize_t n;
        char buf[100];
        for(;;) {
                if((n = read(connfd, buf, 100)) == 0)
                        return;
                write(connfd, buf, n);
        }
}

void install() { //安装对死亡子进程的信号
        struct sigaction act, old_act;
        sigemptyset(&act.sa_mask);
        act.sa_flags = SA_SIGINFO;
        act.sa_sigaction = my_op;

        if(sigaction(SIGCHLD, &act, &old_act) < 0) {
                printf("install signal failed\n");
                exit(1);
        }
}

void my_op(int signum, siginfo_t * info, void * myact) {//对死亡子进程处理,防止其变成僵尸进程
        pid_t pid;
        int stat;

        pid = wait(&stat);
        printf("%d process terminated\n", pid);
        return;
}

 

 

[root@liumengli net]# cat echo_client.c

#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

int main(int argc, char ** argv) {
        int sockfd;
        struct sockaddr_in serv_socket;
        char buf[100];

        if(argc != 2) {
                printf("please input port");
                exit(1);
        }

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        bzero(&serv_socket, sizeof(serv_socket));
        serv_socket.sin_family = AF_INET;
        serv_socket.sin_port = htons(atoi(argv[1]));
        inet_pton(AF_INET, "192.168.1.235", &serv_socket.sin_addr);
        connect(sockfd, (struct sockaddr_in *)&serv_socket, sizeof(serv_socket));
        int n = read(0, buf, 100);//在linux中0是标准输入
        buf[n] = '\0';
        write(sockfd, buf, n + 1);
        read(sockfd, buf, sizeof(buf));
        printf("%s\n", buf);
        close(sockfd);
        exit(0);
}

 

代码不是非常难,如果有linux下信号处理和I/O操作的编程经验,这个程序不难理解。

 

服务器端,首先是定义自己的监听套接口,然后调用listen函数监听,主进程在accept处会由于完成链接队列中没有链接而被挂起,等客户端connect后,主进程会从accept处返回,主进程会调用fork创建一个子进程,子进程开始对链接connfd进行服务。子进程在完成服务后,调用exit死亡,死亡的子进程会想父进程发送SIGHCLD信号,此时父进程会因循环继续挂起在accept处。挂起的父进程会对信号捕获,并调用my_op函数处理信号。一个子进程就彻底处理完毕。

 

客户端,在connect服务器后,进程会在read(0, buf, 100)处等待客户从键盘输入(0就是代表标准输入,默认情况下是键盘。以回车键标志输入结束)。完毕后,通过write(sockfd, buf, n+1)发送给服务器,服务器会返回数据,通过read读取服务器返回数据,并将数据打印。

 

几个值得注意的地方:

1. close(listenfd);/值得注意的地方
str_echo(connfd);
close(connfd);/值得注意的地方

在linux下,任何一个I/O打开,只是在第一次为这个I/O创建描述结构,之后每次的open都只是对这个描述结构引用,并给该引用计数加1,close只会将共享计数减1,等共享计数减成0以后才会将描述结构删除。子进程在创建时候会继承父进程的所有资源,包括其打开的文件和链接,因此所有的链接和文件描述结构的共享计数都会加1,监听接口listenfd共享计数和链接connfd共享计数都会被加1,子进程关闭listenfd和connfd只是减1共享计数。(貌似不自己手动close,子进程在死亡后也会减1,不过我不能确定,最好自己手动添加close)。

 

2.

if(connfd == -1) //值得注意的地方
                        continue;

有些系统调用可能永远阻塞系统称之为慢系统调用,也就是这类调用可能永远无法返回,比如accept,只要没有客户端connect那么这类调用就可能永远不会返回,进程将一直被阻塞,同样后面的read也是这种类型。这儿有个原则:当一个进程被慢系统调用阻塞的时候捕获到一个信号,等到信号处理程序返回时,系统调用可能返回一个EINTR错误。只所以采用可能这个词,是因为某些内核会自动重启这些调用,从而使的主程序将继续在这个系统调用处阻塞。当然不是所有内核都会这么处理,所以后面我们要加上检查语句。

分享到:
评论

相关推荐

    Linux下高并发服务器的研究与实现.pdf

    基于 socket 编程的基础上,对比了 Linux 系统下三种多路复用 I/O 接口:select、poll、epoll,确定了以 socket、epoll 机制以及线程池为基础来设计与实现一个客户端/服务器(client/server)模型的高并发服务器。...

    LINUX环境并发服务器的实现模型

    在网络程序里面,一般来说都是许多客户对应一个服务器,为了处理客户的请求,对服务端的程序就提出了特殊的要求。 目前最常用的服务器模型有: ...•并发服务器:服务器在同一时刻可以响应多个客户端的请求

    iocp 服务端/客服端 大并发访问 上万 源码与文档

    IOCP服务器压力测试 运行以后可以看到IOCP服务器和其他模型的性能差距,还真是不看不知道呢!内附测试程序实现源码,具有重要参考意义

    基于Akka的高性能可伸缩的JAVA网络游戏服务器 简单的单服务器开发与集群开发的切换 使用Actor处理高并发 易于测试

    服务器从客户端收到消息,让后可以根据消息的定义处理对应的逻辑。 我们在这样的模式下开发建议不要建立全局的管理器。注意Actor模式下,每个玩家都是独立的Actor。 处理消息的时候,对于自己的数据更改是线程安全的...

    精通websphere MQ

    客户端和服务器端 (Client & Server)................................................... 27 1.2.10 操作界面 (MQ Interface)..................................................................... 27 ...

    基于Linux C++和socket网络编程的即时通信系统源码+项目说明(课程设计).zip

    服务端:能够接受新的客户连接,并将每个客户端发来的信息,广播给对应的目标客户端 2.客户端:能够连接服务器,并向服务器发送消息,同时可以接受服务器发来的消息 服务端: 1.支持多个客户端接入,实现聊天室...

    高并发数据库设计.pdf

    最后我们把order表放在了8个分库中(编号1到8,分别对应DB1到DB8),每个分库中10个分表(编号0到9,分别对应order_0到 order_9),部署结构如下图所⽰: 根据uid计算数据库编号: 数据库编号 = (uid / 10) % 8 + 1...

    高并发支付系统架构设计与实践.pdf

    在高并发支付系统架构设计中,需要使用到O/R Mapping技术来将对象与数据库表对应起来,以便于快速地读取和写入数据。此外,需要使用到xml格式来存储配置数据,以便于快速地读取和写入数据。 在分布式缓存机制中,...

    Qt编写网络调试助手(TCP客户端+TCP服务端+UDP服务端)终极版.zip

    时隔半年,对网络调试助手工具进行所有代码重写,这次目录结果整齐的一逼,代码整齐的一逼,非常完善了,打死也不再改版了。这次真的打死也不再改版了。...9:四种模式,tcp服务器、tcp客户端、udp服务器、udp客户端。

    公司网络通信系统源码+毕业论文

    在面对几百个甚至更多的客户端同时发出连接信息的情况下,服务器要求能够保持高性能的并发处理机制,迅速地完成这几百个并发请求的处理和发送任务。 在扩展性和伸缩性方面,聊天系统也提出了一定的要求。当一台...

    Qt编写网络调试助手(TCP客户端+TCP服务端+UDP服务端)终极版

    时隔半年,对网络调试助手工具进行所有代码重写,这次目录结果整齐的一逼,代码整齐的一逼,非常完善了,打死也不再改版了。这次真的打死也不再改版了。...9:四种模式,tcp服务器、tcp客户端、udp服务器、udp客户端。

    FileZillaServer的安装和配置.doc

    这里需要输入服务器的地址、端口号和口令,跟远程服务器上面的对应起来。 4. 服务器全局参数设置:点“Edit”菜单,选“Settings”General settings(常规设置): * Listen on Port:监听端口,即 FTP 服务器的...

    用友ERPU890安装与配置.ppt

    即用户可以使用总账、UFO报表、应收款管理、应付款管理、工资管理、固定资产等财务软件,以及采购管理、委外管理、销售管理、库存管理、存货核算等业务软件,并且业务系统可以自动产生财务系统对应的单据及凭证。...

    Java并发编程(学习笔记).xmind

    (3)简化异步事件的处理:服务器应用程序在接受来自多个远程客户端的请求时,如果为每个连接都分配一个线程并且使用同步IO,就会降低开发难度 (4)用户界面具备更短的响应时间:现代GUI框架中大都使用一个...

    NetTool.rar

    基本功能: 1:16进制数据和ASCII数据收发。 2:定时器自动发送。 3:自动从配置文件加载最后一次的界面设置。 4:自动从配置文件加载数据发送下拉框的... 9:四种模式,tcp服务器、tcp客户端、udp服务器、udp客户端。

    MVC设计模式.doc

    2. 由于页面视图状态管理,会有额外数据在客户端与服务端往返,高并发时会占用大量带宽。 Mvc 客户端的请求 1. 不和页面一一对应,URL 包含语义信息。 2. 对搜索引擎更加友好。 URL 路由和控制器顺序 1. 控制器 ...

    javasocket多线程[文].pdf

    每个客户端连接都对应一个服务器端的线程,用于处理客户端的请求。 代码分析 在给定的代码中,我们可以看到 Server 类继承自 Thread 类,用于实现服务器端的多线程编程。Server 类中有多个成员变量,包括 ...

    Android移动开发-使用HttpURLConnection实现多线程的下载

    假设服务器同时最多服务100个用户,在服务器中一条线程对应一个用户,100条线程在计算机内并发执行,也就是有CPU划分时间片轮流执行,如果A应用使用了99条线程下载文件,那么相当于占用了99个用户的资源,自然就拥有...

    勤哲excel服务器2010教程

    23.2.6 其它程序员用VBA编程接口 400 第24章、 网盘 405 24.1 安装网络硬盘服务器 405 24.2 网盘配置 406 24.2.1 管理控制台中的设置 406 24.2.2 注册网盘服务器和网盘根目录 406 24.3 权限设置 408 24.4 网盘的应用...

    JMeter接口压力测试.doc

    接口测试是指通过测试程序或工具,模拟客户端向服务器发送请求报文,服务器接收请求报文后对相应的报文做出处理,然后再把应答报文发送给客户端,客户端接收应答报文的过程。 使用 JMeter 做接口测试的过程可以分为...

Global site tag (gtag.js) - Google Analytics