• 4412 进程间通信


    无名管道

    从最早的UNIX 系统开始,无名管道的通信方式就存在,有点类似硬
    件中的串口,从最初的设计者定型之后,这种通信模型就一直延续到
    今天,说明无名管道当初的设计就极具科学性
    无名管道有一定的局限性
    它是属于半双工的通信方式
    只有具有亲缘关系的的进程才能使用这种通信方式,也就是父进程和
    子进程之间
    man 2 pipe

    int pipe(int pipefd[2])
    参数pipefd[0]:用于读管道
    参数pipefd[1]:用于写管道
    返回值:执行成功返回0,失败返回-1
    man 7 pipe
    官方文档中的例程
    编写编译运行测试

    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    //进程读函数
    void read_data(int *);
    //进程写函数 
    void write_data(int *);
    
    
    int main(int argc,char *argv[])
    {
        int pipes[2],rc;
        pid_t pid;
            
        rc = pipe(pipes);    //创建管道                 
        if(rc == -1){
            perror("
    pipes
    ");
            exit(1);
        }
            
        pid = fork();    //创建进程 
    
        switch(pid){
            case -1:
                perror("
    fork
    ");
                exit(1);
            case 0:
                read_data(pipes);    //相同的pipes
            default:
                write_data(pipes);    //相同的pipes
        }    
        return 0;
    }
    
    //进程读函数
    void read_data(int pipes[])
    {
        int c,rc;
        
        //由于此函数只负责读,因此将写描述关闭(资源宝贵)
        close(pipes[1]);
        
        //阻塞,等待从管道读取数据
        //int 转为 unsiged char 输出到终端
        while( (rc = read(pipes[0],&c,1)) > 0 ){          
            putchar(c);                                      
        }
    
        exit(0);
    }
    
    //进程写函数
    void write_data(int pipes[])
    {
        int c,rc;
    
        //关闭读描述字
        close(pipes[0]);                          
    
        while( (c=getchar()) > 0 ){
            rc = write( pipes[1], &c, 1);    //写入管道
            if( rc == -1 ){
                perror("Parent: write");
                close(pipes[1]);
                exit(1);
            }
        }
    
        close( pipes[1] );
        exit(0);
    }
    pipe

    有名管道

    无名管道只能用于有亲缘关于的进程通信,有名管道可以实现无亲缘
    关系的通信
    有名管道fifo 给文件系统提供一个路径,这个路径和管道关联,只要
    知道这个管道路径,就可以进行文件访问,fifo 是指先进先出,也就
    是先写入的数据,先读出来
    有名管道的读写速度非常快
    man 3 mkfifo

    int mkfifo(const char *pathname, mode_t mode)
    参数*pathname:路径名,管道名称
    参数mode:管道的权限
    返回值:成功返回0,错误返回-1
    编写编译运行测试
    代码有点多,切分为多个视频

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdlib.h>
    #include <string.h>
    
    void filecopy(FILE *ifp, char *buf);
    
    int main()
    {
            FILE *fp1;
            long int i = 100000;
            char buf[] = "I want to study Linux!
    ";
            char *file1 = "data.txt";
    
            printf("begin!
    ");
    
            if((fp1 = fopen(file1, "a+")) == NULL) {
                    printf("can't open %s 
    ", file1);
            }
    
            while(i--) {
                    filecopy(fp1, buf);
            }
    
            fclose(fp1);
            printf("over!
    ");
            return 0;
    }
    
    
    void filecopy(FILE *ifp, char *buf)
    {
            char c;
            int i,j;
            j = 0;
            i = strlen(buf) - 1;
            while(i--) {
                    putc(buf[j], ifp);
                    j++;
            }
            putc('
    ',ifp);
    }
    创建数据文件

    写管道

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    #define PIPE_BUF 1024
    
    int main()
    {
            const char *fifo_name = "my_fifo";
            char *file1 = "data.txt";
            int pipe_fd = -1;
            int data_fd = -1;
            int res = 0;
            const int open_mode = O_WRONLY;
            int bytes_send = 0;
            char buffer[PIPE_BUF + 1];
    
            if(access(fifo_name, F_OK) == -1) {
                    //管道文件不存在
                    //创建命名管道
                    res = mkfifo(fifo_name, 0777);
                    if(res != 0) {
                            fprintf(stderr, "Could not create fifo %s
    ", fifo_name);
                            exit(EXIT_FAILURE);
                    }
            }
    
            printf("Process %d opening FIFO O_WRONLY
    ", getpid());
            //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件
            pipe_fd = open(fifo_name, open_mode);
            data_fd = open(file1, O_RDONLY);
            printf("Process %d result %d
    ", getpid(), pipe_fd);
    
            if(pipe_fd != -1) {
                    int bytes_read = 0;
                    bytes_read = read(data_fd, buffer, PIPE_BUF);
                    buffer[bytes_read] = '';
                    while(bytes_read > 0) {
                            //向FIFO文件写数据
                            res = write(pipe_fd, buffer, bytes_read);
                            if(res == -1) {
                                    fprintf(stderr, "Write error on pipe
    ");
                                    exit(EXIT_FAILURE);
                            }
    
                            //累加写的字数,并继续读取数据
                            bytes_send += res;
                            bytes_read = read(data_fd, buffer, PIPE_BUF);
                            buffer[bytes_read] = '';
                    }
                    close(pipe_fd);
                    close(data_fd);
            } else
                    exit(EXIT_FAILURE);
    
            printf("Process %d finished
    ", getpid());
            exit(EXIT_SUCCESS);
    }
    writepipe

    读管道

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define PIPE_BUF 1024
    
    int main()
    {
            const char *fifo_name = "my_fifo";
            int pipe_fd = -1;
            int data_fd = -1;
            int res = 0;
            int open_mode = O_RDONLY;
            char buffer[PIPE_BUF+1];
            int bytes_read = 0;
            int bytes_write = 0;
    
            //清空缓冲数组
            memset(buffer, '', sizeof(buffer));
    
            printf("Process %d opening FIFO O_RDONLY
    ", getpid());
            //以只读阻塞方式打开管道文件,注意与fifowrite.c文件中的FIFO同名
            pipe_fd = open(fifo_name, open_mode);
            //以只读方式创建保存数据文件
            data_fd = open("DataFromFIFO.txt", O_WRONLY|O_CREAT, 0644);
            printf("Process %d result %d
    ", getpid(), pipe_fd);
    
            if(pipe_fd != -1) {
                    do {
                            //读取FIFO中的数据,并把它保存在文件DataFromFIFO.txt文件中
                            res = read(pipe_fd, buffer, PIPE_BUF);
                            bytes_write = write(data_fd, buffer, res);
                            bytes_read += res;
                    } while(res >0);
    
                    close(pipe_fd);
                    close(data_fd);
            } else
                    exit(EXIT_FAILURE);
    
            printf("Process %d finished, %d bytes read
    ", getpid(), bytes_read);
            exit(EXIT_SUCCESS);
    
    }
    readpipe

    消息队列

    消息队列就是一个消息的链表。可以把消息看作一个记录,具有特定的格式以及特定的优先级
    对消息队列有写权限的进程可以向其中按照一定的规则添加新消息;
    对消息队列有读权限的进程则可以从消息队列中读走消息。
    man 2 msgrcv
    函数ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long
    msgtyp,int msgflg)
    参数msqid:消息队列的标识码
    参数*msgp:指向消息缓冲区的指针
    参数msgsz:消息的长短
    参数msgflg:标志位
    返回值:成功返回数据长度,错误返回-1

    函数int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
    参数msqid:消息队列的标识码
    参数*msgp:指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构
    参数msgsz:消息的长短
    参数msgflg:标志位
    返回值:成功返回0,错误返回-1

    结构体msgp,是一个标准的通用结构
    struct msgstru{
    long mtype; //大于0
    char mtext[nbyte];}
    函数int msgget(key_t key, int msgflg)
    参数key:消息队列关联的标识符
    参数msgflg:消息队列的建立标志和存取权限。IPC_CREAT 如果内核中没有此队列则创建它;IPC_EXCL 当和IPC_CREAT 一起使用时,如果队列已经存在,则失败
    返回值:执行成功则返回消息队列的标识符,否则返回-1
    编写编译运行测试
    代码有点多,切分为多个视频

     补充

    函数ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg)
    参数msgtyp
    msgtyp等于0 ,则返回队列的最早的一个消息
    msgtyp大于0,则返回其类型为mtype的第一个消息
    msgtyp小于0,则返回其类型小于或等于mtype参数的绝对值的最小的一个消息
    参数msgflg:标志位为0,则表示忽略

     发送

    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/msg.h>
    #include <errno.h>
    #include <stdlib.h>
    
    #define MAX_TEXT 512
    
    //需要自己设置
    struct msg_st {
        long int msg_type;
        char text[MAX_TEXT];
    };
    
    int main(void)
    {
        struct msg_st data;
        char buffer[BUFSIZ];        //BUFSIZ是通用size
        int msgid = -1;
        int running = 1;
    
        msgid = msgget((key_t)1234, IPC_CREAT|0666);
        if(msgid == -1) {
            fprintf(stderr, "msgget failed with error:%d
    ", errno);
            exit(EXIT_FAILURE);
        }
    
        while(running) {
            printf("Enter some text:");
            fgets(buffer, BUFSIZ, stdin);
            data.msg_type = 1;
            strcpy(data.text, buffer);
            if(msgsnd(msgid, (void *)&data, MAX_TEXT, 0) < 0) {
                fprintf(stderr, "msgsnd failed
    ");
                exit(EXIT_FAILURE);
            }
            if(strncmp(buffer, "end", 3) == 0) {
                running = 0;
            }
            sleep(1);
        }
    
        return 0;
    }
    msgsend

    接收

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/msg.h>
    #include <errno.h>
    
    #define MAX_TEXT 512
    
    //需要自己设置
    struct msg_st {
        long int msg_type;
        char text[MAX_TEXT];
    };
    
    int main()
    {
        struct msg_st data;
        char buffer[BUFSIZ];        //BUFSIZ是通用size
        int msgid = -1;
        int running = 1;
        long int msgtype = 0;
    
        msgid = msgget((key_t)1234, 0666|IPC_CREAT);
        if(msgid == -1) {
            fprintf(stderr, "msgget failed with error:%d
    ", errno);
            exit(EXIT_FAILURE);
        }
    
        while(running) {
            if(msgrcv(msgid, (void *)&data, MAX_TEXT, msgtype, 0) < 0) {
                fprintf(stderr, "msgrcv failed with error:%d
    ", errno);
                exit(EXIT_FAILURE);
            }
            printf("You wrote:%s
    ", data.text);
            if(strncmp(data.text, "end", 3) == 0)
                running = 0;
        }
    
        if(msgctl(msgid, IPC_RMID, NULL) < 0) {
            fprintf(stderr, "msgctl(IPC_RMID) failed
    ");
            exit(EXIT_FAILURE);
        }
    
        return 0;
    }
    msgrcv

    信号

    信号用于处理异步事件,信号的通信方式理解起来还是有一定难度的,
    它既可以在一个进程内进行通信,发送信号给进程,又可以用于进程
    外通信
    man 2 alarm
    man 2 signal

    unsigned int alarm(unsigned int seconds)
    参数seconds:闹钟的时间,单位为秒
    返回值:成功返回0 或者返回剩余时间;错误返回-1


    sighandler_t signal(int signum, sighandler_t handler);
    参数signum:等待的信号
    参数handler:信号到来之后,触发的处理方式
    返回值:成功返回0,错误返回-1
    编写编译运行测试
    两个独立程序

    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    
    void alarmhandler(int sig)
    {
        printf("hello
    ");
    }
    
    int main()
    {
        int i;
        signal(SIGALARM, alarmhandler);
        alarm(5);
    
        for(i=1;i<7;i++) {
            printf("sleep %d ...
    ", i);
            sleep(1);
        }
    
        return 0;
    }
    signal_hello

    以下情况会产生信号
    按下按键;硬件异常;kill函数或者命令等
    常见信号
    SIGALRM:闹钟
    SIGHUP:终端发出的结束信号
    SIGINT:键盘的ctrl+c
    SIGKILL:kill命令产生的信号
    SIGSTOP:键盘ctrl+z

    函数pause
    用于捕捉进程挂起直到捕捉到信号
    编写编译运行测试

    #include <stdio.h>
    #include <unistd.h>
    #include <signal.h>
    
    void handler(int sig)
    {
        printf("Handler the signal %d
    "); 
    }
    
    int main(void)
    {
        sigset_t sigset;        //记录屏蔽字
        sigset_t ign;           //记录被阻塞的信号
        struct sigaction act;
    
        //清空信号集
        sigemptyset(&sigset);
        sigemptyset(&ign);
    
        //向信号集添加信号SIGINT
        sigaddset(&sigset, SIGINT);
    
        //设置处理函数和信号集
        act.sa_handler = handler;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGINT, &act,  0);
    
        printf("Wait the signal SIGINT...
    "); 
        pause();        //挂起进程等待信号
    
        //设置进程屏蔽字,屏蔽字为SIGINT
        sigprocmask(SIG_SETMASK, &sigset, 0);
        printf("Wait the signal Ctrl+c ...
    ");
        sleep(10);
    
        //测试信号SIGINT是否被屏蔽
        sigpending(&ign);
        //测试是否加入了信号集
        if(sigismember(&ign, SIGINT))
            printf("The SIGINT signal has ignored
    ");
    
        //在信号集中删除信号
        sigdelset(&sigset, SIGINT);
        printf("Wait the signal SIGINT ...
    ");
    
        //将进程的屏蔽字重新设置,取消对SIGINT的屏蔽
        //挂起进程
        sigsuspend(&sigset);
    
        printf("The app exit in 5 seconds!
    ");
        sleep(5);
        exit(0);
    }
    signal

    信号量 Semaphore

    接着介绍一下 semget 函数的用法。
    int semget(key_t key, int nsems, int semflg);
    参数 key:一个用来允许不相关的进程访问相同信号量的整数值。
    参数 nsems:需要的信号量数目。这个值通常总是 1。

    参数 semflg:标记集合,与 open 函数的标记十分类似。
    返回值:成功返回标识符,用于其它信号函数,错误返回-1。

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <sys/sem.h>
    
    union semun {
        int val;
        struct semid_ds *buf;
        unsigned short *arry;
    };
    
    int sem_id = 0;
    
    int set_semvalue();
    void del_semvalue();
    int semaphore_p();
    int semaphore_v();
    
    int main(int argc, char *argv[])
    {
        char message = 'X';
        int i = 0;
        //创建信号量
        sem_id = semget((key_t)1234, 1, 0666|IPC_CREAT);
    
        if(argc > 1) {
            //程序第一次被调用,初始化信号量
            if(!set_semvalue()) {
                fprintf(stderr, "Failed to initialize semaphore
    ");
                exit(EXIT_FAILURE);
            }
            //设置要输出到屏幕中的信息,即其参数的第一个字符
            message = argv[1][0];
            sleep(2);
        }
    
        for(i=0;i<10;i++) {
            //进入临界区
            if(!semaphore_p())
                exit(EXIT_FAILURE);
            //想屏幕中输出数据
            printf("%c", message);
            //清理缓冲区,然后休眠随机时间
            fflush(stdout);
            sleep(rand()%3);
            //离开邻接区前再一次向屏幕输出数据
            printf("%c", message);
            fflush(stdout);
            //离开临界区,休眠随机事件后继续循环
            if(!semaphore_v())
                exit(EXIT_FAILURE);
            sleep(rand()%2);
        }
    
        sleep(10);
        printf("
    %d - finished
    ", getpid());
    
        if(argc > 1) {
            //如果程序是第一次被调用,则在退出前删除信号量
            sleep(3);
            del_semvalue();
        }
        exit(EXIT_SUCCESS);
    }
    
    int set_semvalue()
    {
        //用于初始化信号量,在使用信号量前必须这样做
        union semun sem_union;
    
        sem_union.val = 1;
        if(semctl(sem_id, 0, SETVAL, sem_union) == -1)
            return 0;
        return 1;
    }
    
    void del_semvalue()
    {
        //删除信号量
        union semun sem_union;
    
        if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
            fprintf(stderr, "Failed to delete semaphore
    ");
    }
    
    int semaphore_p()
    {
        //对信号量做减1操作,即等待P(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = -1;
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1) {
            fprintf(stderr, "semaphore_p failed
    ");
            return 0;
        }
        return 1;
    }
    
    int semaphore_v()
    {
        //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = 1;
        sem_b.sem_flg = SEM_UNDO;
        if(semop(sem_id, &sem_b, 1) == -1) {
            fprintf(stderr, "semphore_v failed
    ");
            return 0;
        }
        return 1;
    }
    semaphore

    共享内存 shmdata

    共享内存是进程间通信中最简单的方式之一。共享内存在各种进程间通信方式中具有最高
    的效率。因为系统内核没有对访问共享内存进行同步,您必须提供自己的同步措施。解决这些
    问题的常用方法是通过使用信号量进行同步。

    函数 int shmget(key_t key, size_t size, int shmflg);
    参数 key:建立新的共享内存对象
    参数 size:新建立的内存大小
    参数 shmflg:标识符
    返回值:成功 shmget 返回一个共享内存标识符或创建一个共享内存对象,错误返回-1。

    void *shmat(int shmid, const void *shmaddr, int shmflg)
    参数 shmid:共享内存标识符
    参数 shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为 NULL 让内核
    自己决定一个合适的地址位置
    参数 shmflg :SHM_RDONLY,为只读模式,其他为读写模式
    返回值:成功返回共享的内存地址,否则返回-1

     

    int shmdt(const void *shmaddr)
    参数 shmaddr:连接的共享内存的起始地址。
    返回值:成功返回 0,错误返回-1。

    int shmctl(int shmid, int cmd, struct shmid_ds *buf)
    参数 shmid:共享内存标识符
    参数 cmd IPC_RMID:删除这片共享内存
    参数 buf:共享内存管理结构体
    返回值:成功返回 0,错误返回-1。

    #ifndef __SHMDATA_H_HEADER
    #define __SHMDATA_H_HEADER
    
    #define TEXT_SZ 2048
    
    struct shared_use_st {
        int written;            //最为标志,非0:表示可读,0表示可写
        char text[TEXT_SZ];     //记录吸入和读取的文本
    };
    
    
    #endif
    shmdata.h

    shmwrite

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/shm.h>
    #include "shmdata.h"
    
    int main(void)
    {
        int running =1;
        void *shm = NULL;
        struct shared_use_st *shared = NULL;
        char buffer[BUFSIZ + 1];        //用于保存输入的文本
        int shmid;
        //创建共享内存
        shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
        if(shmid == -1) {
            fprintf(stderr, "shmget failed
    ");
            exit(EXIT_FAILURE);
        }
    
        //将共享内存连接到当前进程地址空间
        shm = shmat(shmid, (void*)0, 0);
        if(shm == (void *)-1) {
            fprintf(stderr, "shmat failed
    ");
            exit(EXIT_FAILURE);
        }
        printf("Memory attached at %p
    ", shm);
        //设置共享内存
        shared = (struct shared_use_st *)shm;
        while(running)      //向共享内存中写数据
        {
            while(shared->written == 1) {
                sleep(1);
                printf("Waiting ...
    ");
            }
            printf("Enter some text: ");
            fgets(buffer, BUFSIZ, stdin);
            strncpy(shared->text, buffer, TEXT_SZ);
            //写完数据,设置written使共享内存段可读
            shared->written = 1;
            //输入了end,退出循环(程序)
            if(strncmp(buffer, "end", 3) == 0) 
                running = 0;
        }
        //把共享内存从当前进程中分离
        if(shmdt(shm) == -1) {
            fprintf(stderr, "shmdt failed
    ");
            exit(EXIT_FAILURE);
        }
        sleep(2);
        exit(EXIT_SUCCESS);
    }
    shmwrite

    shmread

    #include "shmdata.h"
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/shm.h>
    #include <string.h>
    
    int main(void)
    {
        int running = 1;        //程序是否继续运行的标志
        void *shm = NULL;       //分配的共享内存的原始首地址
        struct shared_use_st *shared;       //指向shm
        int shmid;              //共享内存标识符
        //创建共享内存
        shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);
        if(shmid == -1) {
            fprintf(stderr, "shmget failed
    ");
            exit(EXIT_FAILURE);
        }
        //将共享内存连接到当前进程的地址空间
        shm = shmat(shmid, 0, 0);
        if(shm == (void *)-1) {
            fprintf(stderr, "shmat failed
    ");
            exit(EXIT_FAILURE);
        }
        printf("
    Memory attached at %p
    ", shm);
        //设置共享内存
        shared = (struct shared_use_st*)shm;
        shared->written = 0;
        while(running) {        //读取共享内存中的数据
            //没有进程向共享内存定数据有数据可读取
            if(shared->written != 0) {
                printf("You wrote:%s", shared->text);
                sleep(rand()%3);
                //读取完数据,设置written使共享岑村段可写
                shared->written = 0;
                //输入了end,退出循环(程序)
                if(strncmp(shared->text, "end", 3) == 0)
                    running = 0;
            } else  //尤其他进程在写数据,不能读取数据
                sleep(1);
        }
        //把共享内存从当前进程中分离
        if(shmdt(shm) == -1) {
            fprintf(stderr, "shmdt failed
    ");
            exit(EXIT_FAILURE);
        }
        //删除共享内存
        if(shmctl(shmid, IPC_RMID, 0) == -1) {
            fprintf(stderr, "shmctl(IPC_RMID) failed
    ");
            exit(EXIT_FAILURE);
        }
        exit(EXIT_SUCCESS);
    }
    shmread

    TCP

    TCP 是一种面向连接的、可靠的、基于 IP 的传输层协议。通过 TCP 可以保证我们传送的数据的正确性。
    Linux 下网络通信程序基本上都是采用 socket 的方式。socket 起源于 Unix,而Unix/Linux 基本哲学之一就是“一切皆文件”,都可以用“打开 open->读写 read/write-> 关闭 close”模式来操作。Socket 就是该模式的一个实现,socket 即是一种特殊的文件,一些socket 函数就是对其进行的操作(读/写 IO、打开、关闭)。说白了 socket 是应用程序与 TCP/IP协议族通信的中间软件抽象层,它是一组接口。
    现在我们看一下基于 TCP/IP 应用程序通信的流程,如下图:

    通过上图我们可以看到 TCP/IP 通信是基于服务器/客户端的模式来实现的,首先是服务器(server)端调用 socket 函数创建一个套接字,然后调用 bind 绑定函数,绑定函数主要是设置通信时使用哪种地址族(IPv4,IPv6 等),使用的端口号。然后调用 listen 函数来监听客户端的连接请求。
    现在我们来看下客户端(client)端的流程,首先调用 socket 函数创建一个套接字,然后调用 connect 函数连接服务器,这时服务器端的 listen 函数监听到客户端的连接请求就会调用 accept 函数去接受请求,这样连接就建立好了。之后双方就可以调用 read/write 函数收发数据了,在完成通信以后服务器(server)和客户端(client)调用 close 函数关闭创建的套接字 。

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    
    int main()
    {
        int sfp, nfp, num = 0;
        struct sockaddr_in s_add, c_add;
        int sin_size;
        unsigned short portnum = 0x8888;
    
        char buffer[100] = {0};
    
        printf("Hello,welcome to my server !
    ");
    
        sfp = socket(AF_INET, SOCK_STREAM, 0);
        if(sfp == -1) {
            printf("socket failed!
    ");
            return -1;
        }
    
        printf("socket ok !
    ");
    
        bzero(&s_add, sizeof(struct sockaddr_in));
        s_add.sin_family = AF_INET;
        s_add.sin_addr.s_addr = htonl(INADDR_ANY);
        s_add.sin_port = htons(portnum);
    
        if(bind(sfp, (struct sockaddr*)(&s_add), sizeof(struct sockaddr))==-1) {
            printf("bind fail !
    ");
            return -1;
        }
    
        printf("bind ok !
    ");
    
        if(listen(sfp, 5) == -1) {
            printf("listen fail !
    ");
            return -1;
        }
    
        printf("listen ok 
    ");
    
        sin_size = sizeof(struct sockaddr_in);
        nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
        if(nfp == -1) {
            printf("accept fail !
    ");
            return -1;
        }
    
        printf("accept ok!
    Server start get connect from %#x : %#x
    ",
            ntohl(c_add.sin_addr.s_addr), ntohs(c_add.sin_port));
        while(1) {
            memset(buffer, 0, 100);
            sprintf(buffer, "Hello, welcome to my server(%d) 
    ", num++);
            send(nfp, buffer, strlen(buffer), 0);
            usleep(500000);
        }
        close(nfp);
    
        close(sfp);
    
        return 0;
    }
    server

    client.c

    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    
    int main(int argc, char *argv[])
    {
        int cfd;
        int recbyte;
        int sin_size;
        char buffer[1024] = {0};
    
        struct sockaddr_in s_add, c_add;
        unsigned short portnum = 0x8888;
    
        printf("Hello, welcome to client!
    ");
    
        if(argc != 2) {
            printf("usage: echo ip
    ");
            return -1;
        }
    
        cfd = socket(AF_INET, SOCK_STREAM, 0);
        if(cfd == -1) {
            printf("socket fail !
    ");
            return -1;
        }
    
        printf("socket ok !
    ");
    
        bzero(&s_add, sizeof(struct sockaddr_in));
        s_add.sin_family = AF_INET;
        s_add.sin_addr.s_addr = inet_addr(argv[1]);
        s_add.sin_port = htons(portnum);
        printf("s_addr = %#x, port: %#x
    ", s_add.sin_addr.s_addr, s_add.sin_port);
    
        if(connect(cfd, (struct sockaddr *)(&s_add), sizeof(struct sockaddr)) == -1) {
            printf("connect fail !
    ");
            return -1;
        }
    
        printf("connect ok !
    ");
    
        while(1) {
            if((recbyte = read(cfd, buffer, 1024)) == -1) {
                printf("read data fail !
    ");
                return -1;
            }
    
            printf("read ok
    RECV
    ");
            buffer[recbyte] = '';
            printf("%s
    ", buffer);
        }
    
        close(cfd);
    
        return 0;
    
    }
    client
    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    Linux(debian)安装Redis教程
    Linux下Nginx学习笔记
    Linux常用命令
    MySQL代码片段
    MySql学习笔记
    漂亮的Web颜色
    Electron开发问题总结
    Git 教程
    Python基础
    【原创】servlet+spring+mybatis配置总结
  • 原文地址:https://www.cnblogs.com/ch122633/p/9413942.html
Copyright © 2020-2023  润新知