zoukankan      html  css  js  c++  java
  • Linux下的几种IPC方式及其C语言实现

    写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文!

    本博客全网唯一合法URL:http://www.cnblogs.com/acm-icpcer/p/8933628.html

    (本篇博客参考了:https://blog.csdn.net/a987073381/article/details/52006729)

      在linux下的多个进程间的通信机制叫做IPC(Inter-Process Communication),它是多个进程之间相互沟通的一种方法。在linux下有多种进程间通信的方法:半双工管道、命名管道、消息队列、信号、信号量、共享内存、内存映射文件,套接字等等。使用这些机制可以为linux下的网络服务器开发提供灵活而又坚固的框架。在这篇博客中我实现了其中的几种机制,详细如下:

     

    1、无名管道:

      

      管道实际是用于进程间通信的一段共享内存,创建管道的进程称为管道服务器,连接到一个管道的进程为管道客户机。一个进程在向管道写入数据后,另一进程就可以从管道的另一端将其读取出来。

      管道的特点:

      (1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

      (2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。比如fork或exec创建的新进程,在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。当父进程与使用fork创建的子进程直接通信时,发送数据的进程关闭读端,接受数据的进程关闭写端。

      (3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

      (4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

      管道的实现机制:

      管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

      管道只能在本地计算机中使用,而不可用于网络间的通信。

    #include <unistd.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main()
    {
        int pipe_fd[2];
        pid_t pid;
        char r_buf[10];
        char w_buf[4];
        int r_num;
    
        memset(r_buf,0,sizeof(r_buf));
        memset(w_buf,0,sizeof(w_buf));
        if(pipe(pipe_fd)<0)
        {
            printf("pipe create error
    ");
            return -1;
        }
    
        if((pid=fork())==0)
        {
            printf("
    ");
            close(pipe_fd[1]);
            sleep(3);//确保父进程关闭写端
            r_num=read(pipe_fd[0],r_buf,10);
            printf(    "read num is %d the data read from the pipe is %d
    ",r_num,atoi(r_buf));
    
            close(pipe_fd[0]);
            exit(1);
        }
        else if(pid>0)
        {
            close(pipe_fd[0]);//close read
            strcpy(w_buf,"111");
            if(write(pipe_fd[1],w_buf,4)!=-1)
                printf("parent write over
    ");
            printf("parent close fd[1] over
    ");
            close(pipe_fd[1]);//write
            sleep(10);
        }
        return 0;
    }

     

    2、有名管道:

     

      命名管道是一种特殊类型的文件,它在系统中以文件形式存在。这样克服了无名管道的弊端,他可以允许没有亲缘关系的进程间通信。

      无名管道和命名管道的区别:

      对于命名管道FIFO来说,IO操作和普通管道IO操作基本一样,但是两者有一个主要的区别,在命名管道中,管道可以是事先已经创建好的,比如我们在命令行下执行mkfifo myfifo就是创建一个命名通道,我们必须用open函数来显示地建立连接到管道的通道,而在管道中,管道已经在主进程里创建好了,然后在fork时直接复制相关数据或者是用exec创建的新进程时把管道的文件描述符当参数传递进去。

      一般来说FIFO和PIPE一样总是处于阻塞状态。也就是说如果命名管道FIFO打开时设置了读权限,则读进程将一直阻塞,一直到其他进程打开该FIFO并向管道写入数据。这个阻塞动作反过来也是成立的。如果不希望命名管道操作的时候发生阻塞,可以在open的时候使用O_NONBLOCK标志,以关闭默认的阻塞操作。

    //writing
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <fcntl.h>
    
    #define N 80
    
    int main() {
        int out_file;
        int nbyte;
        char buf[N];
        if((mkfifo("myfifo",0666))<0)    //创建有名管道
        {
            if(errno==EEXIST)
            {
                printf("The fifo is exist.
    ");
            }
            else{
                perror("creat myfifo failed!
    ");
                exit(-1);
            }
        }else{
            printf("created by this process.
    ");
        }
        out_file = open("myfifo",O_WRONLY);
        if (out_file < 0) {
            printf("Error opening fifo.");
            exit(1);
        }
        printf("please input something:
    ");
        while((nbyte = read(0,buf,N))){
            write(out_file,buf,nbyte);
            printf("please input something:
    ");
        }
        close(out_file);
        return 0;
    }
    //reading
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <errno.h>
    #include <fcntl.h>
    #define N 80
    
    int main(void) {
        int in_file;
        int count = 1;
        char buf[N];
        if((mkfifo("myfifo",0666))<0)//创建有名管道
        {
            if(errno==EEXIST)//管道已经存在
            {
                printf("The fifo is exist.
    ");
            }
            else{
                printf("creat myfifo failed!
    ");
                exit(-1);
            }
        }
        else
        {
            printf("created by this process.
    ");
        }
        in_file = open("myfifo",O_RDONLY);
        if (in_file < 0) {
            printf("Error in opening.
    ");
            exit(1);
        }
    
        while ((count = read(in_file,buf,N)) > 0)
        {
            printf("received from fifo: %s
    ", buf);
            memset(buf,0,N);
        }
        close(in_file);
        return 0;
    }

    3、C/S:

    //client.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/msg.h>
    
    // 用于创建一个唯一的key
    #define MSG_FILE "/etc/passwd"
    
    // 消息结构
    struct msg_form {
        long mtype;
        char mtext[256];
    };
    
    int main()
    {
        int msqid;
        key_t key;
        struct msg_form msg;
    
        // 获取key值
        if ((key = ftok(MSG_FILE, 'z')) < 0)
        {
            perror("ftok error");
            exit(1);
        }
    
        // 打印key值
        printf("Message Queue - Client key is: %d.
    ", key);
    
        // 打开消息队列
        if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
        {
            perror("msgget error");
            exit(1);
        }
    
        // 打印消息队列ID及进程ID
        printf("My msqid is: %d.
    ", msqid);
        printf("My pid is: %d.
    ", getpid());
    
        // 添加消息,类型为888
        msg.mtype = 888;
        sprintf(msg.mtext, "hello, I'm client %d", getpid());
        msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
    
        // 读取类型为777的消息
        msgrcv(msqid, &msg, 256, 999, 0);
        printf("Client: receive msg.mtext is: %s.
    ", msg.mtext);
        printf("Client: receive msg.mtype is: %d.
    ", msg.mtype);
        return 0;
    }
    //server.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/msg.h>
    
    // 用于创建一个唯一的key
    #define MSG_FILE "/etc/passwd"
    
    // 消息结构
    struct msg_form {
        long mtype;
        char mtext[256];
    };
    
    int main()
    {
        int msqid;
        key_t key;
        struct msg_form msg;
    
        // 获取key值
        if((key = ftok(MSG_FILE,'z')) < 0)
        {
            perror("ftok error");
            exit(1);
        }
    
        // 打印key值
        printf("Message Queue - Server key is: %d.
    ", key);
    
        // 创建消息队列
        if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
        {
            perror("msgget error");
            exit(1);
        }
    
        // 打印消息队列ID及进程ID
        printf("My msqid is: %d.
    ", msqid);
        printf("My pid is: %d.
    ", getpid());
    
        // 循环读取消息
        for(;;)
        {
            msgrcv(msqid, &msg, 256, 888, 0);// 返回类型为888的第一个消息
            printf("Server: receive msg.mtext is: %s.
    ", msg.mtext);
            printf("Server: receive msg.mtype is: %d.
    ", msg.mtype);
    
            msg.mtype = 999; // 客户端接收的消息类型
            sprintf(msg.mtext, "hello, I'm server %d", getpid());
            msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
        }
        return 0;
    }

    4、读者/写者:

    //write
    #include<stdio.h>
    #include<stdlib.h>   // exit
    #include<fcntl.h>    // O_WRONLY
    #include<sys/stat.h>
    #include<time.h>     // time
    
    int main()
    {
        int fd;
        int n, i;
        char buf[1024];
        time_t tp;
    
        printf("I am %d process.
    ", getpid()); // 说明进程ID
    
        if((fd = open("fifo1", O_WRONLY)) < 0) // 以写打开一个FIFO
        {
            perror("Open FIFO Failed");
            exit(1);
        }
    
        for(i=0; i<10; ++i)
        {
            time(&tp);  // 取系统当前时间
            n=sprintf(buf,"Process %d's time is %s",getpid(),ctime(&tp));
            printf("Send message: %s", buf); // 打印
            if(write(fd, buf, n+1) < 0)  // 写入到FIFO中
            {
                perror("Write FIFO Failed");
                close(fd);
                exit(1);
            }
            sleep(1);  // 休眠1秒
        }
    
        close(fd);  // 关闭FIFO文件
        return 0;
    }
    //read
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<fcntl.h>
    #include<sys/stat.h>
    
    int main()
    {
        int fd;
        int len;
        char buf[1024];
    
        if(mkfifo("fifo1", 0666) < 0 && errno!=EEXIST) // 创建FIFO管道
            perror("Create FIFO Failed");
    
        if((fd = open("fifo1", O_RDONLY)) < 0)  // 以读打开FIFO
        {
            perror("Open FIFO Failed");
            exit(1);
        }
    
        while((len = read(fd, buf, 1024)) > 0) // 读取FIFO管道
            printf("Read message: %s", buf);
    
        close(fd);  // 关闭FIFO文件
        return 0;
    }

    5、信号量机制:

     

      信号量是一种计数器,用于控制对多个进程共享的资源进行的访问。它们常常被用作一个锁机制,在某个进程正在对特定的资源进行操作时,信号量可以防止另一个进程去访问它。

      信号量是特殊的变量,它只取正整数值并且只允许对这个值进行两种操作:等待(wait)和信号(signal)。(P、V操作,P用于等待,V用于信号)
      P(sv):如果sv的值大于0,就给它减1;如果它的值等于0,就挂起该进程的执行

      V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行;如果没有其他进程因等待sv而挂起,则给它加1 
      简单理解就是P相当于申请资源,V相当于释放资源

      操作系统课程里面大量提到过信号量机制,故在此不赘述。

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/sem.h>
    
    // 联合体,用于semctl初始化
    union semun
    {
        int              val; /*for SETVAL*/
        struct semid_ds *buf;
        unsigned short  *array;
    };
    
    // 初始化信号量
    int init_sem(int sem_id, int value)
    {
        union semun tmp;
        tmp.val = value;
        if(semctl(sem_id, 0, SETVAL, tmp) == -1)
        {
            perror("Init Semaphore Error");
            return -1;
        }
        return 0;
    }
    
    // P操作:
    //    若信号量值为1,获取资源并将信号量值-1
    //    若信号量值为0,进程挂起等待
    int sem_p(int sem_id)
    {
        struct sembuf sbuf;
        sbuf.sem_num = 0; /*序号*/
        sbuf.sem_op = -1; /*P操作*/
        sbuf.sem_flg = SEM_UNDO;
    
        if(semop(sem_id, &sbuf, 1) == -1)
        {
            perror("P operation Error");
            return -1;
        }
        return 0;
    }
    
    // V操作:
    //    释放资源并将信号量值+1
    //    如果有进程正在挂起等待,则唤醒它们
    int sem_v(int sem_id)
    {
        struct sembuf sbuf;
        sbuf.sem_num = 0; /*序号*/
        sbuf.sem_op = 1;  /*V操作*/
        sbuf.sem_flg = SEM_UNDO;
    
        if(semop(sem_id, &sbuf, 1) == -1)
        {
            perror("V operation Error");
            return -1;
        }
        return 0;
    }
    
    // 删除信号量集
    int del_sem(int sem_id)
    {
        union semun tmp;
        if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
        {
            perror("Delete Semaphore Error");
            return -1;
        }
        return 0;
    }
    
    
    int main()
    {
        int sem_id;  // 信号量集ID
        key_t key;
        pid_t pid;
    
        // 获取key值
        if((key = ftok(".", 'z')) < 0)
        {
            perror("ftok error");
            exit(1);
        }
    
        // 创建信号量集,其中只有一个信号量
        if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
        {
            perror("semget error");
            exit(1);
        }
    
        // 初始化:初值设为0资源被占用
        init_sem(sem_id, 0);
    
        if((pid = fork()) == -1)
            perror("Fork Error");
        else if(pid == 0) /*子进程*/
        {
            sleep(2);
            printf("Process child: pid=%d
    ", getpid());
            sem_v(sem_id);  /*释放资源*/
        }
        else  /*父进程*/
        {
            sem_p(sem_id);   /*等待资源*/
            printf("Process father: pid=%d
    ", getpid());
            sem_v(sem_id);   /*释放资源*/
            del_sem(sem_id); /*删除信号量集*/
        }
        return 0;
    }

    6、共享内存:

     

      共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,由IPC为进程创建的一个特殊地址范围,它将出现在该进程的地址空间中。其他进程可以将同一段共享内存连接到自己的地址空间中。所有进程都可以访问共享内存中的地址,就好像它们是malloc分配的一样。如果一个进程向共享内存中写入了数据,所做的改动将立刻被其他进程看到。

      共享内存是IPC最快捷的方式,因为共享内存方式的通信没有中间过程,而管道、消息队列等方式则是需要将数据通过中间机制进行转换。共享内存方式直接将某段内存段进行映射,多个进程间的共享内存是同一块的物理空间,仅仅映射到各进程的地址不同而已,因此不需要进行复制,可以直接使用此段空间。

      注意:共享内存本身并没有同步机制,需要程序员自己控制。

    ////share_mem_client
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/shm.h>  // shared memory
    #include<sys/sem.h>  // semaphore
    #include<sys/msg.h>  // message queue
    #include<string.h>   // memcpy
    
    // 消息队列结构
    struct msg_form {
        long mtype;
        char mtext;
    };
    
    // 联合体,用于semctl初始化
    union semun
    {
        int              val; /*for SETVAL*/
        struct semid_ds *buf;
        unsigned short  *array;
    };
    
    // P操作:
    //  若信号量值为1,获取资源并将信号量值-1
    //  若信号量值为0,进程挂起等待
    int sem_p(int sem_id)
    {
        struct sembuf sbuf;
        sbuf.sem_num = 0; /*序号*/
        sbuf.sem_op = -1; /*P操作*/
        sbuf.sem_flg = SEM_UNDO;
    
        if(semop(sem_id, &sbuf, 1) == -1)
        {
            perror("P operation Error");
            return -1;
        }
        return 0;
    }
    
    // V操作:
    //  释放资源并将信号量值+1
    //  如果有进程正在挂起等待,则唤醒它们
    int sem_v(int sem_id)
    {
        struct sembuf sbuf;
        sbuf.sem_num = 0; /*序号*/
        sbuf.sem_op = 1;  /*V操作*/
        sbuf.sem_flg = SEM_UNDO;
    
        if(semop(sem_id, &sbuf, 1) == -1)
        {
            perror("V operation Error");
            return -1;
        }
        return 0;
    }
    
    
    int main()
    {
        key_t key;
        int shmid, semid, msqid;
        char *shm;
        struct msg_form msg;
        int flag = 1; /*while循环条件*/
    
        // 获取key值
        if((key = ftok(".", 'z')) < 0)
        {
            perror("ftok error");
            exit(1);
        }
    
        // 获取共享内存
        if((shmid = shmget(key, 1024, 0)) == -1)
        {
            perror("shmget error");
            exit(1);
        }
    
        // 连接共享内存
        shm = (char*)shmat(shmid, 0, 0);
        if((int)shm == -1)
        {
            perror("Attach Shared Memory Error");
            exit(1);
        }
    
        // 创建消息队列
        if ((msqid = msgget(key, 0)) == -1)
        {
            perror("msgget error");
            exit(1);
        }
    
        // 获取信号量
        if((semid = semget(key, 0, 0)) == -1)
        {
            perror("semget error");
            exit(1);
        }
    
        // 写数据
        printf("***************************************
    ");
        printf("*                 IPC                 *
    ");
        printf("*    Input r to send data to server.  *
    ");
        printf("*    Input q to quit.                 *
    ");
        printf("***************************************
    ");
    
        while(flag)
        {
            char c;
            printf("Please input command: ");
            scanf("%c", &c);
            switch(c)
            {
                case 'r':
                    printf("Data to send: ");
                    sem_p(semid);  /*访问资源*/
                    scanf("%s", shm);
                    sem_v(semid);  /*释放资源*/
                    /*清空标准输入缓冲区*/
                    while((c=getchar())!='
    ' && c!=EOF);
                    msg.mtype = 888;
                    msg.mtext = 'r';  /*发送消息通知服务器读数据*/
                    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
                    break;
                case 'q':
                    msg.mtype = 888;
                    msg.mtext = 'q';
                    msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
                    flag = 0;
                    break;
                default:
                    printf("Wrong input!
    ");
                    /*清空标准输入缓冲区*/
                    while((c=getchar())!='
    ' && c!=EOF);
            }
        }
    
        // 断开连接
        shmdt(shm);
    
        return 0;
    }
    //share_mem_server
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/shm.h>  // shared memory
    #include<sys/sem.h>  // semaphore
    #include<sys/msg.h>  // message queue
    #include<string.h>   // memcpy
    
    // 消息队列结构
    struct msg_form {
        long mtype;
        char mtext;
    };
    
    // 联合体,用于semctl初始化
    union semun
    {
        int              val; /*for SETVAL*/
        struct semid_ds *buf;
        unsigned short  *array;
    };
    
    // 初始化信号量
    int init_sem(int sem_id, int value)
    {
        union semun tmp;
        tmp.val = value;
        if(semctl(sem_id, 0, SETVAL, tmp) == -1)
        {
            perror("Init Semaphore Error");
            return -1;
        }
        return 0;
    }
    
    // P操作:
    //  若信号量值为1,获取资源并将信号量值-1
    //  若信号量值为0,进程挂起等待
    int sem_p(int sem_id)
    {
        struct sembuf sbuf;
        sbuf.sem_num = 0; /*序号*/
        sbuf.sem_op = -1; /*P操作*/
        sbuf.sem_flg = SEM_UNDO;
    
        if(semop(sem_id, &sbuf, 1) == -1)
        {
            perror("P operation Error");
            return -1;
        }
        return 0;
    }
    
    // V操作:
    //  释放资源并将信号量值+1
    //  如果有进程正在挂起等待,则唤醒它们
    int sem_v(int sem_id)
    {
        struct sembuf sbuf;
        sbuf.sem_num = 0; /*序号*/
        sbuf.sem_op = 1;  /*V操作*/
        sbuf.sem_flg = SEM_UNDO;
    
        if(semop(sem_id, &sbuf, 1) == -1)
        {
            perror("V operation Error");
            return -1;
        }
        return 0;
    }
    
    // 删除信号量集
    int del_sem(int sem_id)
    {
        union semun tmp;
        if(semctl(sem_id, 0, IPC_RMID, tmp) == -1)
        {
            perror("Delete Semaphore Error");
            return -1;
        }
        return 0;
    }
    
    // 创建一个信号量集
    int creat_sem(key_t key)
    {
        int sem_id;
        if((sem_id = semget(key, 1, IPC_CREAT|0666)) == -1)
        {
            perror("semget error");
            exit(-1);
        }
        init_sem(sem_id, 1);  /*初值设为1资源未占用*/
        return sem_id;
    }
    
    
    int main()
    {
        key_t key;
        int shmid, semid, msqid;
        char *shm;
        char data[] = "this is server";
        struct shmid_ds buf1;  /*用于删除共享内存*/
        struct msqid_ds buf2;  /*用于删除消息队列*/
        struct msg_form msg;  /*消息队列用于通知对方更新了共享内存*/
    
        // 获取key值
        if((key = ftok(".", 'z')) < 0)
        {
            perror("ftok error");
            exit(1);
        }
    
        // 创建共享内存
        if((shmid = shmget(key, 1024, IPC_CREAT|0666)) == -1)
        {
            perror("Create Shared Memory Error");
            exit(1);
        }
    
        // 连接共享内存
        shm = (char*)shmat(shmid, 0, 0);
        if((int)shm == -1)
        {
            perror("Attach Shared Memory Error");
            exit(1);
        }
    
    
        // 创建消息队列
        if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
        {
            perror("msgget error");
            exit(1);
        }
    
        // 创建信号量
        semid = creat_sem(key);
    
        // 读数据
        while(1)
        {
            msgrcv(msqid, &msg, 1, 888, 0); /*读取类型为888的消息*/
            if(msg.mtext == 'q')  /*quit - 跳出循环*/
                break;
            if(msg.mtext == 'r')  /*read - 读共享内存*/
            {
                sem_p(semid);
                printf("%s
    ",shm);
                sem_v(semid);
            }
        }
    
        // 断开连接
        shmdt(shm);
    
        /*删除共享内存、消息队列、信号量*/
        shmctl(shmid, IPC_RMID, &buf1);
        msgctl(msqid, IPC_RMID, &buf2);
        del_sem(semid);
        return 0;
    }

    7、网际套接字:

     

      套接字是计算机网络课程、java课程上见过面的老朋友了。套接字机制不但可以单机的不同进程通信,而且使得跨网机器间进程可以通信。

      套接字的创建和使用与管道是有区别的,套接字明确地将客户端与服务器区分开来,可以实现多个客户端连到同一服务器。

      (1)服务器套接字连接过程描述:

      首先,服务器应用程序用socket创建一个套接字,它是系统分配服务器进程的类似文件描述符的资源。 接着,服务器调用bind给套接字命名。这个名字是一个标示符,它允许linux将进入的针对特定端口的连接转到正确的服务器进程。 然后,系统调用listen函数开始接听,等待客户端连接。listen创建一个队列并将其用于存放来自客户端的进入连接。 当客户端调用connect请求连接时,服务器调用accept接受客户端连接,accept此时会创建一个新套接字,用于与这个客户端进行通信。

      (2)客户端套接字连接过程描述:

      客户端首先调用socket创建一个未命名套接字,让后将服务器的命名套接字作为地址来调用connect与服务器建立连接。

      只要双方连接建立成功,我们就可以像操作底层文件一样来操作socket套接字实现通信。

       //server.c
        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
    
        int main(void)
        {
            //create socket
            int fd = socket(AF_INET, SOCK_DGRAM, 0);
            if(fd==-1)
            {
                perror("socket
    ");
                exit(-1);
            }
            printf("socket fd=%d
    ",fd);
    
            //build connection address
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(6666);
            addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    
            int r;
            r = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
            if(r==-1)
            {
                perror("bind");
                close(fd);
                exit(-1);
            }
            printf("bind address successful!
    ");
            //accept or send message
            char buf[255];
            struct sockaddr_in from;
            socklen_t len;
            len = sizeof(from);
            while(1)
            {
                r = recvfrom(fd,buf,sizeof(buf)-1,0,(struct sockaddr*)&from,&len);
                if(r>0)
                {
                    buf[r]=0;
                    printf("The message from %s is:%s
    ",inet_ntoa(from.sin_addr),buf);
                }
                else
                {
                    break;
                }
            }
            //close socket
            close(fd);
            return 0;
        }
        //client.c
        #include <stdio.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <string.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include <arpa/inet.h>
    
        int main(void)
        {
            //create socket
            int fd = socket(AF_INET,SOCK_DGRAM,0);
            if(fd==-1)
            {
                perror("socket");
                exit(-1);
            }
            printf("create socket OK!
    ");
            //create an send address
            struct sockaddr_in addr={};
            addr.sin_family = AF_INET;
            addr.sin_port = htons(6666);
            addr.sin_addr.s_addr=inet_addr("127.0.0.1");
            //send the message to the specify address
            int r;
            char buf[255];
            while(1)
            {
                r = read(0,buf,sizeof(buf)-1);
                if(r<=0)
                    break;
                sendto(fd,buf,r,0,(struct sockaddr*)&addr,sizeof(addr));
            }
            //close socket
            close(fd);
            return 0;
        }

    tz@COI HZAU

    2018/4/24

  • 相关阅读:
    SpringMVC 2
    MySQL--事务,隔离性和隔离级别
    String.intern()
    初识消息队列--ActiveMq
    Java后台上传图片到七牛云
    Thread.interrupt(),Thread.isInterrupted(),Thread.interrupted()碎碎念
    Java基础--对象
    Java后台调用gcc编译C语言代码
    ToolProvider.getSystemJavaCompiler()方法空指针的排坑
    [LeetCode]29 两数相除和一个小坑点
  • 原文地址:https://www.cnblogs.com/acm-icpcer/p/8933628.html
Copyright © 2011-2022 走看看