第4章Linux网络编程

网络结构模式

C/S结构
在这里插入图片描述
在这里插入图片描述
B/S结构
在这里插入图片描述

MAC地址、IP地址、端口

网卡
在这里插入图片描述
MAC地址
在这里插入图片描述
IP地址
在这里插入图片描述
子网掩码
在这里插入图片描述
端口
在这里插入图片描述
在这里插入图片描述

网络类型

OSI七层参考模型
在这里插入图片描述
TCP/IP四层模型
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

协议

在这里插入图片描述
常见协议
在这里插入图片描述
UDP协议
在这里插入图片描述
TCP协议
在这里插入图片描述
在这里插入图片描述
IP协议
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以太网帧协议
在这里插入图片描述
ARP协议
在这里插入图片描述

网络通信过程

封装
在这里插入图片描述
分用
在这里插入图片描述
在这里插入图片描述

socket

socket介绍
在这里插入图片描述
在这里插入图片描述

字节序

简介
在这里插入图片描述
举例
小端存储
在这里插入图片描述
大端存储
在这里插入图片描述

/*
    字节序:字节在内存中存储的顺序
    小端字节序:数据的高位字节存储在内存的高位地址,低位字节存储在内存的低位地址。
    大端字节序:数据的低位字节存储在内存的高位地址,高位字节存储在内存的低位地址。
*/
//通过代码检测当前主机的字节序
#include<stdio.h>
int main()
{
    union 
    {
        short value; //2字节
        char bytes[sizeof(short)]; //char[2]
    }test;
    test.value = 0x0102;
    if(test.bytes[0] == 1 && test.bytes[1] == 2)
    {
        printf("大端字节序\n");
    }
    else if(test.bytes[0] == 2 && test.bytes[1] == 1)
    {
        printf("小端字节序\n");
    }
    else
    {
        printf("未知\n");
    }
    return 0;
}

字节序转换函数

在这里插入图片描述
在这里插入图片描述
示例:

/*
    网络通信时,需要将主机字节序转换成网络字节序(大端),
    另外一端获取到数据以后根据情况将网络字节序转换成主机字节序
*/
#include<stdio.h>
#include<arpa/inet.h>
int main()
{
    //htons 转换端口
    unsigned short a = 0x0102;
    printf("%x\n",a);
    unsigned short b = htons(a);
    printf("%x\n",b);
    printf("=======================================\n");

    //htonl 转换IP
    char buf[4] = {192,168,1,100};
    int num = *(int*)buf;
    int sum = htonl(num);
    unsigned char *p = (char*)&sum;
    printf("%d,%d,%d,%d\n",*p,*(p+1),*(p+2),*(p+3));
    printf("=======================================\n");

    //ntohl
    unsigned char buf1[4] = {1,1,168,192};
    int num1 = *(int*)buf1;
    int sum1 = ntohl(num1);
    unsigned char* p1 = (unsigned char*)&sum1;
    printf("%d,%d,%d,%d\n",*p1,*(p1+1),*(p1+2),*(p1+3));

    return 0;
}

socket地址

在这里插入图片描述

通用socket地址
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
专用socket地址
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

IP地址转换函数

在这里插入图片描述
示例:

#include<stdio.h>
#include<arpa/inet.h>
int main()
{
    //创建一个ip字符串,点分十进制的ip地址字符串
    char buf[] = "192.168.1.4";
    unsigned int num = 0;

    //将点分十进制的ip字符串转换成网络字节序的整数
    inet_pton(AF_INET,buf,&num);
    unsigned char* p = (unsigned char*)&num;
    printf("%d,%d,%d,%d\n",*p,*(p+1),*(p+2),*(p+3));

    //将网络字节序的ip整数转换成点分十进制的ip字符串
    char ip[16] = "";
    const char* str = inet_ntop(AF_INET,&num,ip,16);
    printf("str : %s\n",str);
    printf("ip  : %s\n",ip);
    return 0;
}

TCP通信流程

在这里插入图片描述

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

套接字函数

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

//TCP通信的服务器端
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
    //1.创建socket(用于监听的套接字)
    int lfd = socket(AF_INET,SOCK_STREAM,0);
    if(lfd == -1)
    {
        perror("socket");
        exit(-1);
    }

    //2.绑定
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    //inet_pton(AF_INET,"192.168.43.130",saddr.sin_addr.s_addr);
    saddr.sin_addr.s_addr = INADDR_ANY;//0.0.0.0
    saddr.sin_port = htons(9999);
    int ret = bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1)
    {
        perror("bind");
        exit(-1);
    }

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

    //4.接受客户端连接
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    int cfd = accept(lfd,(struct sockaddr*)&clientaddr,&len);
    if(cfd == -1)
    {
        perror("accept");
        exit(-1);
    }
    //输出客户端的信息
    char clientIP[16];
    inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,clientIP,sizeof(clientIP));
    unsigned short clientPort = ntohs(clientaddr.sin_port);
    printf("client ip is : %s ; port is : %d \n",clientIP,clientPort);

    //5.通信
    char recvBuf[1024]={0};
    while(1)
    {
        //获取客户端的数据
        int len1 = read(cfd,recvBuf,sizeof(recvBuf));
        if(len1 == -1)
        {
            perror("read");
            exit(-1);
        }
        else if(len1 > 0)
        {
            printf("recv client data : %s\n",recvBuf);
        }
        else if(len1 == 0)
        {
            //表示客户端断开连接
            printf("client closed...");
            break;
        }
        //给客户端发送数据
        char* data="hello, i am server";
        write(cfd,data,strlen(data));
    }
    //关闭文件描述符
    close(cfd);
    close(lfd);
    return 0;
}

client

//TCP通信的客户端
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
    //1.创建套接字
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

    //2.连接服务器
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET,"192.168.43.130",&serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9999);
    int ret = connect(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    if(ret == -1)
    {
        perror("connect");
        exit(-1);
    }

    //3.通信
    char recvBuf[1024]={0};
    while(1)
    {
        //给服务器端发送数据
        char* data="hello, i am client";
        write(fd,data,strlen(data));
        sleep(1);
        //获取服务器端的数据
        int len = read(fd,recvBuf,sizeof(recvBuf));
        if(len == -1)
        {
            perror("read");
            exit(-1);
        }
        else if(len > 0)
        {
            printf("recv server data : %s\n",recvBuf);
        }
        else if(len == 0)
        {
            //表示服务器端断开连接
            printf("server closed...");
            break;
        }
    }
    //关闭连接
    close(fd);
    return 0;
}

TCP三次握手

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

TCP滑动窗口

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

TCP四次挥手

在这里插入图片描述

在这里插入图片描述

TCP通信并发

在这里插入图片描述

多进程实现并发服务器

server端代码:

#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
#include<wait.h>
#include<errno.h>
void recyleChild(int arg)
{
    while(1)
    {
        int ret = waitpid(-1,NULL,WNOHANG);
        if(ret == -1)
        {
            //所有子进程都回收了
            break;
        }
        else if(ret == 0)
        {
            //还有子进程活着
            break;
        }
        else if(ret > 0)
        {
            //被回收了
            printf("子进程 %d 被回收了\n",ret);
        }
    }
}

int main()
{
    struct sigaction act;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    act.sa_handler = recyleChild;

    //注册信号捕捉
    sigaction(SIGCHLD,&act,NULL);
    //创建socket
    int lfd = socket(PF_INET,SOCK_STREAM,0);
    if(lfd == -1)
    {
        perror("socket");
        exit(-1);
    }

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

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

    //不断循环等待客户端连接
    while(1)
    {
        //接受连接
        struct sockaddr_in cliaddr;
        int len = sizeof(cliaddr);
        int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
        if(cfd == -1)
        {
            if(errno == EINTR)
                continue;
            perror("accept");
            exit(-1);
        }

        //每一个连接进来,就创建一个子进程跟客户端通信
        pid_t pid = fork();
        if(pid == 0)
        {
            //子进程
            //获取客户端的信息
            char cliIP[16];
            inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,cliIP,sizeof(cliIP));
            unsigned short cliPort = ntohs(cliaddr.sin_port);
            printf("client ip is : %s , port is %d \n",cliIP,cliPort);

            //接收客户端发来的数据
            char recvBuf[1024] = {0};
            while(1)
            {
                int len = read(cfd,&recvBuf,sizeof(recvBuf));
                if(len == -1)
                {
                    perror("read");
                    exit(-1);
                }
                else if(len > 0)
                {
                    printf("recv client data : %s\n",recvBuf);
                }
                else if(len == 0)
                {
                    printf("client closed...\n");
                    break;
                }
                write(cfd,recvBuf,strlen(recvBuf)+1);
            }
            close(cfd);
            exit(0);    //退出当前子进程
        }
    }
    close(lfd);
    return 0;
}

client端代码:

//TCP通信的客户端
#include<stdio.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
    //1.创建套接字
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

    //2.连接服务器
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    inet_pton(AF_INET,"192.168.43.131",&serveraddr.sin_addr.s_addr);
    serveraddr.sin_port = htons(9998);
    int ret = connect(fd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
    if(ret == -1)
    {
        perror("connect");
        exit(-1);
    }

    //3.通信
    char recvBuf[1024]={0};
    int i=0;
    while(1)
    {
        //给服务器端发送数据
        sprintf(recvBuf,"data : %d \n",i++);
        write(fd,recvBuf,strlen(recvBuf)+1);
        //获取服务器端的数据
        int len = read(fd,recvBuf,sizeof(recvBuf));
        if(len == -1)
        {
            perror("read");
            exit(-1);
        }
        else if(len > 0)
        {
            printf("recv server data : %s\n",recvBuf);
        }
        else if(len == 0)
        {
            //表示服务器端断开连接
            printf("server closed...");
            break;
        }
        sleep(1);
    }
    //关闭连接
    close(fd);
    return 0;
}

多线程实现并发服务器

服务端

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

struct sockInfo
{
    int fd;         //通信的文件描述符
    pthread_t tid;  //线程号
    struct sockaddr_in addr;
};
struct sockInfo sockInfos[128];

void* working(void* arg)
{
    //子线程和客户端通信 
    //获取客户端的信息
    struct sockInfo* pinfo = (struct sockInfo*)arg;
    char cliIP[16];
    inet_ntop(AF_INET,&pinfo->addr.sin_addr.s_addr,cliIP,sizeof(cliIP));
    unsigned short cliPort = ntohs(pinfo->addr.sin_port);
    printf("client ip is : %s , port is %d \n",cliIP,cliPort);

    //接收客户端发来的数据
    char recvBuf[1024] = {0};
    while(1)
    {
        int len = read(pinfo->fd,&recvBuf,sizeof(recvBuf));
        if(len == -1)
        {
            perror("read");
            exit(-1);
        }
        else if(len > 0)
        {
            printf("recv client data : %s\n",recvBuf);
        }
        else if(len == 0)
        {
            printf("client closed...\n");
            break;
        }
        write(pinfo->fd,recvBuf,strlen(recvBuf)+1);
    }
    close(pinfo->fd);
    return NULL;
}

int main()
{
    //初始化数据
    int max=sizeof(sockInfos) / sizeof(sockInfos[0]);
    for(int i=0;i<max;i++)
    {
        bzero(&sockInfos[i],sizeof(sockInfos[i]));
        sockInfos[i].fd=-1;
        sockInfos[i].tid=-1;
    }

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

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

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

    //循环等待客户端连接,一旦一个客户端连接进来,就创建一个子线程进行通信
    while (1)
    {
        //接受连接
        struct sockaddr_in cliaddr;
        int len = sizeof(cliaddr);
        int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);


        struct sockInfo* pinfo;
        for(int i=0;i<max;i++)
        {
            //从这个数组中找到一个可以用的sockInfo元素
            if(sockInfos[i].fd == -1)
            {
                pinfo = &sockInfos[i];
                break;
            }
            if(i == max-1)
            {
                sleep(1);
                i=-1;
            }
        }
        pinfo->fd=cfd;
        memcpy(&pinfo->addr,&cliaddr,len);
        //创建子线程
        pthread_create(&pinfo->tid,NULL,working,pinfo);
        //释放
        pthread_detach(pinfo->tid);
    }
    close(lfd);
    return 0;
}

客户端不变

TCP状态转换

在这里插入图片描述
在这里插入图片描述
TIME_WAIT经过2MSL变成CLOSED
确保通信的另一方能够接收到最后一次的ACK
在这里插入图片描述

半关闭

一方close确认了,另一方还没close;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

端口复用

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

I/O多路复用(I/O多路转接)

此处I/O指的是对缓冲区的操作
在这里插入图片描述
两种待改进的模型:
1.阻塞等待
BIO模型:使用多线程缓解阻塞等待
在这里插入图片描述
accept、read、recv都是阻塞的
2.非阻塞、忙轮询
NIO模型:通过遍历轮询
在这里插入图片描述
使用IO多路转接技术进行改进,委托内核来检测。

select

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
select服务器

#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/select.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);

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

    while(1)
    {
        tmp = rdset;
        //调用select系统函数,让内核帮忙检测哪些文件描述符有数据
        int ret = select(maxfd+1,&tmp,NULL,NULL,NULL);
        if(ret == -1)
        {
            perror("select");
            exit(-1);
        }
        else if(ret == 0)
        {
            continue;
        }
        else if(ret > 0)
        {
            //说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
            //ret只返回有几个发生了改变,具体是哪几个需要我们自己去查
            if(FD_ISSET(lfd,&tmp))
            {
                //表示有新的客户端连接进来了
                struct sockaddr_in cliaddr;
                int len = sizeof(cliaddr);
                int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
                //将新的文件描述符加入到集合中,并更新maxfd
                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 len = read(i,buf,sizeof(buf));
                    if(len == -1)
                    {
                        perror("read");
                        exit(-1);
                    }
                    else if(len == 0)
                    {
                        printf("client closed ... \n");
                        close(i);
                        FD_CLR(i,&rdset);

                    }
                    else if(len > 0)
                    {
                        printf("read buf = %s\n",buf);
                        write(i,buf,strlen(buf)+1);
                    }
                }
            }
        }
    }
    close(lfd);
    return 0;
}

在这里插入图片描述

poll

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
poll服务器

#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<poll.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);

    //初始化检测的文件描述符数组
    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");
            exit(-1);
        }
        else if(ret == 0)
        {
            continue;
        }
        else if(ret > 0)
        {
            //说明检测到了有文件描述符的对应的缓冲区的数据发生了改变
            //ret只返回有几个发生了改变,具体是哪几个需要我们自己去查
            if(fds[0].revents & POLLIN)
            {
                //表示有新的客户端连接进来了
                struct sockaddr_in cliaddr;
                int len = sizeof(cliaddr);
                int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
                //将新的文件描述符加入到集合中,并更新最大文件描述符的索引
                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;
            }
            for(int i=1;i<=nfds;i++)
            {
                if(fds[i].revents & POLLIN)
                {
                    //说明这个文件描述符对应的客户端发来了数据
                    char buf[1024] = {0};
                    int len = read(fds[i].fd,buf,sizeof(buf));
                    if(len == -1)
                    {
                        perror("read");
                        exit(-1);
                    }
                    else if(len == 0)
                    {
                        printf("client closed ... \n");
                        close(fds[i].fd);
                        fds[i].fd = -1;

                    }
                    else if(len > 0)
                    {
                        printf("read buf = %s\n",buf);
                        write(fds[i].fd,buf,strlen(buf)+1);
                    }
                }
            }
        }
    }
    close(lfd);
    return 0;
}

epoll

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
epoll服务器:

#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/epoll.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);

                epev.events = EPOLLIN;
                epev.data.fd = cfd;
                epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
            }
            else
            {
                //有数据到达,需要通信
                char buf[1024] = {0};
                    int len = read(curfd,buf,sizeof(buf));
                    if(len == -1)
                    {
                        perror("read");
                        exit(-1);
                    }
                    else if(len == 0)
                    {
                        printf("client closed ... \n");
                        epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
                        close(curfd);
                    }
                    else if(len > 0)
                    {
                        printf("read buf = %s\n",buf);
                        write(curfd,buf,strlen(buf)+1);
                    }
            }
        }
    }
    close(lfd);
    close(epfd);
    return 0;
}

epoll的两种工作模式

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置边沿触发需要设置EPOLLET
ET模式服务器

#include<stdio.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<unistd.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 = 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
            {
                //有数据到达,需要通信
                //循环读取所有数据
                //上文中已将文件描述符设为非阻塞
                char buf[5];
                int len = 0;
                while((len = read(curfd,buf,sizeof(buf))) > 0)
                {
                    //打印数据
                    printf("recv data : %s\n",buf);
                    write(curfd,buf,len);
                }
                if(len == 0)
                {
                    printf("client closed ...\n");
                }
                else if(len == -1)
                {
                    if(errno == EAGAIN)
                    {
                        printf("data read over ...\n");
                    }
                    else
                    {
                        perror("read");
                        exit(-1);
                    }
                }
            }
        }
    }
    close(lfd);
    close(epfd);
    return 0;
}

UDP通信

在这里插入图片描述
在这里插入图片描述
服务端:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
    //1.创建一个通信的socket
    int fd = socket(PF_INET,SOCK_DGRAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

    //2.绑定
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9999);
    addr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
    if(ret == -1)
    {
        perror("bind");
        exit(-1);
    }

    //3.通信
    while(1)
    {
        char recvbuf[128];
        char ipbuf[16];
        struct sockaddr_in cliaddr;
        int len =sizeof(cliaddr);
        //接收数据
        int num = recvfrom(fd,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&cliaddr,&len);
        printf("client IP : %s, PORT : %d\n",
            inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,ipbuf,sizeof(ipbuf)),
            ntohs(cliaddr.sin_port)
        );
        printf("client say : %s\n",recvbuf);

        //发送数据
        sendto(fd,recvbuf,strlen(recvbuf)+1,0,(struct sockaddr*)&cliaddr,sizeof(cliaddr));
    }
    close(fd);
    return 0;
}

客户端:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
    //1.创建一个通信的socket
    int fd = socket(PF_INET,SOCK_DGRAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

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

    int num=0;
    //2.通信
    while(1)
    {
        //发送数据
        char sendbuf[128];
        sprintf(sendbuf,"hello , i am client %d\n",num++);
        sendto(fd,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&saddr,sizeof(saddr));
        //接收数据
        recvfrom(fd,sendbuf,sizeof(sendbuf),0,NULL,NULL);
        printf("server say : %s\n",sendbuf);

        sleep(1);
    }
    close(fd);
    return 0;
}

广播

在这里插入图片描述
在这里插入图片描述
服务器代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
    //1.创建一个通信的socket
    int fd = socket(PF_INET,SOCK_DGRAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

    //2.设置广播属性
    int op = 1;
    setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&op,sizeof(op));
    
    //2.创建一个广播的地址
    struct sockaddr_in cliaddr;
    cliaddr.sin_family = AF_INET;
    cliaddr.sin_port = htons(9999);
    inet_pton(AF_INET,"192.168.43.255",&cliaddr.sin_addr.s_addr);

    //3.通信
    int num = 0;
    while(1)
    {
        char sendbuf[128];
        sprintf(sendbuf,"hello, client ...%d\n",num++);
        //发送数据
        sendto(fd,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&cliaddr,sizeof(cliaddr));
        printf("广播的数据:%s\n",sendbuf);
        sleep(1);
    }
    close(fd);
    return 0;
}

客户端代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
    //1.创建一个通信的socket
    int fd = socket(PF_INET,SOCK_DGRAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

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

    int num=0;
    //2.通信
    while(1)
    {
        char buf[128];
        //接收数据
        recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);
        printf("server say : %s\n",buf);
    }
    close(fd);
    return 0;
}

组播(多播)

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

服务器代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
    //1.创建一个通信的socket
    int fd = socket(PF_INET,SOCK_DGRAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

    //2.设置多播属性,设置外出接口
    struct in_addr imr_multiaddr;
    //初始化多播地址
    inet_pton(AF_INET,"239.0.0.10",&imr_multiaddr.s_addr);
    setsockopt(fd,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++);
        //发送数据
        sendto(fd,sendbuf,strlen(sendbuf)+1,0,(struct sockaddr*)&cliaddr,sizeof(cliaddr));
        printf("组播的数据:%s\n",sendbuf);
        sleep(1);
    }
    close(fd);
    return 0;
}

客户端代码:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
    //1.创建一个通信的socket
    int fd = socket(PF_INET,SOCK_DGRAM,0);
    if(fd == -1)
    {
        perror("socket");
        exit(-1);
    }

    //客户端绑定本地的IP和端口
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(fd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(ret == -1)
    {
        perror("bind");
        exit(-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(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&op,sizeof(op));
    //2.通信
    while(1)
    {
        char buf[128];
        //接收数据
        recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);
        printf("server say : %s\n",buf);
    }
    close(fd);
    return 0;
}

本地套接字

在这里插入图片描述
流程:
在这里插入图片描述
在这里插入图片描述
服务端代码:

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

int main()
{
    //先把伪文件删了
    unlink("server.sock");
    //1.创建监听的套接字
    int lfd = socket(AF_LOCAL,SOCK_STREAM,0);
    if(lfd == -1)
    {
        perror("socket");
        exit(-1);
    }

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

    //3.监听
    ret = listen(lfd,100);
    if(ret == -1)
    {
        perror("listen");
        exit(-1);
    }

    //4.等待客户端连接
    struct sockaddr_un cliaddr;
    int len = sizeof(cliaddr);
    int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
    if(cfd == -1)
    {
        perror("accept");
        exit(-1);
    }
    printf("client socket filename : %s\n",cliaddr.sun_path);

    //5.通信
    while (1)
    {
        char buf[128];
        int len = recv(cfd, buf, sizeof(buf),0);
        if(len == -1)
        {
            perror("recv");
            exit(-1);
        }
        else if(len == 0)
        {
            printf("client closed ...\n");
            break;
        }
        else if(len > 0)
        {
            printf("client say : %s\n",buf);
            send(cfd,buf,len,0);
        }
    }
    close(cfd);
    close(lfd);
    return 0;
}

客户端代码:

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

int main()
{
    //先把伪文件删了
    unlink("client.sock");
    //1.创建套接字
    int cfd = socket(AF_LOCAL,SOCK_STREAM,0);
    if(cfd == -1)
    {
        perror("socket");
        exit(-1);
    }

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

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

    //4.通信
    int num = 0;
    while (1)
    {
        //发送数据
        char buf[128];
        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");
            exit(-1);
        }
        else if(len == 0)
        {
            printf("server closed ...\n");
            break;
        }
        else if(len > 0)
        {
            printf("server say : %s\n",buf);
        }
        sleep(1);
    }
    close(cfd);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值