Linux——多进程服务器与客户端并发通信

简介: Linux——多进程服务器与客户端并发通信

运用知识


套接字相关函数socket()、bind()、listen()、connect()、accept()、recv()、send()、select()、close()


https://blog.csdn.net/weixin_45525272/article/details/107732407


atoi


atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数,应用在计算机程序和办公软件中。int atoi(const char *nptr)函数会扫描参数 nptr字符串,会跳过前面的空白字符(例如空格,tab缩进)等。如果 nptr不能转换成 int 或者 nptr为空字符串,那么将返回 0 。特别注意,该函数要求被转换的字符串是按十进制数理解的。atoi输入的字符串对应数字存在大小限制(与int类型大小有关),若其过大可能报错-1。


htons


htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。


bzero()函数


原型:extern void bzero(void *s, int n);


参数说明:s 要置零的数据的起始地址;


n 要置零的数据字节个数。


用法:#include <string.h>


功能:置字节字符串s的前n个字节为零且包括‘\0’。


说明:bzero无返回值,并且使用string.h头文件,string.h曾经是posix标准的一部分,但是在POSIX.1-2001标准里面,这些函数被标记为了遗留函数而不推荐使用。在POSIX.1-2008标准里已经没有这些函数了。推荐使用memset替代bzero。


代码示例



服务器


/*************************************************************************
    > File Name: server_fork.c
    > Author: 杨永利
    > Mail: 1795018360@qq.com 
    > Created Time: 2020年08月01日 星期六 10时02分04秒
 ************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
int main(int argc, char* argv[]){
  if (argc!=2)
  {
    /* code */
    printf("参数错误!\n");
    return -1;
  }
  // 第一步 创建套接字描述符
  int server_socket=socket(PF_INET,SOCK_STREAM,0);
  //第二步 创建服务器地址结构
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(atoi(argv[1]));
    //INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。 一般来说,在各个系统中均定义成为0值。
    server_addr.sin_addr.s_addr = /*htonl()*/INADDR_ANY;
    // 第三步 绑定套接字
    if (bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)))
    {
      /* code */
      printf("绑定套接字失败!\n");
      return -1;
    }
    // 第四步 监听套接字
    if (listen(server_socket,10))
    {
      /* code */
      printf("监听套接字失败!\n");
      return -1;
    }
    // 第五步 多进程连接
    struct sockaddr_in client_addr;
    int client_addr_size=sizeof(client_addr);
    while(1)
    {
      int client_socket=0;
      bzero(&client_addr,sizeof(client_addr));
      if((client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_addr_size)) == -1)
      {
          printf("接收客户端失败!\n-----client_socket=%d",client_socket);
          return -1;
      }
      // 创建子进程 fork()
      /*
      负值:创建子进程失败。
      零:返回到新创建的子进程。
      正值:返回父进程或调用者。该值包含新创建的子进程的进程ID
      */
      int pid=fork();
      // 创建新的子进程成功
      if (pid==0)
      {
        // 创建临时客户端地址结构体用于存放开始accept到的客户端数据
        struct sockaddr_in client_in_addr;
        client_in_addr.sin_addr.s_addr=client_addr.sin_addr.s_addr;
        client_in_addr.sin_port=client_addr.sin_port;
        printf("来了一个新的客户端,ip是:%s,端口:%d\n", inet_ntoa(client_addr.sin_addr),client_in_addr.sin_port);
        // 关闭当前进程的套接字描述符,
        close(server_socket);
            char buf[1024];
            int recv_len;
            while(1)
            {
              bzero(buf, sizeof(buf));
                if((recv_len = recv(client_socket, buf, sizeof(buf), 0)) < 0)
                    return -1;
                // 判断如果接收到为0就有个客户端退出,跳出循环
                if(!recv_len){
                    printf("有客户端退出了,ip:%s 端口:%d\n", inet_ntoa(client_in_addr.sin_addr),client_in_addr.sin_port);//struct in_addr
                  break;
                }
                printf("客户端-%s -%d 对我说:%s\n",inet_ntoa(client_in_addr.sin_addr),client_in_addr.sin_port,buf);
              if(send(client_socket, buf, strlen(buf), 0) <= 0)
                    return -1;
            }
            close(client_socket);
            return 0;
      }
      // 返回给父进程子进程id  父进程就可以关闭接收的套接字描述符
        else if(pid > 0)
        {
            close(client_socket);
        }
        else
        //创建子进程失败,直接退出
           exit(-1);
    }
    return 0;
}


客户端


/*************************************************************************
    > File Name: client.c
    > Author: 杨永利
    > Mail: 1795018360@qq.com 
    > Created Time: 2020年08月01日 星期六 10时01分47秒
 ************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <strings.h>
int main(int argc, char* argv[]){
  if (argc != 3)
  {
    printf("参数错误!\n");
    return -1;
  }
  // 第一步创建网络通信套接字描述符
  int client_socket=socket(AF_INET,SOCK_STREAM,0);
  // 第二步创建服务器的地址结构体
  struct sockaddr_in server_addr;
  bzero(&server_addr,sizeof(server_addr));
  server_addr.sin_family=AF_INET;
  server_addr.sin_port=htons(atoi(argv[2]));
  server_addr.sin_addr.s_addr=inet_addr(argv[1]);
  // 第三步 连接服务器
  if (connect(client_socket,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1)
  {
    /* code */
    printf("连接服务器失败!\n");
    return -1;
  }
  printf("连接服务器成功!\n");
  char message[1024];
  bzero(message,sizeof(message));
  char buf[1024];
  printf("我是客户端,我将给服务器发送内容\n");
  printf("如果希望聊天退出,请输入q或者Q:\n");
  while(1)
  {
    printf("我发送给服务器的:\n");
    scanf("%s",buf);
    if (strlen(buf)==1)
    {
      if(buf[0] == 'q' || buf[0] == 'Q')
                break;
    }
    if(strlen(buf) != write(client_socket, buf, strlen(buf))){
            printf("write() error!\n");
            exit(-1);
        }
        if(read(client_socket, message, sizeof(message)) <= 0){
            printf("read() error!\n");
            return -1;
        }
        printf("服务器对我说:%s\n", message);
        bzero(buf, sizeof(buf));
        bzero(message, sizeof(message));
  }
  close(client_socket);
  printf("服务器关闭了!\n");
    return 0;
}
相关文章
|
10月前
|
弹性计算 安全 Linux
阿里云服务器ECS安装宝塔Linux面板、安装网站(新手图文教程)
本教程详解如何在阿里云服务器上安装宝塔Linux面板,涵盖ECS服务器手动安装步骤,包括系统准备、远程连接、安装命令执行、端口开放及LNMP环境部署,手把手引导用户快速搭建网站环境。
|
Web App开发 Linux 程序员
获取和理解Linux进程以及其PID的基础知识。
总的来说,理解Linux进程及其PID需要我们明白,进程就如同汽车,负责执行任务,而PID则是独特的车牌号,为我们提供了管理的便利。知道这个,我们就可以更好地理解和操作Linux系统,甚至通过对进程的有效管理,让系统运行得更加顺畅。
372 16
|
Unix Linux
对于Linux的进程概念以及进程状态的理解和解析
现在,我们已经了解了Linux进程的基础知识和进程状态的理解了。这就像我们理解了城市中行人的行走和行为模式!希望这个形象的例子能帮助我们更好地理解这个重要的概念,并在实际应用中发挥作用。
250 20
|
11月前
|
监控 Linux 网络安全
FinalShell SSH工具下载,服务器管理,远程桌面加速软件,支持Windows,macOS,Linux
FinalShell是一款国人开发的多平台SSH客户端工具,支持Windows、Mac OS X和Linux系统。它提供一体化服务器管理功能,支持shell和sftp同屏显示,命令自动提示,操作便捷。软件还具备加速功能,提升访问服务器速度,适合普通用户和专业人士使用。
3483 0
|
数据挖掘 Linux 数据库
服务器数据恢复—Linux系统服务器数据恢复案例
服务器数据恢复环境: linux操作系统服务器中有一组由4块SAS接口硬盘组建的raid5阵列。 服务器故障: 服务器工作过程中突然崩溃。管理员将服务器操作系统进行了重装。 用户方需要恢复服务器中的数据库、办公文档、代码文件等。
|
12月前
|
监控 Shell Linux
Linux进程控制(详细讲解)
进程等待是系统通过调用特定的接口(如waitwaitpid)来实现的。来进行对子进程状态检测与回收的功能。
277 0
|
12月前
|
存储 负载均衡 算法
Linux2.6内核进程调度队列
本篇文章是Linux进程系列中的最后一篇文章,本来是想放在上一篇文章的结尾的,但是想了想还是单独写一篇文章吧,虽然说这部分内容是比较难的,所有一般来说是简单的提及带过的,但是为了让大家对进程有更深的理解与认识,还是看了一些别人的文章,然后学习了学习,然后对此做了总结,尽可能详细的介绍明白。最后推荐一篇文章Linux的进程优先级 NI 和 PR - 简书。
339 0
|
12月前
|
存储 Linux Shell
Linux进程概念-详细版(二)
在Linux进程概念-详细版(一)中我们解释了什么是进程,以及进程的各种状态,已经对进程有了一定的认识,那么这篇文章将会继续补全上篇文章剩余没有说到的,进程优先级,环境变量,程序地址空间,进程地址空间,以及调度队列。
224 0
|
12月前
|
Linux 调度 C语言
Linux进程概念-详细版(一)
子进程与父进程代码共享,其子进程直接用父进程的代码,其自己本身无代码,所以子进程无法改动代码,平时所说的修改是修改的数据。为什么要创建子进程:为了让其父子进程执行不同的代码块。子进程的数据相对于父进程是会进行写时拷贝(COW)。
283 0
|
人工智能 搜索推荐 程序员
用 Go 语言轻松构建 MCP 客户端与服务器
本文介绍了如何使用 mcp-go 构建一个完整的 MCP 应用,包括服务端和客户端两部分。 - 服务端支持注册工具(Tool)、资源(Resource)和提示词(Prompt),并可通过 stdio 或 sse 模式对外提供服务; - 客户端通过 stdio 连接服务器,支持初始化、列出服务内容、调用远程工具等操作。
2925 5