第四章 Linux网络编程(3)

一、IO多路复用简介

输入:文件里面的数据输入到内存中
输出:内存中的数据输出到文件中
在这里插入图片描述

IO多路复用图解

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

二、select API介绍

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

三、select代码编写

select.c

#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/select.h>

int main(){

    //创建socket
    int lfd = socket(PF_INET,SOCK_STREAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    //绑定ip和端口
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons(9999);
    int ret = bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(ret == -1){
        perror("bind");
        return -1;
    }

    //监听
    ret = listen(lfd,128);
    if(ret == -1){
        perror("listen");
        return -1;
    }

    //创建一个fd_set的集合,存放的是需要检测的文件描述符
    fd_set rdset,tmp;
    FD_ZERO(&rdset);
    FD_SET(lfd,&rdset);
    //最大的文件描述符
    int maxfd = lfd;

    while(1){

        tmp = rdset;

        //调用select系统函数,让内核帮检测哪些文件描述符有数据
        ret = select(maxfd+1,&tmp,NULL,NULL,NULL);
        if(ret == -1){
            perror("select");
            return -1;
        }else if(ret == 0){
            // 检测的文件描述符里面没有哪个数据到达
            //再循环进行检测
            continue;
        }else if(ret > 0){
            //说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
            //FD_ISSET判断fd对应的标志位0还是1, 返回值:fd对应的标志位的值0或者1
            if(FD_ISSET(lfd,&tmp)){
                //表示有新的客户端连接进来了
                struct sockaddr_in cliaddr;
                int clilen = sizeof(cliaddr);
                int cfd = accept(lfd,(struct sockaddr *)&cliaddr,&clilen);

                //将新的文件描述符加入到集合中
                FD_SET(cfd,&rdset);
                //更新最大的文件描述符
                maxfd = maxfd > cfd ? maxfd : cfd;
            }

            //看剩余的文件描述符是否发生变化
            for(int i = lfd +1;i<= maxfd;++i){
                if(FD_ISSET(i,&tmp)){
                    //说明这个文件描述符对应的客户端发来了数据
                    char buf[1024] = {0};
                    int rlen = read(i,&buf,sizeof(buf)); 
                    if(rlen == -1){
                        perror("read");
                        return -1;
                    }else if(rlen == 0){
                        printf("client closed....\n");
                        close(i);
                        //断开连接,将对应的文件描述符对应的标志位设为0
                        FD_CLR(i,&rdset);
                    }else if(rlen > 0){
                        printf("read buf : %s\n",buf);
                        //忘记了为啥要加1
                        write(i,buf,strlen(buf)+1);
                    }
                }
            }



        }
    }


    close(lfd);
    return 0;
}

client.c

#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main() {

    // 创建socket
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in seraddr;
    inet_pton(AF_INET, "127.0.0.1", &seraddr.sin_addr.s_addr);
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(9999);

    // 连接服务器
    int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));

    if(ret == -1){
        perror("connect");
        return -1;
    }

    int num = 0;
    while(1) {
        char sendBuf[1024] = {0};
        sprintf(sendBuf, "send data %d", num++);
        write(fd, sendBuf, strlen(sendBuf) + 1);

        // 接收
        int len = read(fd, sendBuf, sizeof(sendBuf));
        if(len == -1) {
            perror("read");
            return -1;
        }else if(len > 0) {
            printf("read buf = %s\n", sendBuf);
        } else {
            printf("服务器已经断开连接...\n");
            break;
        }
        sleep(1);
        //usleep(1000);
    }

    close(fd);

    return 0;
}

四、poll API介绍及代码编写

在这里插入图片描述

poll.c

#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<poll.h>

int main(){

    //创建socket
    int lfd = socket(PF_INET,SOCK_STREAM,0);
    // if(lfd == -1){
    //     perror("socket");
    //     return -1;
    // }

    //绑定ip和端口
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = 0;
    saddr.sin_port = htons(9999);
    //int ret = bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    // if(ret == -1){
    //     perror("bind");
    //     return -1;
    // }

    //监听
    //ret = listen(lfd,8);
    listen(lfd,8);
    // if(ret == -1){
    //     perror("listen");
    //     return -1;
    // }


    //初始化检测的文件描述符
    struct pollfd fds[1024];
    for(int i =0;i<1024;++i){
        fds[i].fd = -1;
        fds[i].events = POLLIN;
    }
    fds[0].fd = lfd;
    int nfds = 0;

    while(1){
        //调用poll系统函数,让内核帮检测哪些文件描述符有数据
        int ret = poll(fds,nfds+1,-1);
        if(ret == -1){
            perror("poll");
            return -1;
        }else if(ret == 0){
            //没有检测到有数据的文件描述符
            continue;
        }else if(ret > 0){
            //检测到对应文件描述符的对应缓冲区有数据
            if(fds[0].revents & POLLIN){
                //有新的客户端连接进来了
                struct sockaddr_in cliaddr;
                int clilen = sizeof(cliaddr);
                int cfd = accept(lfd,(struct sockaddr *)&cliaddr,&clilen);

                //将新的文件描述符加入到集合中
                for(int i = 1; i<1024;i++){
                    if(fds[i].fd == -1){
                        fds[i].fd = cfd;
                        fds[i].events = POLLIN;
                        break;
                    }
                }

                //加入到集合中之后,更新最大的文件描述符的索引
                nfds = nfds > cfd ? nfds:cfd;
            }//if

            //查看连接的客户端有没有发来数据
            //fds[0] 是监听文件描述符 fds[1]及以后的才是客户端对应的描述符
            for(int i =1; i<=nfds; ++i){
                if(fds[i].revents & POLLIN){
                    //说明这个文件描述符对应的客户端发来了数据
                    //接收,然后再发送回去(回射服务器)
                    char buf[1024]={0};
                    int rlen = read(fds[i].fd,buf,sizeof(buf));
                    if(rlen == -1){
                        perror("read");
                        return -1;
                    }else if(rlen == 0){
                        printf("client closed.....\n");
                        close(fds[i].fd);
                        fds[i].fd = -1;

                    }else if(rlen > 0){
                        //接收到了数据
                        printf("resv client data : %s\n",buf);
                        //再把接收到的数据写回去
                        write(fds[i].fd,buf,strlen(buf)+1);
                    }
                }
            }



        }//else if(ret > 0)
    }//while(1)
    close(lfd);
    return 0;
}

client.c

#include <stdio.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int main() {

    // 创建socket
    int fd = socket(PF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in seraddr;
    inet_pton(AF_INET, "192.168.49.129", &seraddr.sin_addr.s_addr);
    seraddr.sin_family = AF_INET;
    seraddr.sin_port = htons(9999);

    // 连接服务器
    int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof(seraddr));

    if(ret == -1){
        perror("connect");
        return -1;
    }

    int num = 0;
    while(1) {
        char sendBuf[1024] = {0};
        sprintf(sendBuf, "send data %d", num++);
        write(fd, sendBuf, strlen(sendBuf) + 1);

        // 接收
        int len = read(fd, sendBuf, sizeof(sendBuf));
        if(len == -1) {
            perror("read");
            return -1;
        }else if(len > 0) {
            printf("read buf = %s\n", sendBuf);
        } else {
            printf("服务器已经断开连接...\n");
            break;
        }
        sleep(1);
        //usleep(1000);
    }

    close(fd);

    return 0;
}

五、epoll API介绍

在这里插入图片描述

在这里插入图片描述

六、epoll代码编写

epoll.c (client.c略)

#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/select.h>
#include<sys/epoll.h>

int main(){

    //创建socket
    int lfd = socket(PF_INET,SOCK_STREAM,0);

    //绑定ip和端口
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons(9999);

    bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));

    //监听
    listen(lfd,8);

    //调用epoll_create创建一个epoll实例
    int epfd = epoll_create(100);

    //将监听的文件描述符相关的监测信息添加到epoll实例中   
    struct epoll_event epev;
    epev.events = EPOLLIN;
    epev.data.fd = lfd;
    epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);

    //接收检测后的数据
    struct epoll_event epevs[1024];

    while(1){
        int ret = epoll_wait(epfd,epevs,1024,-1);
        if(ret == -1){
            perror("epoll_wait");
            return -1;
        }

        printf("ret = %d\n",ret);

        //循环遍历数组epevs,epevs里面有多少个值,通过ret可以知道,ret是发生变化的文件描述符的个数
        for(int i =0; i<ret; i++){
            if(epevs[i].data.fd == lfd){
                //监听的文件描述符有数据到达,有客户端连接
                struct sockaddr_in cliaddr;
                int clilen = sizeof(cliaddr);
                int cfd = accept(lfd,(struct sockaddr *)&cliaddr,&clilen);

                //重用之前的epev
                epev.events = EPOLLIN;
                epev.data.fd = cfd;
                //将监听的文件描述符相关的监测信息添加到epoll实例中
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);

            }else{
                //有数据到达,需要通信
                //上一个是监听描述符,如果不是那就是连接的描述符
                char buf[1024]={0};
                int rlen = read(epevs[i].data.fd,buf,sizeof(buf));
                if(rlen == -1){
                    perror("read");
                    return -1;
                }else if(rlen == 0){
                    printf("client closed.....\n");
                    epoll_ctl(epfd,EPOLL_CTL_DEL,epevs[i].data.fd,NULL);
                    close(epevs[i].data.fd);
                    //fds[i].fd = -1;

                }else if(rlen > 0){
                    //接收到了数据  
                    printf("resv client data : %s\n",buf);
                    //再把接收到的数据写回去
                    write(epevs[i].data.fd,buf,strlen(buf)+1);
                }

            }


        }
    }
    close(lfd);
    close(epfd);
    return 0;
}

七、epoll的两种工作模式

在这里插入图片描述

在这里插入图片描述
epoll默认触发方式为水平触发

#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>

int main() {

    // 创建socket
    int lfd = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in saddr;
    saddr.sin_port = htons(9999);
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = INADDR_ANY;

    // 绑定
    bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));

    // 监听
    listen(lfd, 8);

    // 调用epoll_create()创建一个epoll实例
    int epfd = epoll_create(100);

    // 将监听的文件描述符相关的检测信息添加到epoll实例中
    struct epoll_event epev;
    epev.events = EPOLLIN;
    epev.data.fd = lfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev);

    struct epoll_event epevs[1024];

    while(1) {

        int ret = epoll_wait(epfd, epevs, 1024, -1);
        if(ret == -1) {
            perror("epoll_wait");
            exit(-1);
        }

        printf("ret = %d\n", ret);

        for(int i = 0; i < ret; i++) {

            int curfd = epevs[i].data.fd;

            if(curfd == lfd) {
                // 监听的文件描述符有数据达到,有客户端连接
                struct sockaddr_in cliaddr;
                int len = sizeof(cliaddr);
                int cfd = accept(lfd, (struct sockaddr *)&cliaddr, &len);

                // 设置cfd属性非阻塞
                int flag = fcntl(cfd, F_GETFL);
                flag | O_NONBLOCK;
                fcntl(cfd, F_SETFL, flag);

                epev.events = EPOLLIN | EPOLLET;    // 设置边沿触发
                epev.data.fd = cfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev);
            } else {
                if(epevs[i].events & EPOLLOUT) {
                    continue;
                }  

                // 循环读取出所有数据
                char buf[5];
                int len = 0;
                while( (len = read(curfd, buf, sizeof(buf))) > 0) {
                    // 打印数据
                    // printf("recv data : %s\n", buf);
                    write(STDOUT_FILENO, buf, len);
                    write(curfd, buf, len);
                }
                if(len == 0) {
                    printf("client closed....");
                }else if(len == -1) {
                    if(errno == EAGAIN) {
                        printf("data over.....");
                    }else {
                        perror("read");
                        exit(-1);
                    }
                    
                }

            }

        }
    }

    close(lfd);
    close(epfd);
    return 0;
}

八、UDP通信实现

在这里插入图片描述
在这里插入图片描述
UDP不需要使用多线程多进程就可以实现多个客户端与服务端实现通信

udp_server.c

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>

int main(){

    // 1、创建一个通信的socket
    int lfd = socket(PF_INET,SOCK_DGRAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    // 2、绑定ip和端口
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(9999);

    int ret = bind(lfd,(struct sockaddr *)&addr,sizeof(addr));
    if(ret == -1){
        perror("bind");
        return -1;
    }

    //3、通信
    while(1){
        //接收数据
        char recvbuf[128] = {0};
        char ipbuf[16] = {0};
        struct sockaddr_in cliaddr;
        int len = sizeof(cliaddr);
        int num = recvfrom(lfd,recvbuf,sizeof(recvbuf),0,(struct sockaddr *)&cliaddr,&len);
        //默认已经接收到了数据,就不验证了
        printf("client ip : %s, port : %d",
            inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,ipbuf,sizeof(ipbuf)),
            ntohs(cliaddr.sin_port));

        printf("client say : %s\n",recvbuf);


        //发送数据    +1表示把字符结束符也发送回去
        sendto(lfd,recvbuf,sizeof(recvbuf)+1,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));

    }

    close(lfd);
    return 0;
}

udp_client.c

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>

int main(){

    // 1、创建一个通信的socket
    int lfd = socket(PF_INET,SOCK_DGRAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    //服务器的地址信息
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    //saddr.sin_addr.s_addr = htonl("192.168.49.129");
    inet_pton(AF_INET,"192.168.49.129",&saddr.sin_addr.s_addr);
    saddr.sin_port = htons(9999);
    

    int num = 0;
    //3、通信
    while(1){

        char sendBuf[128];
        sprintf(sendBuf,"hello, i am client %d \n",num++);

        //发送数据    +1表示把字符结束符也发送回去
        sendto(lfd,sendBuf,sizeof(sendBuf)+1,0,(struct sockaddr *)&saddr,sizeof(saddr));

        //接收数据
        int num = recvfrom(lfd,sendBuf,sizeof(sendBuf),0,NULL,NULL);
        //默认已经接收到了数据,就不验证了
        
        printf("server say : %s\n",sendBuf);

    }

    close(lfd);
    return 0;
}

九、广播

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

bro_server.c

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>

int main(){

    // 1、创建一个通信的socket
    int lfd = socket(PF_INET,SOCK_DGRAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    //2、设置广播属性
    int op = 1;
    setsockopt(lfd,SOL_SOCKET,SO_BROADCAST,&op,sizeof(op));

    // 3、创建一个广播的地址
    struct sockaddr_in cliaddr;
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(9999);
    inet_pton(AF_INET,"192.168.49.255",&cliaddr.sin_addr.s_addr);

    //3、通信
    int num = 0;
    while(1){
        
        char sendBuf[128];
        sprintf(sendBuf,"hello , client .....%d\n",num++);


        //发送数据    +1表示把字符结束符也发送回去
        sendto(lfd,sendBuf,sizeof(sendBuf)+1,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
        printf("广播的数据 : %s\n",sendBuf);
        sleep(1);
    }

    close(lfd);
    return 0;
}

bro_client.c

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>

int main(){

    // 1、创建一个通信的socket
    int lfd = socket(PF_INET,SOCK_DGRAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    //2、客户端绑定本地的ip和端口
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    //inet_pton(AF_INET,"192.168.49.129",&saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons(9999);
    
    int ret = bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(ret == -1){
        perror("bind");
        return -1;
    }

    
    //3、通信
    while(1){

        char resvBuf[128];

        //接收数据
        int num = recvfrom(lfd,resvBuf,sizeof(resvBuf),0,NULL,NULL);
        //默认已经接收到了数据,就不验证了
        
        printf("server say : %s\n",resvBuf);

    }

    close(lfd);
    return 0;
}

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

十、组播

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

multi_server.c

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>

int main(){

    // 1、创建一个通信的socket
    int lfd = socket(PF_INET,SOCK_DGRAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    //2、设置多播属性,server端设置多出接口
    struct in_addr imr_multiaddr;
    //初始化多播地址
    inet_pton(AF_INET,"239.0.0.10",&imr_multiaddr.s_addr);
    setsockopt(lfd,IPPROTO_IP,IP_MULTICAST_IF,&imr_multiaddr,sizeof(imr_multiaddr));

    // 3、初始化客户端的地址信息
    struct sockaddr_in cliaddr;
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(9999);
    inet_pton(AF_INET,"239.0.0.10",&cliaddr.sin_addr.s_addr);

    //3、通信
    int num = 0;
    while(1){
        
        char sendBuf[128];
        sprintf(sendBuf,"hello , client .....%d\n",num++);


        //发送数据    +1表示把字符结束符也发送回去
        sendto(lfd,sendBuf,sizeof(sendBuf)+1,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
        printf("组播的数据 : %s\n",sendBuf);
        sleep(1);
    }

    close(lfd);
    return 0;
}

multi_client.c

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

int main(){

    // 1、创建一个通信的socket
    int lfd = socket(PF_INET,SOCK_DGRAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    //2、客户端绑定本地的ip和端口
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    //inet_pton(AF_INET,"192.168.49.129",&saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;
    saddr.sin_port = htons(9999);
    
    int ret = bind(lfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(ret == -1){
        perror("bind");
        return -1;
    }

    struct ip_mreq op;
    inet_pton(AF_INET,"239.0.0.10",&op.imr_multiaddr.s_addr);
    op.imr_interface.s_addr = INADDR_ANY;
    //加入到多播组
    setsockopt(lfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&op,sizeof(op));

    

    
    //3、通信
    while(1){

        char resvBuf[128];

        //接收数据
        int num = recvfrom(lfd,resvBuf,sizeof(resvBuf),0,NULL,NULL);
        //默认已经接收到了数据,就不验证了
        
        printf("server say : %s\n",resvBuf);

    }

    close(lfd);
    return 0;
}

十一、本地套接字通信

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

ipc_server.c

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/un.h>
#include<string.h>

int main(){

    //运行客户端会生成一个server.sock文件,结束,
    //再重新运行会产生bind:address already in use错误,
    //将server.sock文件删除就能成功运行(会再生成一个server.sock文件)
    //而unlink就可以删除这个文件,
    //每次运行都会检测有没有这个文件,如果有就删除
    unlink("server.sock");

    //1、创建套接字
    int lfd = socket(AF_LOCAL,SOCK_STREAM,0);
    if(lfd == -1){
        perror("socket");
        return -1;
    }

    //绑定本地套接字文件
    struct sockaddr_un addr;
    addr.sun_family = AF_LOCAL;
    //sprintf(&addr.sun_path,"server.sock");
    strcpy(addr.sun_path,"server.sock");
    int ret = bind(lfd,(struct sockaddr *)&addr,sizeof(addr));
    if(ret == -1){
        perror("bind");
        return -1;
    }

    //3、进行监听
    ret = listen(lfd,100);
    if(ret == -1){
        perror("listen");
        return 0;
    }

    //4、接受连接
    struct sockaddr_un cliaddr;
    int clilen = sizeof(cliaddr);
    int cfd =accept(lfd,(struct sockaddr *)&cliaddr,&clilen);
    if(cfd == -1){
        perror("accept");
        return -1;
    }

    printf("client socket filename : %s\n",cliaddr.sun_path);

    //5、通信
    while(1){
        char buf[1024];
        int len = recv(cfd,buf,sizeof(buf),0);

        if(len == -1){
            perror("recv");
            return -1;
        }else if(len == 0){
            printf("client closed......");
            break;
        }else if(len > 0){
            //接收到了数据
            printf("client say : %s \n",buf);
            send(cfd,buf,len,0);
        }
    }


    close(cfd);
    close(lfd);

    return 0;
}

ipc_client

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/un.h>
#include<string.h>

int main(){
    
    //运行客户端会生成一个client.sock文件,结束,
    //再重新运行会产生bind:address already in use错误,
    //将client.sock文件删除就能成功运行(会再生成一个client.sock文件)
    //而unlink就可以删除这个文件
    unlink("client.sock");

    //1、创建套接字
    int cfd = socket(AF_LOCAL,SOCK_STREAM,0);
    if(cfd == -1){
        perror("socket");
        return -1;
    }

    //绑定本地套接字文件
    struct sockaddr_un addr;
    addr.sun_family = AF_LOCAL;
    //sprintf(&addr.sun_path,"server.sock");
    strcpy(addr.sun_path,"client.sock");
    int ret = bind(cfd,(struct sockaddr *)&addr,sizeof(addr));
    if(ret == -1){
        perror("bind");
        return -1;
    }


    //3、连接服务端
    struct sockaddr_un saddr;
    saddr.sun_family = AF_LOCAL;
    strcpy(saddr.sun_path,"server.sock");
    ret = connect(cfd,(struct sockaddr *)&saddr,sizeof(saddr));
    if(ret == -1){
        perror("connect");
        return -1;
    }


    //4、通信
    int num = 0;
    while(1){

        //发送数据
        char buf[1024];
        sprintf(buf,"hello , i am client %d\n",num++);
        send(cfd,buf,strlen(buf)+1,0);
        printf("client say : %s \n",buf);

        //接收数据
        int len = recv(cfd,buf,sizeof(buf),0);

        if(len == -1){
            perror("recv");
            return -1;
        }else if(len == 0){
            printf("server closed......");
            break;
        }else if(len > 0){
            //接收到了数据
            printf("server say : %s \n",buf);
        }

        sleep(1);
    }


    close(cfd);

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值