IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 多进程socket服务器(c语言) -> 正文阅读

[系统运维]多进程socket服务器(c语言)

一、进程
1.进程是动态的,是程序的一次运行活动。

2.进程在内存中由数据段、堆栈段和代码段组成。c程序内存分布图如下
在这里插入图片描述
3.每个进程都有自己的进程标识符pid,可以调用getpid()来得到自己的pid,getppid()获得父进程的标识符

4.创建子进程:
pid_t fork(void);
fork函数调用一次返回两次,子进程的返回值是0,父进程的返回值是子进程的PID,因此通常利用这个区别去设置判断语句令父子进程执行不同的操作。一般来说,fork之后的父子进程的执行顺序是不确定的,这个取决于调度算法。
fork之后的处理方法:

  • fork之后子进程和父进程处理不同的代码段
  • fork之后调用exec,让子进程处理完全不同的程序

pid_t vfork(void);

vfork创建一个子进程,但是该子进程并不对父进程地址空间进行拷贝而是直接共享。因此如果让子进程对父进程数据进行读或写都可能产生段错误。主要原因在于,因为vfork产生的子进程是要exec一个新程序的,于是也就不会引用该地址空间了,不过子进程再调用exec()或
exit()之前,他将在父进程的空间中运行,但如果子进程想尝试修改数据域(数据段、堆、栈)都会带来未知的结果,就算写实复制copyonwrite(COW)( 这些数据区域由父子进程共享,内核将他们的访问权限改成只读,如果父进程和子进程中的任何一个试图修改这些区域的时候,内核再为修改区域的那块内存制作一个副本。)机制也不如索性不复制节约时间。

vfork保证子进程先运行,在它调用exec或者exit后父进程才可以被调用执

5.wait()和waitpid()
pid_t wait(int *status);
当一个进程正常或异常退出时,内核就会向其父进程发送SIGCHLD信号。因为子进程退出是一个异步事件,所以这种信号也是内核向父进程发送的一个异步通知。父进程可以选择忽略该信号,或者提供一个该信号发生时即将被执行的函数,父进程可以调用wait()或waitpid()可以用来查看子进程退出的状态。进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
参数:
参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针止。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,可以直接传NULL。
返回值:
如果成功,wait会返回被收集的子进程的进程ID
如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

pid_t waitpid(pid_t pid,int *status,int options);
系统调用waitpid和wait的作用是完全相同的,但waitpid多出了两个可由用户控制的参数pid和options,提供了另一种更灵活的方式。
二、使用多进程编程改写服务器的流程如下:
在这里插入图片描述

**服务器端的代码如下:**
/*********************************************************************************
 *      Copyright:  (C) 2021 jiaoer237
 *                  All rights reserved.
 *
 *       Filename:  socket_server_fork.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(11/23/2021)
 *         Author:  yanp <2405204881@qq.com>
 *      ChangeLog:  1, Release initial version on "11/23/2021 04:35:39 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>

#define MSG_STR "Hello yanp\n"
#define BACKLOG 13

void printf_usage(char *program);
int socket_init(char *listen_ip,int listen_port);

int main(int argc,char **argv)
{
    int daemon_run=0;
    char *program;
    int serv_port=0;
    int listen_fd;
    int clifd=-1;
    int rv=-2;
    int opt;

    struct sockaddr_in cliaddr;
    socklen_t cliaddr_len;
    pid_t pid;

    struct option long_options[] =/*参数解析包括需要监听的端口号和是否在后台运行*/
    {
        {"daemon", no_argument, NULL, 'b'},
        {"port", required_argument, NULL, 'p'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };

    program=argv[0];
    while((opt = getopt_long(argc, argv, "bp:h", long_options, NULL)) != -1)
    {
        switch(opt)
        {
            case 'b':
                daemon_run=1;
                break;
            case 'p':
                serv_port = atoi(optarg);/*将字符串转换成整型*/
                break;
            case 'h':
                printf_usage(program);
                break;
            default:
                break;
        }
    }
    if(!serv_port)
    {
        printf_usage(argv[0]);
        return -1;
    }
    if((listen_fd=socket_init(NULL,serv_port))<0)/*sockt函数初始化创建listen_fd*/
    {
        printf("socket_init failure error:%s",strerror(errno));
        return -2;
    }
    if(daemon_run)/*设置服务器在后台运行*/
    {
        daemon(0,0);
    }
    while(1)
    {
        printf("start accept new client income...\n");
        clifd=accept(listen_fd,(struct sockaddr *)&cliaddr,&cliaddr_len);/*accept调用接受新的客户端的连接,服务器通过clifd与新的客户端通信*/
        if(clifd<0)
        {
            printf("accept new client failure:%s",strerror(errno));
            continue;
        }
        printf("accept new client[%s:%d] successful\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));
        pid=fork();/*创建子进程,子进程包括clifd和listen_fd*/
        if(pid<0)
        {
            printf("create child process failure:%s\n",strerror(errno));
            close(clifd);
            continue;
        }
        else if(0==pid)/*子进程运行*/
        {   
            char buf[1024];
            close(listen_fd);/*子进程关闭listen_fd,clifd和新连接的客户端进行数据交换*/
            printf("child process communicate with new client\n");
            memset(buf,0,sizeof(buf));
            rv=read(clifd,buf,sizeof(buf));
            if(rv<0)
            {
                printf("Read data from client sockfd[%d] failure: %s\n", clifd,strerror(errno));
                close(clifd);
                exit(0);
            }
            else if(0==rv)
            {
                printf("Socket[%d] get disconnected\n", clifd);
                close(clifd);
                exit(0);
            }
            else if(rv>0)
            {
                printf("read %d bytes from client:%s\n",rv,buf);
            }
            rv=write(clifd, MSG_STR, strlen(MSG_STR));
            if(rv < 0)
            {
                printf("write to client by socket[%d] failure:%s\n",listen_fd,strerror(errno));
                close(clifd);
                exit(0);
            }
            sleep(1);
            printf("close client socket[%d] and child process exit\n", clifd);
            close(clifd);
            exit(0);/*读写一次后退出*/

        }
        else if(pid>0)/*父进程运行*/
        {
            close(clifd);/*父进程关闭clifd,通过listen_fd继续监听下一个客户端的连接*/
            continue;
        }

    }
    


   
}

void printf_usage(char *program)
{
    printf("使用方法:%s【选项】 \n", program);
    printf(" %s是一个服务器程序,用来等待客户端的连接\n",program);
    printf("\n传入参数\n");
    printf(" -b[daemon]设置程序在后台运行\n");
    printf(" -p[port ] 指定连接的端口号\n");
    printf(" -h[help ] 打印帮助信息\n");
    printf("\n例如: %s -b -p 8900\n", program);
    return;

}
int socket_init(char *listen_ip,int listen_port)/*socket初始化函数*/
{
    int listenfd;
    struct sockaddr_in servaddr;
                

    if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        printf("socket_server to create a TCP socket fd failure:[%s]\n",strerror(errno));
        return -1;
    }
    printf("create a tcp socket fd[%d] success\n",listenfd);

    int on=1;  
    if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)  
    {
        printf("setsockopt failure:%s",strerror(errno));
        return -2;
    }

    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(listen_port);
    if(!listen_ip)
    {
        servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    }
    else                         
    { 
        servaddr.sin_addr.s_addr=htonl(listen_port);
    }                                               
    if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)
    { 
        printf("socket[%d] bind on port[%d] for ip address failure:%s\n",listenfd,listen_port,strerror(errno));
        return -2;
    }
    printf("socket[%d] bind on port[%d] for ip address success\n",listenfd,listen_port);
    listen(listenfd,BACKLOG);
    printf("start listen on port[%d]\n",listen_port);
    return listenfd;
}

客户端的代码

/*********************************************************************************
 *      Copyright:  (C) 2021 jiaoer237
 *                  All rights reserved.
 *
 *       Filename:  socket_client.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(11/21/2021)
 *         Author:  yanp <2405204881@qq.com>
 *      ChangeLog:  1, Release initial version on "11/21/2021 08:49:25 PM"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <getopt.h>

#define MSG_STR "Hello yanp, Unix Network Program World!"

void print_usage(char *progname);

int main(int argc,char **argv)
{
    int conn_fd = -1;
    int rv = -1;
    char buf[1024];
    struct sockaddr_in serv_addr;
    int serv_port;
    char *serv_ip=NULL;
    char *program=NULL;
    int opt;

    struct option long_options[] =
    {
        {"ip",required_argument,NULL,'i'},
        {"port",required_argument, NULL, 'p'},
        {"help",no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };

    program=argv[0];
    while ((opt = getopt_long(argc, argv, "i:p:h", long_options, NULL)) != -1)
    {
        switch (opt)
        {
            case 'i':
            serv_ip=optarg;
            break;
            case 'p':
            serv_port = atoi(optarg);
            break;
            case 'h': /*  Get help information */
            print_usage(argv[0]);
            return 0;
            default:
            break;
        }
    }

    if( !serv_port ||!serv_ip)
    {
        print_usage(argv[0]);
        return -1;                                                                                    
    }

                                            
    conn_fd=socket(AF_INET,SOCK_STREAM,0);
    if(conn_fd<0)
    {
        printf("create client socket failure:%s\n",strerror(errno));
        return -1;
    }

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(serv_port);
    inet_aton(serv_ip,&serv_addr.sin_addr);

    if(connect(conn_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)
    {
        printf("client[%d] connect to server[%s:%d] failure:%s\n",conn_fd,serv_ip,serv_port,strerror(errno));
        return -1;
    }

    if(write(conn_fd,MSG_STR,strlen(MSG_STR))<0)
    {
        printf("write data to server[%s,%d] failure:%s\n",serv_ip,serv_port,strerror(errno));
        return -2;
    }
    
    memset(buf,0,sizeof(buf));
    rv=read(conn_fd,buf,sizeof(buf));
    if(rv<0)
    {
        printf("read data from server failure:%s\n",strerror(errno));
        return -3;
    }
    else if(rv==0)
    {
        printf("client connetc to server get disconnect\n");
        return -4;
    }
    printf("read %d bytes from server:'%s'\n",rv,buf);

    return 0;
}

void print_usage(char *program)
{
    printf("用法:%s选项 \n", program);
    printf(" %s 是一个客户端程序,需要指定连接服务器的ip地址和端口号\n",program);
    printf(" -i[ip ] 指定需要连接服务器的ip地址\n");
    printf(" -p[port ] 指定需要连接服务器的端口号\n");
    printf(" -h[help ] 打印帮助信息\n");
    printf("\n举例 %s -i 192.168.1.2 -p 12345\n", program);
    return ;
}

运行结果如下::在这里插入图片描述
在这里插入图片描述

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-11-25 08:31:26  更:2021-11-25 08:33:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 1:52:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码