美文网首页一个Java码农眼中的技术世界技术干货
资深程序员大牛带你用C语言实现网络通信(流程实例)

资深程序员大牛带你用C语言实现网络通信(流程实例)

作者: 6596bd2fab00 | 来源:发表于2018-04-05 07:11 被阅读2次

    零基础小白到大神之路,欢迎加学习企鹅裙 341636727裙里有免费c/c++课程,还有大量干货哦

    主要函数:

    ------------------------------------------

    TCP实现服务器与客户端的通信流程

    //服务器端---服务器是一个被动的角色

    1.socket          //买一个手机

    2.bind            //SIM卡 绑定一个手机号(ip+port)

    3.listen          //待机(等待电话打入)

    4.accept          //接听电话

    5.read/write      //通话

    6.close           //挂机

    //客户端---客户端是一个主动发起请求的一端

    1.socket           //买一个手机

    2.bind(可选的)     //SIM卡(绑定号码)

    3.connect          //拨打电话

    4.read/write       //通话

    5.close            //挂机

    //1.socket  ---- 插口 

    int socket(int domain, int type, int protocol);

    功能:  创建通信的一端 (socket)

    参数:

        @domain    //"域" --范围

                   AF_INET  //IPV4 协议的通信

        @type      SOCK_STREAM //TCP (流式套接字)

        @protocol  0           //LINUX下 流式套接字 ==>TCP

                  //协议 

    返回值:

       成功 对应的socket文件描述符

       失败 返回-1 

    注意:

    文件描述符:

       实际上就是 创建好的 socket的一个标识符

    //2.bind 

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

    功能:

        给指定的 socket 绑定地址信息

    参数:

       @sockfd    //表示操作的socket

       @addr      //填充的地址信息(ip + port)

       @addrlen   //地址信息结构体的大小

    返回值:

       成功 0

       失败 -1

       //通用的地址结构

       struct sockaddr {

           sa_family_t sa_family; //AF_INET //IPV4的协议

           char       sa_data[14];//(ip+port)

       }

    //网络通信的地址结构(internet)

    struct sockaddr_in {

        sa_family_t    sin_family; /* address family: AF_INET */

        in_port_t      sin_port;   /* port in network byte order */

        struct in_addr sin_addr;   /* internet address */

    };

    /* Internet address. */

    struct in_addr {

        uint32_t       s_addr;      /* address in network byte order */

    };

    //1.定义一个 地址结构体变量

    struct sockaddr_in addr;

    bzero(&addr,sizeof(addr)); //清0的函数

    //2.之后进行信息填充

    addr.sin_family = AF_INET;

    addr.sin_port = htons(8888);

    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 

                         //127.0.0.1 是回环测试的地址

    //3.进行绑定

    if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0)

    {

        perror("bind fail");

        return 0;

    }

    //3.listen  --- 设置监听 ---作用:让操作系统监控是否有客户端发起连接

    int listen(int sockfd, int backlog);

    功能:

       设置监听 

    参数:

       @sockfd    //监听套接字

       @backlog   //监听队列的大小

    返回值

       成功 0

       失败 -1

       listenfd 

    --------------监听队列------------------

    fd1 fd2 fd3 fd4

     |

    -|--------------------------------------

     |

     \---->建立好连接的套接字 

           accept函数获取已连接的套接字 返回对应

           的标识符 

             |--->后面的读写操作 都是通过这个标识符

                  进行的

    -----------------------------------------------

    accept(); //accept 从监听队列中获得已连接的的socket,返回一个标示符来表示已连接的socket

              //后续通过已连接的socket进行通信

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

    功能: 获取连接

    参数:

       @sockfd    //监听套接字的fd(标识符)

       @addr     //来电显示(保存对端的地址信息)(ip+port)

       @addrlen  //表示 addr 参数对应类型的大小,值结果参数 --- 就是在用的时候,必须先赋一个初值,最后函数调用完成

                 //通过该参数,返回一个结果值

    返回值:

       成功 已连接的socket的标识符

       失败 -1

    //connect ---发起连接

       int connect(

           int sockfd,  //表示 进行通信的 socket的标识符

           const struct sockaddr *addr, //对端的地址信息(ip+port)

           socklen_t addrlen); //表示的是 addr 参数类型的长度

          参数:

            @sockfd   //通过socket函数获得的fd

            @addr     //服务器端的地址

            @addrlen  //参数addr类型的大小 

    //数据流向  fd --> buf (count 表示一次读取多少个字节)

    ssize_t read(int fd, void *buf, size_t count);

    //数据流向  buf--> fd (count 表示一次写多少个字节)

    ssize_t    write(int  fd,    const  void  *buf, size_t count);

    参数:

       @fd 就是要操作的 socket对应的 标示符

       @buf 保存数据的一块内存首地址

       @count 一次操作的字节数

    confd 

    char buf[] = "hello QCXY\n";

    write(confd,buf,strlen(buf)); //写 数据到socket中

    //读数据出来

    char rbuf[1024] = {0}; //表示申请了一块1024个字节大小

                           //的内存空间

    read(confd,rbuf,sizeof(rbuf)); //读取数据

    //练习:

    实现 客户端 向服务器发送数据

    服务器回发数据的功能

    client       -----        server

     scanf();  ---(1)---->        read 之后printf

      read     <--(2)----         write

      printf

      循环做,结束条件

      当客户端输入 "quit"字符串时 客户端结束

      怎么判断客户端读到的是"quit"

     c语言中

     "quit" == buf; (X) //不能这么写

    //字符串的比较函数

     strcmp("quit",buf);

     strncmp("quit",buf,4);

    客户端的程序:

    #include

    #include           /* See NOTES */

    #include

    #include

    #include

    #include

    #include

    //./client 127.0.0.1 8888 

    int main(int argc, const char *argv[])

    {

        int fd;

        int ret = 0;

        char buf[1024] = {0};

        char rbuf[1024] = {0};

        //处理命令行参数

        //1.socket(手机)

        //2.bind(电话卡)

        //3.connect (拨打电话)

        //处理命令行参数

        if(argc != 3)

        {

            printf("Usage: %s \n",argv[0]);

            return -1;

        }

        //1.socket(手机)

        fd = socket(AF_INET,SOCK_STREAM,0);

        if(fd < 0) //出错处理

        {

            perror("socket fail");

            return -1;

        }

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

        //2.bind(电话卡)---绑定的是客户端自己的地址信息

         //客户端地址信息

        //1.定义一个 地址结构体变量

        struct sockaddr_in cli_addr;

        bzero(&cli_addr,sizeof(cli_addr)); //清0的函数

        //2.之后进行信息填充

        cli_addr.sin_family = AF_INET;

        cli_addr.sin_port = htons(7777);

        cli_addr.sin_addr.s_addr = inet_addr(argv[1]);

        if(bind(fd,(struct sockaddr*)&cli_addr,sizeof(cli_addr)) < 0)

        {

            perror("bind fail");

            return -1;

        }

        //服务器端的地址信息

        //1.定义一个 地址结构体变量

        struct sockaddr_in addr;

        bzero(&addr,sizeof(addr)); //清0的函数

        //2.之后进行信息填充

        addr.sin_family = AF_INET;

        addr.sin_port = htons(atoi(argv[2]));

        addr.sin_addr.s_addr = inet_addr(argv[1]); 

        //3.connect (拨打电话)

        if(connect(fd,(struct sockaddr*)&addr,sizeof(addr))<0)

        {

            perror("connect fail");

            return -1;

        }

        printf("connect success\n");

       //通信过程

        while(1)

        {

         //客户端从键盘获得数据 

         //数据流向stdin --> buf

         fgets(buf,sizeof(buf),stdin); //stdin表示是从键盘获得数据

         //发送给服务器

         write(fd,buf,strlen(buf));

         //接受服务器回发的消息

         ret = read(fd,rbuf,sizeof(rbuf));

         //如果回发的消息是

         //quit 

         //则结束

         rbuf[ret] = '\0';

         printf("rbuf = %s\n",rbuf);

         if(strncmp("quit",buf,4) == 0)

         {

             close(fd);

             break;

         }

        }

        return 0;

    }

    服务端

    #include

    #include           /* See NOTES */

    #include

    #include

    #include

    #include

    //./server 127.0.0.1 8888

    int main(int argc, const char *argv[])

    {

        int fd = 0;

        int connfd = 0;

        int ret = 0; 

        char buf[1024] = {0};

        //处理命令行参数

        if(argc != 3)

        {

            printf("Usage: %s \n",argv[0]);

            return -1;

        }

        //1.socket 创建套接字

        fd = socket(AF_INET,SOCK_STREAM,0);

        if(fd < 0) //出错处理

        {

            perror("socket fail");

            return -1;

        }

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

        //2.绑定

        //1.准备地址信息

        //2.绑定

        //

        //1.定义一个 地址结构体变量

        struct sockaddr_in addr;

        bzero(&addr,sizeof(addr)); //清0的函数

        //2.之后进行信息填充

        addr.sin_family = AF_INET;

        addr.sin_port = htons(atoi(argv[2]));

        addr.sin_addr.s_addr = inet_addr(argv[1]); 

        //127.0.0.1 是回环测试的地址

        //3.进行绑定

        if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)

        {

            perror("bind fail");

            return 0;

        }

        printf("bind success\n");

        //4.设置监听

        if(listen(fd,5) < 0)

        {

            perror("listen fail");

            return -1;

        }

        struct sockaddr_in peer_addr;

        socklen_t addrlen = sizeof(peer_addr); 

        //5.获取连接 -- 接听电话

        while(1) //可以不断接受客户端的请求

        {

            //connfd =  accept(fd,NULL,NULL);

            connfd =  accept(fd,(struct sockaddr*)&peer_addr,&addrlen);

            if(connfd < 0)

            {

                perror("accept fail");

                return -1;

            }

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

            printf("-----------------------\n");

            printf("ip = %s\n",inet_ntoa(peer_addr.sin_addr));

            printf("port = %d\n",ntohs(peer_addr.sin_port));

            printf("-----------------------\n");

          //通信过程

          //效果实现数据回发

            while(1)

            {

                //read 与 write 的返回值 大于0 时表示的是

                //一次成功操作到的字节数

                ret = read(connfd,buf,sizeof(buf));

                //hello

                buf[ret] = '\0'; //添加'\0'--转换成字符串

                printf("buf = %s\n",buf);//字符串打印 需要

                                         //结束标志 '\0'

                if(ret == 0 || strncmp(buf,"quit",4) == 0)

                {

                    close(connfd);

                    break;

                }

                write(connfd,buf,ret);

            }

        } //telnet

        return 0;

    }

    补充:

    可以用如下函数替代read,write

         ssize_t recv(int sockfd,  void* buf,size_t len,int flags);

        ssize_t send(int sockfd,const void *buf,size_t len,int flags);

         @sockfd //进行操作的socket的文件描述符

         @buf    //保存数据的首地址

         @len    //一次操作的字节数

         @flags   //标志0--默认的操作方式(阻塞)

    返回值:

          成功  成功操作的字节数

         失败   -1&errno

    相关文章

      网友评论

        本文标题:资深程序员大牛带你用C语言实现网络通信(流程实例)

        本文链接:https://www.haomeiwen.com/subject/vqlrhftx.html