进程间通信(管道、XSI--->SysV、网络套接字)

本文详细介绍了进程间通信的管道、信号量、共享内存以及网络套接字(UDP和TCP)的概念、函数用法和实例。在UDP通信中,讨论了广播和多播的实现。最后,展示了如何使用静态和动态进程池来处理并发TCP连接请求。
摘要由CSDN通过智能技术生成

进程间通信

1、管道

内核提供,单工,自同步机制(迁就慢的一方)
匿名管道
命名管道
相关函数:
匿名管道
pipe
----------------------- create pipe

SYNOPSIS
#include <unistd.h>
int pipe(int pipefd[2]);

pipefd[0] refersto the read end of the pipe.
pipefd[1] refers to the write end of the pipe.
0端代表读端,1端表示写端

RETURN VALUE
On success, zero is returned. On error, -1 is returned, and errno is
set appropriately.
创建的是匿名管道
在这里插入图片描述

实现父进程写,子进程读
在这里插入图片描述

代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFSIZE 1024
int main()
{
    int pd[2];
    pid_t pid;
    int len;
    char buf[BUFSIZE];
    if(pipe(pd)<0)
    {
        perror("pipe()");
        exit(1);
    }

    pid=fork();
    if(pid<0)
    {
        perror("fork()");
        exit(1);
    }

    if(pid==0)    //child
    {
        close(pd[1]);
        len=read(pd[0],buf,BUFSIZE);
        write(1,buf,len);    
        close(pd[0]);
        exit(0);
    }
    else
    {
        close(pd[0]);
        write(pd[1],"hello!",6);
        close(pd[1]);
        wait(NULL);
        exit(0);
    }
    

}
mpg123解码器相关
NAME
       mpg123 - play audio MPEG 1.0/2.0/2.5 stream (layers 1, 2 and 3)

SYNOPSIS
       mpg123 [ options ] file ... | URL ... | -

DESCRIPTION
       mpg123  reads  one  or more files (or standard input if ``-'' is speci‐
       fied) or URLs and plays them on the audio device (default)  or  outputs
       them to stdout.  file/URL is assumed to be an MPEG audio bit stream.
       
mpg123后面如果减号的话,表示指定播出是标准输入上来的内容
在命令行播放音乐的方式是mpg123 “歌名”
那么也可以     cat “歌名” |  mpg123 -
就是将一个命令的标准输出作为另一个命令的标准输出
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFSIZE 1024
int main()
{
    int pd[2];
    pid_t pid;
    int len;
    char buf[BUFSIZE];
    if(pipe(pd)<0)
    {
        perror("pipe()");
        exit(1);
    }

    pid=fork();
    if(pid<0)
    {
        perror("fork()");
        exit(1);
    }

    if(pid==0)    //child
    {
        close(pd[1]); //close write
        dup2(pd[0],0);
        close(pd[0]);
        fd=open("dev/null",O_RDWR);
        dup2(fd,1);
        dup2(fd,2);

        execl("/usr/bin/mpg123","mpg123","-",NULL);
        perror("execl()");
        exit(1);
    }
    else
    {
        close(pd[0]);   //close read
        //parent recive data from net and input pipe
        close(pd[1]);
        wait(NULL);
        exit(0);
    }
    

}

命名管道
相关函数
mkfifo()
------------------- make a FIFO special file (a named pipe)

   #include <sys/types.h>
   #include <sys/stat.h>
   int mkfifo(const char *pathname, mode_t mode);

   mkfifo()  makes a FIFO special file with name pathname.  mode specifies
   the FIFO's permissions.  It is modified by the process's umask  in  the
   usual way: the permissions of the created file are (mode & ~umask).

也有mkfifo命令就是用mkfifo函数封装出来的
创建一个管道
在这里插入图片描述

管道必须凑齐读写双方
在这里插入图片描述

如果右边的没有回车,那么左边终端光标就会一直在,当我们在右边终端按下回车,结果如下:
在这里插入图片描述

反过来也是一样的,如果线cat管道,回车以后也是有光标停留,直到有管道有写入的东西,光标才会消失

2、XSI—>SysV

    IPC -> Inter-Process-Communication 

主动端:先发包的一方
被动端:先收包的一方(先运行)

可以用于有亲缘关系的也可以用于没有亲缘关系的进程间的通信
有三种机制:
1、Message Queues

2、Semaphore Arrays

3、Shared Memory Segments

这三种机制都包含key
key是为了确定通信双方拿到同一个通信机制
相关函数:
ftok()
----------------为了让双方拿到同一个key值

   #include <sys/types.h>
   #include <sys/ipc.h>
   key_t ftok(const char *pathname, int proj_id);

xxxget(创建) xxxop(使用) xxxctl(控制)
xxx-> msg sem shm

看其中的创建的函数:

   #include <sys/types.h>
   #include <sys/ipc.h>
   #include <sys/msg.h>
   int msgget(key_t key, int msgflg);


   #include <sys/types.h>
   #include <sys/ipc.h>
   #include <sys/sem.h>
   int semget(key_t key, int nsems, int semflg);



   #include <sys/ipc.h>
   #include <sys/shm.h>
   int shmget(key_t key, size_t size, int shmflg);

三个函数都包含key,就是说当前任何机制的创建都要经过一个key值来产生一个id,然后通信双方拿着相同的id就相当于找到了同一种机制。得到同一个key值是为了创建某一个实例,创建实例,拿到id,就相当于用同一个机制来创建。
1、Message Queues
消息队列是双工的
相关函数:

msgget
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>
       int msgget(key_t key, int msgflg);
msgop
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/msg.h>
       int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);//发送
       ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
                      int msgflg);//接收
参数解析:
long msgtyp:表示是否挑消息来接收
msgflg:有无特殊要求
void *msgp:
       The msgp argument is a pointer to a  caller-defined  structure  of  the
       following general form:

           struct msgbuf {
               long mtype;       /* message type, must be > 0 */
               char mtext[1];    /* message data */
           };

msgctl
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);

   Valid values for cmd are:
   IPC_STAT
          Copy  information from the kernel data structure associated with
          msqid into the msqid_ds structure pointed to by buf.  The caller
          must have read permission on the message queue.

   IPC_SET
          Write  the  values  of  some  members  of the msqid_ds structure
          pointed to by buf to the kernel data structure  associated  with
          this  message  queue,  updating  also its msg_ctime member.  The
          following members of  the  structure  are  updated:  msg_qbytes,
          msg_perm.uid,  msg_perm.gid,  and  (the least significant 9 bits
          of) msg_perm.mode.  The effective UID  of  the  calling  process
          must  match  the owner (msg_perm.uid) or creator (msg_perm.cuid)
          of the message queue, or the caller must be privileged.   Appro‐
          priate  privilege  (Linux:  the  CAP_SYS_RESOURCE capability) is
          required to raise the msg_qbytes value beyond the system parame‐
          ter MSGMNB.

   IPC_RMID
          Immediately  remove  the  message  queue,  awakening all waiting
          reader and writer processes (with an error return and errno  set
          to EIDRM).  The calling process must have appropriate privileges
          or its effective user ID must be either that of the  creator  or
          owner  of  the message queue.  The third argument to msgctl() is
          ignored in this case.

消息队列实例:
proto.h文件内容(用来规定通信双发的协议)

#ifndef PROTO_H__
#define PROTO_H__

#define KEYPATH "/etc/services"
#define KEYPROJ 'g'
#define NAMESIZE 1024

struct msg_st
{
    long mtype;
    char name[NAMESIZE];
    int math;
    int chinese;
};

#endif

rcver.c文件内容

#include <stdio.h>
#include <stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int main()
{
    key_t key;
    int msgid;
    struct msg_st rbuf;
    
    key=ftok(KEYPATH,KEYPROJ);
    if(key<0)
    {
        perror("ftok()");
        exit(1);
    }
    
    

    msgid=msgget(key,IPC_CREAT|0600);
    if(msgid<0)
    {
        perror("msgget()");
        exit(1);
    }
    
    while(1)
    {
        if(msgrcv(msgid,&rbuf,sizeof(rbuf)-sizeof(long),0,0)<0)
        {
            perror("msgrc");
            exit(1);
        }
        printf("NAME=%s\n",rbuf.name);
        printf("MATH=%d\n",rbuf.math);
        printf("CHINESE=%d\n",rbuf.chinese);
    }


    msgctl(msgid,IPC_RMID,NULL);
    

    exit(0);
}

snder.c文件内容

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#include "proto.h"


int main()
{
    key_t key;
    int msgid;
    struct msg_st sbuf;
    key=ftok(KEYPATH,KEYPROJ);
    if(key<0)
    {
        perror("ftoke");
        exit(1);
    }

    msgid=msgget(key,0);
    if(msgid<0)
    {
        perror("msgget()");
        exit(1);
    }

    sbuf.mtype=1;
    strcpy(sbuf.name,"ALLEN");
    sbuf.math=rand()%100;
    sbuf.chinese=rand()%100;
    if(msgsnd(msgid,&sbuf,sizeof(sbuf)-sizeof(long),0)<0)
    {
        perror("msgsnd()");
        exit(1);
    }

    puts("OK!");

    exit(0);
}

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

当我们没有先运行rcver端,就发送了消息,会先把这些消息存起来,等到运行rcver的时候,就会先将这些信息打印到终端上
在这里插入图片描述

回车后就会直接接收四对消息
在这里插入图片描述

但是不可以一直放到缓存上。通过ulimit -a可以查看可以放多少
在这里插入图片描述

可以通过ipcs命令查看创建的消息队列
在这里插入图片描述

程序结束了,为什么还可以看到这个消息队列?
因为结束不是正常的结束,是通过ctrl+c结束的,函数最后不会运行到msgctl语句,所以不会销毁,可以利用信号处理函数,让这个语句在程序结束的时候可以运行到。(后面复盘的时候在尝试写)

也可以使用命令ipcrm销毁它
ipcrm - remove certain IPC resources
后缀选项:

   -M, --shmem-key shmkey//根据key来删除
          Remove  the  shared memory segment created with shmkey after the
          last detach is performed.

   -m, --shmem-id shmid//根据id删除
          Remove the shared memory segment identified by shmid  after  the
          last detach is performed.

   -Q, --queue-key msgkey//消息队列的key
          Remove the message queue created with msgkey.

   -q, --queue-id msgid
          Remove the message queue identified by msgid.

   -S, --semaphore-key semkey
          Remove the semaphore created with semkey.

   -s, --semaphore-id semid
          Remove the semaphore identified by semid.

在这里插入图片描述
在这里插入图片描述

可以利用状态机实现该过程

C端状态机:
在这里插入图片描述

S端自行实现
思路一:
proto1.h文件内容:

#ifndef PROTO1_H__
#define PROTO1_H__

#define KEYPATH "/etc/services"
#define KEYPROJ 'a'
#define PATHMAX 1024
#define DATAMAX 1024

enum
{

    MSG_PATH=1,
    MSG_DATA,
    MSG_EOT

};

struct msg_path_st
{
    long mtype;        /* must be MSG_PATH */
    char path[PATHMAX];    /* ASCIIZ 带尾0的串 */

};

typedef struct mag_data_st
{
    long mtype;        /* must be MSG_DATA */
    char data[DATAMAX];
    int datalen;
    
}msg_path_t;


typedef struct msg_eot_st
{

    long mtype;        /* must be MSG_EOT */
}msg_eot_t;


union msg_s2c_un
{
    long mtype;
    msg_data_t datamsg;
    msg_eot_t eotmsg;
};


#endif

思路2:

#ifndef PROTO1_H__
#define PROTO1_H__

#define KEYPATH "/etc/services"
#define KEYPROJ 'a'
#define PATHMAX 1024
#define DATAMAX 1024

enum
{

    MSG_PATH=1,
    MSG_DATA,
    MSG_EOT

};

struct msg_path_st
{
    long mtype;        /* must be MSG_PATH */
    char path[PATHMAX];    /* ASCIIZ 带尾0的串 */

};

typedef struct msg_s2c_st
{
    long mtype;        /* must be MSG_DATA or MSG_EOT*/
    int datalen;
    /*
    datalen > 0  :data
    datalen = 0  :eot
    */
    char data[DATAMAX];

    
}msg_path_t;

#endif

2、Semaphore Arrays
相关函数:

semget()
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>
       int semget(key_t key, int nsems, int semflg);
semop()
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semop(int semid, struct sembuf *sops, size_t nsops);

       int semtimedop(int semid, struct sembuf *sops, size_t nsops,
                      const struct timespec *timeout);


The  elements of this structure are of type struct sembuf,
       containing the following members:

           unsigned short sem_num;  /* semaphore number */
           short          sem_op;   /* semaphore operation */
           short          sem_flg;  /* operation flags */
semctl()
       #include <sys/types.h>
       #include <sys/ipc.h>
       #include <sys/sem.h>

       int semctl(int semid, int semnum, int cmd, ...);
   Valid values for cmd are:
   IPC_STAT  Copy information from the kernel  data  structure  associated
             with semid into the semid_ds structure pointed to by arg.buf.
             The argument semnum is ignored.   The  calling  process  must
             have read permission on the semaphore set.

   IPC_SET   Write  the  values  of some members of the semid_ds structure
             pointed to by arg.buf to the kernel data structure associated
             with  this semaphore set, updating also its sem_ctime member.
             The  following  members  of  the   structure   are   updated:
             sem_perm.uid, sem_perm.gid, and (the least significant 9 bits
             of) sem_perm.mode.  The effective UID of the calling  process
             must    match    the    owner   (sem_perm.uid)   or   creator
             (sem_perm.cuid) of the semaphore set, or the caller  must  be
             privileged.  The argument semnum is ignored.

   IPC_RMID  Immediately remove the semaphore set, awakening all processes
             blocked in semop(2) calls on the set (with  an  error  return
             and  errno set to EIDRM).  The effective user ID of the call‐
             ing process must match the creator or owner of the  semaphore
             set,  or  the caller must be privileged.  The argument semnum
             is ignored.

为什么会有这样的机制?

在这里插入图片描述

这种情况下就需要arr[2]数组来管理这两个资源

实例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#define PROCNUM 20
#define FNAME "/tmp/out"
#define LINESIZE 1024
static int semid;


static void P(void)
{
    struct sembuf op;
    op.sem_num=0;
    op.sem_op=-1;
    op.sem_flg=0;
    
    while(semop(semid,&op,1)<0)
    {
        if(errno!=EINTR||errno!=EAGAIN)
        {
            perror("semop");
            exit(1);
        }    

    }


}

static void V(void)
{    
    struct sembuf op;
    op.sem_num=0;
    op.sem_op=1;
    op.sem_flg=0;
    
    if(semop(semid,&op,1)<0)
    {
        perror("semop()");
        exit(1);
    }
    
}



static void func_add(void)
{
        FILE *fp;
    int fd;
        char linebuf[LINESIZE];
        fp=fopen(FNAME,"r+");
        if(fp==NULL)
        {
        perror("fopen()");
        exit(1);
        }

    
    P();
        fgets(linebuf,LINESIZE,fp);
        fseek(fp,0,SEEK_SET);
    sleep(1);
        fprintf(fp,"%d\n",atoi(linebuf)+1);
        fflush(fp);
    V();
    
    fclose(fp);
    return ;

}

int main()
{
      
        int i;
    pid_t pid;

    

    semid=semget(IPC_PRIVATE,1,0600);
    if(semid<0)
    {
        perror("semget");
        exit(1);
    }

    if(semctl(semid,0,SETVAL,1)<0)
    {
        perror("semctl()");
        exit(1);
    }

        for(i=0;i<PROCNUM;i++)
        {
                   pid=fork();       
        if(pid<0)
        {
            perror("fork()");
            exit(1);
        }
        if(pid==0)
        {
            func_add();
            exit(0);
        }
        
          
        }

        for(i=0;i<PROCNUM;i++)
                wait(NULL);

    semctl(semid,0,IPC_RMID);
        exit(0);
    
}

运行结果:

在这里插入图片描述

3、Shared Memory Segments
相关函数:

shmget()
       #include <sys/ipc.h>
       #include <sys/shm.h>
       int shmget(key_t key, size_t size, int shmflg);
shmop()
       #include <sys/types.h>
       #include <sys/shm.h>
       void *shmat(int shmid, const void *shmaddr, int shmflg);//把共享内存映射过来
       int shmdt(const void *shmaddr);//解除映射
参数解析
const void *shmaddr:映射到当前空间的那一块位置,可以写NULL,那么这块函数就会主动找一块可以用的空间
int shmflg:特殊要求无特殊要求就写0
返回值就是那一块真正的映射地址
shmctl()
       #include <sys/ipc.h>
       #include <sys/shm.h>
       int shmctl(int shmid, int cmd, struct shmid_ds *buf);

实例:

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#include <stdlib.h>

#define MEMSIZE 1024
int main()
{
        
    int shmid;
    char *ptr;
    pid_t pid;
    shmid=shmget(IPC_PRIVATE,MEMSIZE,0600);
    if(shmid<0)
    {
        perror("shmget()");
        exit(1);
    }

    pid=fork();
    if(pid<0)
    {
        perror("fork()");
        exit(1);
    }
    if(pid==0)    //child write
    {
        ptr=shmat(shmid,NULL,0);
        if(ptr==(void *)-1)
        {
            perror("shmat");
            exit(1);
        }
        strcpy(ptr,"Hello!");
        shmdt(ptr);
        exit(0);
    }
    else    //parent read
    {
        wait(NULL);
        ptr=shmat(shmid,NULL,0);
        if(ptr==(void *)-1)
        {
            perror("shmat()");
            exit(1);
        }
        puts(ptr);
        shmdt(ptr);
        shmctl(shmid,IPC_RMID,NULL);
        exit(0);
    }

    

    exit(0);
}

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

3、网络套接字

跨主机的传输要注意的问题
1、字节序的问题:大端存储:低地址处放高字节
小端存储:低地址处放低字节

主机字节序:host
网络字节序:network

转换:
to _:htons,htonl,ntohs,ntohl//(s和l分别代表两个字节的和3个字节的)

2、对齐:
结构体当中就会用到对其
struct
{
int i;
char ch;
float f;
};
在这里插入图片描述

以上结构体占的大小为12个字节
如果地址号可以整除字节大小就将其放到这个地址上
解决办法就是要告诉编译器,不对齐。
在定义一个结构体的时候,利用宏来告诉编译器

3、类型长度的问题:
int到底占几个字节

char有无符号

解决办法:int32_t, uint32_t ,i nt64_t, uint64_t, int8_t, uint8_t

socket:可以理解为中间层
在这里插入图片描述

 socket
-------------------------- create an endpoint for communication

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain(协议族), int type(上层怎么实现的,流式?报式?等等), int protocol(某一个协议));
返回值:
On  success,  a  file  descriptor  for  the new socket is returned.  On
error, -1 is returned, and errno is set appropriately.
协议族:
Name                            Purpose                                        Man page
AF_UNIX, AF_LOCAL               Local communication                   unix(7)        //本地协议
AF_INET                         IPv4 Internet protocols                 ip(7)        //IPv4协议
AF_INET6                        IPv6 Internet protocols                 ipv6(7)        //IPv6协议
AF_IPX                          IPX - Novell protocols            
AF_NETLINK                      Kernel user interface device         netlink(7)    //内核与用户态通信
AF_X25                          ITU-T X.25 / ISO-8208 protocol   x25(7)        
AF_AX25                         Amateur radio AX.25 protocol        //短波通信
AF_ATMPVC                       Access to raw ATM PVCs            //
AF_APPLETALK                    AppleTalk                                      ddp(7)    //局域网的一个协议
AF_PACKET                       Low level packet interface             packet(7)    //底层的协议
AF_ALG                          Interface to kernel crypto API            //

报式套接字:

  • 被动端:(先运行)

1、取得socket
2、给socket取得地址(绑定一个地址
3、收/发信息
4、关闭socket

端口号的使用最好使用1024以上的

  • 主动端:

1、取得socket
2、给socket取得地址(绑定一个地址)(可以省略)(会自动分配)
3、发/收信息
4、关闭socket

bind();//地址格式不统一
    ------------------ bind a name to a socket

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);

对于第二个参数,其实没有准确的struct sockaddr*类型
不同的协议族当中对于通信地址的约定式不一定
这个类型是捏造出来

The  actual  structure  passed for the addr argument will depend on the
address family.  The sockaddr structure is defined as something like:

           struct sockaddr    
          {
               sa_family_t sa_family;
               char        sa_data[14];
           }

具体内容取决于协议族的具体情况:

The  rules used in name binding vary between address families.  Consult
the manual entries in Section 7 for detailed information.  For  AF_INET
see  ip(7),  for  AF_INET6  see  ipv6(7),  for AF_UNIX see unix(7), for
AF_APPLETALK see ddp(7), for AF_PACKET see packet(7),  for  AF_X25  see x25(7) and for AF_NETLINK see netlink(7).

这里使用IP的协议族,那么我们man 7 ip

类型是struct sockaddr_in类型

struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET */
               in_port_t      sin_port;   /* port in network byte order */
               struct in_addr sin_addr;   /* internet address */
           };


/* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* address in network byte order */
           };//表示IP地址,这里不是点分式,在网络上传输,是一个IP地址的大整数

ip地址和大整数之间的转换
相关函数:

inet_pton()//点分转为大整数
----------convert IPv4 and IPv6 addresses from text to binary form

       #include <arpa/inet.h>
       int inet_pton(int af//协议族, const char *src//IP地址, void *dst//结果放在这);
inet_ntop()//大整数转化为点分
----------- convert IPv4 and IPv6 addresses from binary to text form

       #include <arpa/inet.h>
       const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
recvfrom();
recv, recvfrom, recvmsg
--------------------- receive a message from a socket

       #include <sys/types.h>
       #include <sys/socket.h>
       ssize_t recv(int sockfd, void *buf, size_t len, int flags);//流式
       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);//报式
       ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

被动端代码:

#include <stdio.h>
#include <stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define IPSTRSIZE 40

int main()
{
    int sd;
    
    struct msg_st rbuf;
    struct sockaddr_in laddr,raddr;
    char ipstr[IPSTRSIZE];
    socklen_t raddr_len;



    sd=socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);

    }
    
    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(RCVPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    
    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {

    perror("bind()");
    exit(1);
    
    }
    
    /*initilize*/
    
    raddr_len=sizeof(raddr);

    while(1)
    {

    recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len);
    inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
    printf("---MESSAGE FROM:%s:%d---\n",raddr.sin_addr,ntohs(raddr.sin_port));
    printf("NAME = %s\n",rbuf.name);
    printf("MATH=%d\n",ntohl(rbuf.math));
    printf("CHINESE=%d\n",ntohl(rbuf.chinese));





    }
    close(sd);


    exit(0);




}

netstat -anu查看状态,u代表udp
可以看到IP地址和port端口都打开了

主动端:(发送方)
相关函数:

sendto();

send, sendto, sendmsg
--------------------------------- send a message on a socket

    #include <sys/types.h>
       #include <sys/socket.h>
       ssize_t send(int sockfd, const void *buf, size_t len, int flags);//流式
       ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);//报式
       ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

主动端代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>
#include "proto.h"

int main(int argc,char *argv[])
{
    struct msg_st sbuf;
    struct sockaddr_in raddr;
    if(argc<2)
    {
        fprintf(stderr,"remember input host ip\n");
        exit(1);
    }
    
    int sd;
    sd=socket(AF_INET,SOCK_DGRAM,0);
    if(sd<0)
    {
        perror("socket():");
        exit(1);
        
    }

    //bind();
    
    strcpy(sbuf.name,"Allan");
    sbuf.math=htonl(rand()%100);
    sbuf.chinese=htonl(rand()%100);


    raddr.sin_family=AF_INET;
    raddr.sin_port=htons(atoi(RCVPORT));    
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);
    

    if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr))<0)
    {
        perror("sendto()");
        exit(1);
    }
    
    puts("OK!");
    
    close(sd);



    exit(0);
}

头文件代码:

#ifndef PROTO_H__
#define PROTO_H__
#define RCVPORT "1998"
#define NAMESIZE 11
#include <stdint.h>
struct msg_st
{


    uint8_t name[NAMESIZE];
    uint32_t math;
    uint32_t chinese;

}__attribute__((packed));       

#endif       

在这里面的名字我们规定了定长,在这里改善一下
头文件代码:

#ifndef PROTO_H__
#define PROTO_H__

#define RCVPORT "1998"
#define NAMEMAX  512-8-8
#include <stdint.h>
struct msg_st
{


    uint32_t math;
    uint32_t chinese;
    uint8_t name[1];

}__attribute__((packed));       

#endif       

被动端代码(接收端):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define IPSTRSIZE 40

int main()
{
    int sd;
    int size;    
    struct msg_st *rbufp;
    struct sockaddr_in laddr,raddr;
    char ipstr[IPSTRSIZE];
    socklen_t raddr_len;
    
    size=sizeof(struct msg_st)+NAMEMAX-1;
    rbufp=malloc(size);
    if(rbufp==NULL)
    {
        perror("malloc");
        exit(1);
    }


    sd=socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);

    }
    
    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(RCVPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    
    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {

    perror("bind()");
    exit(1);
    
    }
    
    /*initilize*/
    
    raddr_len=sizeof(raddr);

    while(1)
    {

    recvfrom(sd,rbufp,size,0,(void *)&raddr,&raddr_len);
    inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
    printf("---MESSAGE FROM:%s:%d---\n",ipstr,ntohs(raddr.sin_port));
    printf("NAME = %s\n",rbufp->name);
    printf("MATH=%d\n",ntohl(rbufp->math));
    printf("CHINESE=%d\n",ntohl(rbufp->chinese));

    }
    close(sd);
    exit(0);
}

主动端(发送端):

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>
#include "proto.h"

int main(int argc,char *argv[])
{
    struct msg_st *sbufp;
    int size;
    struct sockaddr_in raddr;
    if(argc<3)
    {
        fprintf(stderr,"remember input host ip\n");
        exit(1);
    }
    
    size=sizeof(struct msg_st)+strlen(argv[2]);
    sbufp=malloc(size);
    if(sbufp==NULL)
    {
        perror("malloc()");
        exit(1);


    }

    int sd;
    sd=socket(AF_INET,SOCK_DGRAM,0);
    if(sd<0)
    {
        perror("socket():");
        exit(1);
        
    }

    //bind();
    
    strcpy(sbufp->name,argv[2]);
    sbufp->math=htonl(rand()%100);
    sbufp->chinese=htonl(rand()%100);


    raddr.sin_family=AF_INET;
    raddr.sin_port=htons(atoi(RCVPORT));    
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);
    

    if(sendto(sd,sbufp,size,0,(void *)&raddr,sizeof(raddr))<0)
    {
        perror("sendto()");
        exit(1);
    }
    
    puts("OK!");
    
    close(sd);

    exit(0);

}

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

多点通信:广播(全网广播、子网广播),多播/组播
全网广播:
当前广播,默认是不让发出的(组播也一样

man 7 socket
找到Socket options
The socket options listed below can be set by using setsockopt(2) and read with getsockopt(2) with the socket level set to SOL_SOCKET(这层的名字) for all sockets. Unless otherwise noted, optval is a pointer to an int.
SO_BROADCAST
Set or get the broadcast flag. When enabled, datagram sockets
are allowed to send packets to a broadcast address. This option
has no effect on stream-oriented sockets

相关函数:

setsockopt,getsockopt
getsockopt, setsockopt - get and set options on sockets

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
某一个指定的socket,某一层上面会有不同的属性,对某一个属性进行设置

广播的实现:
主动端(发送端)代码:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>
#include "proto.h"

int main(int argc,char *argv[])
{
    struct msg_st sbuf;
    struct sockaddr_in raddr;
/*    if(argc<2)
    {
        fprintf(stderr,"remember input host ip\n");
        exit(1);
    }*/
    
    int sd;
    sd=socket(AF_INET,SOCK_DGRAM,0);
    if(sd<0)
    {
        perror("socket():");
        exit(1);
        
    }

    int val=1;
    if(setsockopt(sd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(val))<0)
    {
        perror("setsockopt()");
        exit(1);
    }
    
    //bind();
    memset(&sbuf,'\0',sizeof(sbuf));    
    strcpy(sbuf.name,"Allan");
    sbuf.math=htonl(rand()%100);
    sbuf.chinese=htonl(rand()%100);

    raddr.sin_family=AF_INET;
    raddr.sin_port=htons(atoi(RCVPORT));    
    inet_pton(AF_INET,"255.255.255.255",&raddr.sin_addr);
    

    if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr))<0)
    {
        perror("sendto()");
        exit(1);
    }
    
    puts("OK!");
    
    close(sd);

    exit(0);
}

被动端(接收端)代码:

#include <stdio.h>
#include <stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define IPSTRSIZE 40

int main()
{
    int sd;
    
    struct msg_st rbuf;
    struct sockaddr_in laddr,raddr;
    char ipstr[IPSTRSIZE];
    socklen_t raddr_len;



    sd=socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);

    }
    int val=1;
    if(setsockopt(sd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(val))<0)
    {
        perror("setsockopt()");
        exit(1);

    }
    
    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(RCVPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    
    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {

    perror("bind()");
    exit(1);
    
    }
    
    /*initilize*/
    
    raddr_len=sizeof(raddr);

    while(1)
    {

    recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len);
    inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
    printf("---MESSAGE FROM:%s:%d---\n",ipstr,ntohs(raddr.sin_port));
    printf("NAME = %s\n",rbuf.name);
    printf("MATH=%d\n",ntohl(rbuf.math));
    printf("CHINESE=%d\n",ntohl(rbuf.chinese));

    }
    close(sd);
    exit(0);


}

头文件

#ifndef PROTO_H__
#define PROTO_H__
#define RCVPORT "1998"
#define NAMESIZE 11
#include <stdint.h>
struct msg_st
{


        uint8_t name[NAMESIZE];
        uint32_t math;
        uint32_t chinese;

}__attribute__((packed));

#endif

在这里插入图片描述

组播的实现:
man 7 ip看多播的内容
找到Socket options

IP_MULTICAST_IF (since Linux 1.2)//创建一个多播组
Set the local device for a multicast socket. The argument for
setsockopt(2) is an ip_mreqn or (since Linux 3.5) ip_mreq struc‐
ture similar to IP_ADD_MEMBERSHIP, or an in_addr structure.
(The kernel determines which structure is being passed based on
the size passed in optlen.) For getsockopt(2), the argument is
an in_addr structure.

发送端(主动端):创建多播组

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <arpa/inet.h>
#include "proto.h"

int main(int argc,char *argv[])
{
    struct msg_st sbuf;
    struct sockaddr_in raddr;
/*    if(argc<2)
    {
        fprintf(stderr,"remember input host ip\n");
        exit(1);
    }*/
    
    int sd;
    sd=socket(AF_INET,SOCK_DGRAM,0);
    if(sd<0)
    {
        perror("socket():");
        exit(1);
        
    }
    struct ip_mreqn mreq;
    
    inet_pton(AF_INET,MTGROUP,&mreq.imr_multiaddr);
    inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);
    mreq.imr_ifindex=if_nametoindex("ens33");


    if(setsockopt(sd,IPPROTO_IP,IP_MULTICAST_IF,&mreq,sizeof(mreq))<0)
    {
        perror("setsockopt()");
        exit(1);
    }
    
    //bind();
    memset(&sbuf,'\0',sizeof(sbuf));    
    strcpy(sbuf.name,"Allan");
    sbuf.math=htonl(rand()%100);
    sbuf.chinese=htonl(rand()%100);


    raddr.sin_family=AF_INET;
    raddr.sin_port=htons(atoi(RCVPORT));    
    inet_pton(AF_INET,MTGROUP,&raddr.sin_addr);
    

    if(sendto(sd,&sbuf,sizeof(sbuf),0,(void *)&raddr,sizeof(raddr))<0)
    {
        perror("sendto()");
        exit(1);
    }
    
    puts("OK!");
    
    close(sd);



    exit(0);
}

被动端(接收端):加入多播组

#include <stdio.h>
#include <net/if.h>
#include <stdlib.h>
#include "proto.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#define IPSTRSIZE 40

int main()
{
    int sd;
    
    struct msg_st rbuf;
    struct sockaddr_in laddr,raddr;
    char ipstr[IPSTRSIZE];
    socklen_t raddr_len;



    sd=socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);

    }
    
    struct ip_mreqn mreq;
    inet_pton(AF_INET,MTGROUP,&mreq.imr_multiaddr);
    inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);
    mreq.imr_ifindex=if_nametoindex("ens33");
    
    
    if(setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0)
    {
        perror("setsockopt()");
        exit(1);

    }
    
    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(RCVPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    
    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {

    perror("bind()");
    exit(1);
    
    }
    
    /*initilize*/
    
    raddr_len=sizeof(raddr);

    while(1)
    {

    recvfrom(sd,&rbuf,sizeof(rbuf),0,(void *)&raddr,&raddr_len);
    inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
    printf("---MESSAGE FROM:%s:%d---\n",ipstr,ntohs(raddr.sin_port));
    printf("NAME = %s\n",rbuf.name);
    printf("MATH=%d\n",ntohl(rbuf.math));
    printf("CHINESE=%d\n",ntohl(rbuf.chinese));





    }
    close(sd);

    exit(0);
}

头文件代码:

#ifndef PROTO_H__
#define PROTO_H__
#define RCVPORT "1998"
#define NAMESIZE 11
#define MTGROUP "224.2.2.2"
#include <stdint.h>
struct msg_st
{


    uint8_t name[NAMESIZE];
    uint32_t math;
    uint32_t chinese;

}__attribute__((packed));       

#endif 
      

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

用报式套接字实现下面的内容
在这里插入图片描述

UDP传输特点:
UDP有个现象叫做丢包,抓包会看到字段TTL,这个指的是跳的路由个数,并不是时间单位的东西。
这个值再linux环境下默认是64。这个数值完全足够使用我们跳到任何一个路由上。丢包的现象不是TTL造成的,是由于阻塞现象造成的。
怎么解决:阻塞就会想到流控,不能用开环的流控(没有校验的流控),要用闭环流控
在这里插入图片描述

加上这个停等式流控,丢包率有下降吗?
没有,DATA会丢,ACK也会丢。这个机制牺牲了丢包率,确保收到正确内容。
C端状态机

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

以上就是一个完整的停等式流控实现要求.

流式套接字:

TCP传输协议分析:
在这里插入图片描述

点对点的

  • 客户端(C端)

1、获取socket
2、给socket取得地址
3、发送连接
4、收/发消息
5、关闭

  • 服务端(S端)

1、获取socket
2、给socket取得地址
3、将socket设置成监听模式
4、接受连接
5、收/发消息
6、关闭

相关函数:

listen()
------------------------------- listen for connections on a socket

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int listen(int sockfd, int backlog);

man 7 socket
Socket options
SO_REUSEADDR
Indicates that the rules used in validating addresses supplied
in a bind(2) call should allow reuse of local addresses. For
AF_INET sockets this means that a socket may bind, except when
there is an active listening socket bound to the address. When
the listening socket is bound to INADDR_ANY with a specific port
then it is not possible to bind to this port for any local
address. Argument is an integer boolean flag.

connect()
------------------------------ initiate a connection on a socket
       
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
accept()
       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>
       int accept(int sockfd, struct sockaddr *addr(对端地址), socklen_t *addrlen);

客户端代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "proto.h"
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main(int argc,char *argv[])
{
    struct sockaddr_in raddr;
    int sd;
    FILE * fp;
    long long stamp;
    sd=socket(AF_INET,SOCK_STREAM,0);
    if(sd<0)
    {
        perror("socket()");
        exit(1);

    }
    if(argc<2)
    {

        fprintf(stderr,"please input ip\n");
        exit(1);

    }

//    bind();

    raddr.sin_family=AF_INET;
    raddr.sin_port=htons(atoi(SERVERPORT));
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);
    if(connect(sd,(void *)&raddr,sizeof(raddr))<0)
    {

        perror("connect()");
        exit(1);

    }
    
    fp=fdopen(sd,"r+");
    if(fp==NULL)
    {
        perror("fdopen()");
        exit(1);
    }

    if(fscanf(fp,FMT_STAMP,&stamp)<1)
    {
        fprintf(stderr,"Bad stamp");
    }
    else
    {
        fprintf(stdout,"stamp=%lld\n",stamp);
    }

    fclose(fp);
    exit(0);

}

服务器代码:

#include <stdio.h>
#include <netinet/in.h>
#include "proto.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>

#define IPSTRSIZE 40


#define BUFSIZE 1024
static void server_job(int sd)
{
    int len;
    char buf[BUFSIZE];
    len=sprintf(buf,FMT_STAMP,(long long)time(NULL));
    if(send(sd,buf,len,0)<0)
    {
        perror("send()");
        exit(1);
        
    }



}

int main()
{
    int sd,newsd;
    char ipstr[IPSTRSIZE];
    struct sockaddr_in laddr,raddr;
    socklen_t raddr_len;
    sd=socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);
    }

    int val=1;
    if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt");
        exit(1);
    }
    

    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(SERVERPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    

    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {
        perror("bind()");
        exit(1);
    }

    if(listen(sd,200)<0)
    {
        perror("listen()");
        exit(1);
    
    }
    raddr_len=sizeof(raddr);
    while(1)
    {
        newsd=accept(sd,(void *)&raddr,&raddr_len);
        if(newsd<0)
        {
            perror("accept()");
            exit(1);
        }
        inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
        printf("CLient:%s:%d\n",ipstr,ntohs(raddr.sin_port));
        printf("%lld\n",11111111111);        
        server_job(newsd);
        close(newsd);
    }

    

    close(sd);



    exit(0);
}

头文件代码:

#ifndef PROTO_H__
#define PORTO_H__


#define SERVERPORT "1988"

#define FMT_STAMP "%lld\n"

#endif

运行server,命令查看。
在这里插入图片描述

流式套接字的并发版本
只需要修改server.c文件内容:修改后内容

#include <stdio.h>
#include <netinet/in.h>
#include "proto.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>

#define IPSTRSIZE 40


#define BUFSIZE 1024
static void server_job(int sd)
{
    int len;
    char buf[BUFSIZE];
    len=sprintf(buf,FMT_STAMP,(long long)time(NULL));
    if(send(sd,buf,len,0)<0)
    {
        perror("send()");
        exit(1);
        
    }



}

int main()
{
    int sd,newsd;
    pid_t pid;
    char ipstr[IPSTRSIZE];
    struct sockaddr_in laddr,raddr;
    socklen_t raddr_len;
    sd=socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);
    }

    int val=1;
    if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt");
        exit(1);
    }
    

    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(SERVERPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    

    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {
        perror("bind()");
        exit(1);
    }

    if(listen(sd,200)<0)
    {
        perror("listen()");
        exit(1);
    
    }
    raddr_len=sizeof(raddr);
    while(1)
    {
        newsd=accept(sd,(void *)&raddr,&raddr_len);
        if(newsd<0)
        {
            perror("accept()");
            exit(1);
        }

        pid=fork();
        if(pid < 0)
        {
            perror("fork()");
            exit(1);
        }

        if(pid==0)
        {
            inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
            printf("CLient:%s:%d\n",ipstr,ntohs(raddr.sin_port));
            server_job(newsd);
            close(newsd);
            exit(0);
        }
    }

    

    close(sd);
    exit(0);
}

运行结果(出现异常,./client不可以正常终止)
在这里插入图片描述

newsd出现问题

#include <stdio.h>
#include <netinet/in.h>
#include "proto.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>

#define IPSTRSIZE 40


#define BUFSIZE 1024
static void server_job(int sd)
{
    int len;
    char buf[BUFSIZE];
    len=sprintf(buf,FMT_STAMP,(long long)time(NULL));
    if(send(sd,buf,len,0)<0)
    {
        perror("send()");
        exit(1);
        
    }



}

int main()
{
    int sd,newsd;
    pid_t pid;
    char ipstr[IPSTRSIZE];
    struct sockaddr_in laddr,raddr;
    socklen_t raddr_len;
    sd=socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);
    }

    int val=1;
    if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt");
        exit(1);
    }
    

    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(SERVERPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    

    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {
        perror("bind()");
        exit(1);
    }

    if(listen(sd,200)<0)
    {
        perror("listen()");
        exit(1);
    
    }
    raddr_len=sizeof(raddr);
    while(1)
    {
        newsd=accept(sd,(void *)&raddr,&raddr_len);
        if(newsd<0)
        {
            perror("accept()");
            exit(1);
        }

        pid=fork();
        if(pid < 0)
        {
            perror("fork()");
            exit(1);
        }

        if(pid==0)
        {
            close(sd);//修改后
            inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
            printf("CLient:%s:%d\n",ipstr,ntohs(raddr.sin_port));
            server_job(newsd);
            close(newsd);
            exit(0);
        }
        close(newsd);//修改后
    }

    

    close(sd);

    exit(0);
}

运行后没有问题。

静态进程池套接字的实现:
client.c文件内容不变,头文件内容不变
server.c文件内容改变:

#include <stdio.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include "proto.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>

#define IPSTRSIZE 40
#define PROCNUM 4

#define BUFSIZE 1024
static void server_loop(int sd);
static void server_job(int sd)
{
    int len;
    char buf[BUFSIZE];
    len=sprintf(buf,FMT_STAMP,(long long)time(NULL));
    if(send(sd,buf,len,0)<0)
    {
        perror("send()");
        exit(1);
        
    }



}

int main()
{
    int sd;
    pid_t pid;
    int i;
    struct sockaddr_in laddr;
    socklen_t raddr_len;
    sd=socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);
    if(sd<0)
    {
        perror("socket()");
        exit(1);
    }

    int val=1;
    if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt");
        exit(1);
    }
    

    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(SERVERPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    

    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {
        perror("bind()");
        exit(1);
    }

    if(listen(sd,200)<0)
    {
        perror("listen()");
        exit(1);
    
    }
    for(i=0;i<PROCNUM;i++)
    {
        pid=fork();
        if(pid<0)
        {
            perror("fork()");
            exit(1);
        }
        if(pid==0)
        {
            server_loop(sd);
            exit(0);
        }
    }

    for(i=0;i<PROCNUM;i++)
        wait(NULL);

    close(sd);
    exit(0);
}
void server_loop(int sd)
{
    struct sockaddr_in raddr;
    socklen_t raddr_len;
    int newsd;
    char ipstr[IPSTRSIZE];
    raddr_len=sizeof(raddr);
    while(1)
    {
        newsd=accept(sd,(void *)&raddr,&raddr_len);
        if(newsd<0)
        {
            perror("accept()");
            exit(1);
        }
        inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
        printf("[%d]CLient:%s:%d\n",getpid(),ipstr,ntohs(raddr.sin_port));
                
        server_job(newsd);
        close(newsd);
    }

    close(sd);
    
}

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

静态的进程池,过于固定和死板,我们设定的是4个进程,就算是10000个请求,也得4个忙完了再接着忙下4个。动态进程池可以解决这个问题,比如说这个进程池中最少有10个进程最多可以容纳100个进程,当没有请求的时候就有10个防止请求量突然增大,发现10个进程都在忙碌,那么就继续fork产生进程,保证空闲的正在待命的进程不少于10个,最大不可以超过100个。当请求都服务完以后,我们就杀掉一些进程。

动态进程池的实现:
其他文件不变,./server.c文件内容:

#include <stdio.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include "proto.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#define MINSPARESERVER 5
#define MAXSPARESERVER 10
#define MAXCLIENTS 20
#define IPSTRSIZE 40
#define SIG_NOTIFY SIGUSR2
#define LINEBUFSIZE 80
enum
{
    STATE_IDLE=0,
    STATE_BUSY
};


struct server_st
{
    pid_t pid;
    int state;
    //int reuse;
    
};


static struct server_st *serverpool;
static int sd;
static int idle_count=0,busy_count=0;
static int scan_pool(void);
static void usr2_handler(int s)
{
    

return ;
}

static void server_job(int pos)
{
    int ppid;
    struct sockaddr_in raddr;
    socklen_t raddr_len;
    char ipstr[IPSTRSIZE];
    int client_sd;
    ppid=getppid();
    time_t stamp;
    char linebuf[LINEBUFSIZE];
    int len;
    raddr_len= sizeof(raddr);
    while(1)
    {
        serverpool[pos].state=STATE_IDLE;
        kill(ppid,SIG_NOTIFY);
        
        client_sd = accept(sd,(void *)&raddr,&raddr_len);
        if(client_sd < 0)
        {
            if(errno!=EINTR || errno !=EAGAIN)
            {
                perror("accept()");
                exit(1);
            }
        }
        
        serverpool[pos].state=STATE_BUSY;
        kill(ppid,SIG_NOTIFY);
        inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);
    //    printf("[%d]client:%s:%d\n",getpid(),ipstr,ntohs(raddr.sin_port));
                
        stamp=time(NULL);
        len=snprintf(linebuf,LINEBUFSIZE,FMT_STAMP,(long long int)stamp);
        send(client_sd,linebuf,len,0);
        /* if error */
        sleep(5);
        close(client_sd);
    }

}

static int add_1_server(void)
{
    int slot;
    pid_t pid;
    if(idle_count+busy_count>=MAXCLIENTS)
        return -1;
    for(slot=0; slot < MAXCLIENTS; slot++)
        if(serverpool[slot].pid==-1)
            break;
    
    serverpool[slot].state=STATE_IDLE;
    pid=fork();
    if(pid<0)
    {

        perror("fork()");
        exit(1);
    }
    
    if(pid==0)
    {
        server_job(slot);
        exit(0);
    }
    else
    {
        serverpool[slot].pid=pid;
        idle_count ++;
    }
    
    return 0;
}

static int del_1_server(void)
{

    int i;
    if(idle_count == 0)
    {
        return -1;
    }
    for(i=0;i<MAXCLIENTS;i++)
    {
        if(serverpool[i].pid != -1&& serverpool[i].state==STATE_IDLE)
        {
            kill(serverpool[i].pid,SIGTERM);
            serverpool[i].pid=-1;
            idle_count--;
            break;
        }
    }
    
    return 0;
}

static int scan_pool(void)
{
    int i;
    int busy=0,idle=0;
    for(i=0;i<MAXCLIENTS;i++)
    {
        if(serverpool[i].pid==-1)
        {
            continue;
        }
        if(kill(serverpool[i].pid,0))
        {
            serverpool[i].pid=-1;
            continue;
        }
        if(serverpool[i].state==STATE_IDLE)
        {
            idle++;
        }
        else if(serverpool[i].state == STATE_BUSY)
        {
            busy++;
        }
        else
        {
            fprintf(stderr,"Unknown state.\n");
        //    _exit(1);
            abort();
        }
    }
    idle_count=idle;
    busy_count=busy;
    return 0;
}

int main()
{

    int val;
    int i;
    sigset_t set,oset;
    struct sockaddr_in laddr;
    struct sigaction sa,osa;
    sa.sa_handler=SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags=SA_NOCLDWAIT;    
    
    sigaction(SIGCHLD,&sa,&osa);
    
    sa.sa_handler=usr2_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags=0;
    sigaction(SIG_NOTIFY,&sa,&osa);

    sigemptyset(&set);
    sigaddset(&set,SIG_NOTIFY);
    sigprocmask(SIG_BLOCK,&set,&oset);


    serverpool=mmap(NULL,sizeof(struct server_st)*MAXCLIENTS,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
    if(serverpool==MAP_FAILED)
    {
        perror("mmap()");
        exit(1);
    }

    for(i=0;i<MAXCLIENTS;i++)
    {
        serverpool[i].pid=-1;
    }

    sd=socket(AF_INET,SOCK_STREAM,0);
    if(sd<0)
    {
        perror("socket()");
        exit(1);
    }

    
    if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))<0)
    {
        perror("setsockopt");
        exit(1);
    }
    
    laddr.sin_family=AF_INET;
    laddr.sin_port=htons(atoi(SERVERPORT));
    inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);
    if(bind(sd,(void *)&laddr,sizeof(laddr))<0)
    {
        perror("bind()");
        exit(1);
    }

    if(listen(sd,100)<0)
    {
        perror("listen()");
        exit(1);
    }

    for(i=0;i<MINSPARESERVER;i++)
    {
        add_1_server();

    }

    while(1)
    {
        sigsuspend(&oset);

        scan_pool();

        //contrl the pool
        if(idle_count > MAXSPARESERVER)
        {
            for(i=0;i< (idle_count-MAXSPARESERVER);i++)
                del_1_server();
        }
        else if(idle_count < MINSPARESERVER)
        {
            for(i=0;i<(MINSPARESERVER-idle_count);i++)
            {
                add_1_server();

            }
        }
        
        //printf the pool
        for(i=0;i<MAXCLIENTS;i++)
        {
            if(serverpool[i].pid==-1)
                putchar(' ');
            else if(serverpool[i].state==STATE_IDLE)
                putchar('.');
            else
                putchar('x');    
        }
        putchar('\n');
        
        
        
    }

    sigprocmask(SIG_SETMASK,&oset,NULL);
    
    exit(0);

}

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

在终端每一秒一个请求,每一个请求的响应时间时5秒钟,每秒钟发一个,到五秒以后就可以看到一个比较稳定的状态,也就是说当前会有五个busy还有五个idle
在这里插入图片描述

再另一个终端上发出相同的请求,等到稳定以后就可以看到5个idle和10个busy
在这里插入图片描述

再用第三个终端发出同样的请求,这个时候达到上限。会看到五个.或是六个.,六个原因是,说明当前的server刚释放,还没有产生新的连接任务。
在这里插入图片描述

第四个终端,总体大小不会扩张,因为约定的进程池中最多20个server端总量。
在这里插入图片描述

将终端命令杀掉就可以看到.在慢慢变多。

运用accept函数时所出现的的“Invalid Argument”时的错误,主要有以下两种误用:
出错情况1: 在参数1处出错,多半情况是sockfd值非法,也即是没有绑定(bind),监听(listen)或者是负值等等;
出错情况2:在参数3处出错,没有给addrlen传入合法的值,或者是没有初始化(addrlen= sizeof(addr))

URL FS(这两者是一个映射的关系)
/ /var/www
/test/ /var/www/test

将图片抓下来:
先将图片移到/var/www/html/下
在这里插入图片描述

在浏览器中输入127.0.0.1/test.jpg就可以获得图片
在这里插入图片描述

利用代码抓取图片:
代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 1024

int main(int argc,char *argv[])
{
    struct sockaddr_in raddr;
    int sd;
    FILE * fp;
    char rbuf[BUFSIZE];
    long long stamp;
    sd=socket(AF_INET,SOCK_STREAM,0);
    if(sd<0)
    {
        perror("socket()");
        exit(1);

    }
    if(argc<2)
    {

        fprintf(stderr,"please input ip\n");
        exit(1);

    }

//    bind();

    raddr.sin_family=AF_INET;
    raddr.sin_port=htons(80);
    inet_pton(AF_INET,argv[1],&raddr.sin_addr);
    if(connect(sd,(void *)&raddr,sizeof(raddr))<0)
    {

        perror("connect()");
        exit(1);

    }
    
    fp=fdopen(sd,"r+");
    if(fp==NULL)
    {
        perror("fdopen()");
        exit(1);
    }
    
    int len;
    fprintf(fp,"GET /test.jpg\r\n\r\n");
    fflush(fp);
    
    while(1)
    {
        len=fread(rbuf,1,BUFSIZE,fp);
        if(len<=0)
            break;
        fwrite(rbuf,1,len,stdout);

    }
    fclose(fp);
    exit(0);

}

./webdl 127.0.0.1回车止呕会看到一大堆的乱码,这就是图片
在这里插入图片描述

将这些乱码放到文件中,让后用eog查看就可以看到图片
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值