`
memorymyann
  • 浏览: 271849 次
  • 性别: 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错误。只所以采用可能这个词,是因为某些内核会自动重启这些调用,从而使的主程序将继续在这个系统调用处阻塞。当然不是所有内核都会这么处理,所以后面我们要加上检查语句。

分享到:
评论

相关推荐

    基于JavaSocket多客户端并发通信聊天程序的设计与实现

    【JavaSocket多客户端并发通信聊天程序的设计与实现】 在Java编程中,Socket是进行网络通信的核心组件,尤其在构建多客户端并发聊天程序时,Socket扮演着至关重要的角色。本文将详细探讨基于JavaSocket的多客户端...

    c++实现socket:一个服务器对应多个客户端相互传递信息

    标题 "c++实现socket:一个服务器对应多个客户端相互传递信息" 指的是利用C++的socket API创建一个服务器程序,该服务器可以同时处理来自多个客户端的连接请求,并允许这些客户端之间交换数据。这种设计模式通常称为...

    基于Boost.Beast构建的易于使用的HTTP(S)客户端.zip

    2. **连接服务器**: 使用`boost::asio::ip::tcp::resolver`解析服务器的域名或IP地址,获取对应的socket地址。然后,使用`boost::asio::ssl::stream`或`boost::asio::ip::tcp::socket`建立到服务器的连接。 3. **...

    zinx —— golang —— MMO游戏服务器开发对应客户端

    本文将深入探讨Zinx框架如何与Golang结合,构建高效且稳定的MMO游戏服务器,并简单介绍客户端的相关内容。 Zinx框架的核心设计理念是模块化和可扩展性。它提供了基本的网络通信模块,支持TCP和UDP协议,方便开发者...

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

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

    c# tcp 基于完成端口开发 高性能 高并发 吞吐量大 包含服务端 客户端完整代码 支持最大连接数支持65535个长连接

    本资源提供的是一套基于C#语言实现的TCP服务器和客户端代码,利用了完成端口(IO Completion Ports, I/OCP)技术来优化高并发和高性能的网络通信。 完成端口是Windows系统中的一种高级I/O模型,它通过将I/O操作的...

    客户端与服务器的对话程序,有UI界面

    6. **文件结构**:由于提供的压缩包文件名为"服务器-客户端",我们可以推测压缩包内可能包含服务器端的Servlet代码、客户端的UI代码,以及可能的配置文件(如服务器的部署描述符web.xml)。解压后,开发者可以查看...

    Socket服务器与客户端编程(链表)

    总结一下,Socket服务器与客户端编程结合链表是一种常见的网络编程模式,特别是在处理并发连接时。通过链表,服务器可以高效地管理多个客户端连接,而Socket则提供了底层的通信机制。改端口可以根据实际需求调整...

    TDengine Windows客户端和服务器

    在Windows客户端中,你可以选择使用官方提供的命令行工具taos shell,或者利用SDK开发自定义应用,与服务器进行交互。SDK包括C、Java、Python、Go等语言版本,选择适合项目需求的语言进行开发。 对于Windows客户端...

    tcp并发服务器

    本篇将详细探讨TCP并发服务器的工作原理、实现方式以及相关技术。 首先,TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,它确保了数据包的顺序传输和错误检测。在TCP并发服务器中,...

    socket 一个服务器对应多个客户端使用多线程

    在标题"socket 一个服务器对应多个客户端使用多线程"中,提到的是使用Socket编程来设计一个服务器,该服务器能够同时处理来自多个客户端的连接请求,这通常被称为“一对一”或“一对多”模型。在Linux环境下,这种...

    C#写的服务器与客户端通信

    本篇文章将深入探讨使用C#进行服务器与客户端通信的关键概念、技术和实践方法。 一、C#网络编程基础 C#提供了丰富的库来支持网络通信,主要通过System.Net命名空间。在这个命名空间中,我们可以找到Socket类,它是...

    Java NIO实现多个客户端之间的消息互发,客户端与服务器完整代码

    6. 对于客户端间的消息互发,服务器在收到某个客户端的消息后,可以将该消息转发给其他已连接的客户端。 在给定的项目中,"client"包可能包含了C++客户端的源代码,实现了与Java NIO服务器的通信。而"sensor"包可能...

    单客户端与多服务器通讯

    本文将深入探讨如何使用SOCKET API实现一个单客户端与多个服务器的通讯,重点在于多线程和阻塞I/O模式的运用,以及在MFC(Microsoft Foundation Classes)环境下进行编程。 首先,我们来理解“单客户端与多服务器...

    客户端/服务器通信示例程序

    当服务器接收到新的客户端连接请求时,可以创建一个新的线程或启动一个异步操作来处理这个连接,使得服务器可以并发处理多个客户端。 总的来说,"客户端/服务器通信示例程序"是一个演示如何使用VC++ 6.0实现TCP...

    socket 单服务器/多客户端

    在IT领域,Socket编程是网络通信的核心技术之一,主要用于实现客户端和服务器之间的连接与通信。本项目名为"socket 单服务器/多客户端",显然关注的是如何构建一个能够处理多个客户端连接的单一服务器系统,并且修复...

    C# Socket_服务端向指定的客户端发送消息(包含服务器)

    在本文中,我们将深入探讨如何使用C#的Socket编程来实现一个简单的聊天应用程序,其中包含服务器和客户端的交互。...通过学习和实践这些概念,你可以创建出自己的网络应用程序,实现服务器与客户端之间的高效通信。

    Qt:Tcp服务器与客户端程序

    在提供的`chat`文件中,很可能包含了实现这些功能的代码,包括服务器的初始化、监听、连接处理,以及客户端的连接、发送和接收消息。分析和理解这些代码将有助于深入学习Qt和TCP通信的结合使用。

    客户端与服务器端的简单实现工具

    下面将详细阐述客户端与服务器端的基本概念、工作原理以及如何实现简单的消息传递。 1. **基本概念**: - **客户端**:客户端是用户操作的设备或程序,负责发起请求,通常执行用户界面功能,如输入、显示和交互。 ...

    DWR3实现服务器端向客户端精确推送消息

    在“DWR3实现服务器端向客户端精确推送消息”这一主题中,我们将深入探讨如何利用DWR3进行服务器到客户端的消息推送,以及这种技术的优势和应用。 首先,理解DWR3的工作原理是至关重要的。DWR3通过建立一个安全的...

Global site tag (gtag.js) - Google Analytics