zoukankan      html  css  js  c++  java
  • Linux 网络 tcp C/S通信模型

    C/S模型就是server 与 client 的模型

    TCP服务器模型流程图                                                               TCP 客户端模型流程图:

                    

      函数使用:

    (1)创建一个网络通信套接字描述符  int socket(int domain, int type, int protocol);  

      参数:domain : 协议系列,常用的是 AF_INET 表示IPV4 

                      

            type : 常用的两个

            SOCK_STREAM    流式套接字 TCP 常用

            SOCK_DGRAM  数据报套接字 UDP 常用

        

        protocol:一般为0

        

      返回值:通信过程中使用的socket(一个文件描述符)

    (2)把socket与 ip, 端口绑定一起     int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

       参数 :sockfd :通过socket 函数创建返回的通信文件描述符

           addr:addr 是指向 sockaddr_in 结构的指针,包含本机IP 地址和端口号协议类型等

          addrlen: addrLen : sizeof (struct sockaddr_in) 是第二个参数addr结构所占的有效长度

     

    struct sockaddr结构 和 struct sockaddr_in结构

    struct sockaddr {
      sa_family_t sa_family; //协议族
      char sa_data[14];  // 14字节协议地址
    }
    
    struct sockaddr_in{
      u_short sin_family;// 地址族, AF_INET,2 bytes
      u_short sin_port; // 端口,2 bytes
      struct in_addr sin_addr; // IPV4地址结构,4 bytes
      char sin_zero[8]; // 8 bytes unused,作为填充
    };

     在bind时使用 struct sockaddr_in 结构进行初始化,然后通过 struct sockaddr 强制类型转换

    sockaddr_in 与 sockaddr 结构的区别

     

    (3) 监听绑定了ip和port的socket :int listen(int sockfd, int backlog);

      参数:sockfd:监听连接的套接字

         backlog 指定了完全成功连接的最大队列长度,它的作用在于处理可能同时出现的几个连接请求。此参数现在基本已经不用。

      返回值: 0 或 -1

    完成listen()调用后,socket变成了监听

    (4)阻塞等待连接 :int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

      参数: sockfd : 监听套接字

          addr : 对方地址

         addrlen:地址长度

      返回值:已建立好连接的套接字 或 -1

          

    (5) 发送数据

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

      参数: sockfd :向哪个socket发送数据

         buf :存放数据缓存首地址

         len :发送的数据长度

         flags:发送方式(通常为0)

      返回值:成功:实际发送的字节数     失败:-1, 并设置errno

     (6) 接收数据

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

      参数:socket:向哪个socket发送数据

          buf : 发送缓冲区首地址

          length : 发送的字节数

          flags : 接收方式(通常为0)

      返回值: 成功:实际接收的字节数      失败:-1, 并设置errno

     (7) 客户端连接服务器的函数

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

      参数:sockfd : socket返回的文件描述符

         serv_addr : 服务器端的地址信息 

         addrlen : serv_addr的长度

      返回值:0 或 -1

    (8) 关闭通信socket    int close(int fd);

     编写TCP 的server和client 通信程序,服务器监听接收数据

      server.c文件:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>        
    #include <sys/socket.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <string.h>
    #include <strings.h>
    
    #define MAX_BUF          1024
    int main(int argc, const char *argv[])
    {
        char buf[MAX_BUF] = {0};//接收数据缓存
        int server_sock_fd; //服务器,socket描述符
        int client_sock_fd; //链接的客户端,socket 描述符
        ssize_t tybes;
    
        struct sockaddr_in server_addr;//存放服务器ip和端口
        struct sockaddr_in client_addr;//存放客户端ip和端口
        socklen_t len; 
        
        len = sizeof(server_addr);
        bzero(&server_addr,len);//清零
        server_sock_fd = socket(AF_INET, SOCK_STREAM,0);//创建socket,返回socket描述符
        if(server_sock_fd < 0)
        {
            perror("socket fail ");
            exit(1);
        }
    
        //填充 地址和端口结构
        server_addr.sin_family = AF_INET; //协议族
    //    server_addr.sin_port = htons(atoi(argv[2])); //服务器端口    //这两行是通过执行程序时传入参数ip和port
    //    server_addr.sin_addr.s_addr = inet_addr(argv[1]);//IP地址
        server_addr.sin_port = htons(atoi("8080")); //服务器端口 , htons 把主机字节序转为网络字节序,atoi 把字符数据转为整数
        server_addr.sin_addr.s_addr = inet_addr("192.168.3.132");//IP地址 ,inet_addr把字符串ip转为网络字节序整数
        if(bind(server_sock_fd, (struct sockaddr*)&server_addr, len) < 0) //绑定服务器 ip和port
        {
            perror("fail bind ");
            exit(1);
        }
    
        if( listen(server_sock_fd,10) < 0 ) //监听 服务器 
        {
            perror("fail listen ");
            exit(1);
        }
        
        while(1)
        {
            client_sock_fd = accept(server_sock_fd,(struct sockaddr*)&client_addr,&len);//阻塞等待客户端链接
            if(client_sock_fd < 0)
            {
                perror("client socket fail ");
                exit(1);
            }
            printf("client ip %s   port %u
    ",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port)); //打印链接的ip 和 port
            while(1)
            {
                memset(buf,0,1024);
            //    tybes = recv(client_sock_fd,buf,10,0);
                tybes = read(client_sock_fd,buf,10);
                if(tybes <= 0) //断开链接或者出错
                {
                    puts("recv error !");
                    break;
                }
                printf("recv data : %s",buf);//IO缓冲 加 换行输出
                if(strstr(buf,"quit") != NULL ) //接收到 “quit” 断开连接的客户端,等待下一个连接
                {
                    close(client_sock_fd);
                    break;
                }
            }
    
        }
        close(server_sock_fd);
        return 0;
    }

      client.c 文件;

    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <sys/types.h>        
    #include <sys/socket.h>
    #include <strings.h>
    #include <string.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    int main(int argc, const char *argv[])
    {
        int client_sock_fd;
        char buf[20] = {0};
        ssize_t send_tybes;
        struct sockaddr_in server_addr; 
        socklen_t len;
        len = sizeof(server_addr);
        bzero(&server_addr,len); //清零
    
        client_sock_fd = socket(AF_INET,SOCK_STREAM,0); //创建客户端 socket 
        if(client_sock_fd < 0)
        {
            perror("client_sock_fd fail ");
            exit(1);
        }
    
        //填充服务器 地址
        server_addr.sin_family = AF_INET;
    //    server_addr.sin_port = htons(atoi(argv[2]));
    //    server_addr.sin_addr.s_addr = (inet_addr(argv[1]));
        server_addr.sin_port = htons(atoi("8080"));
        server_addr.sin_addr.s_addr = (inet_addr("192.168.3.132"));
    
        if( connect(client_sock_fd, (struct sockaddr*)&server_addr, len) < 0) //连接服务器
        {
            perror("connect fail ");
            exit(1);
        }
    
        while(1) 
        {
            fgets(buf,10,stdin); //终端读取数据
            send_tybes = send(client_sock_fd,buf,10,0);
            if(send_tybes <= 0) //接收数据失败或者断开连接
            {
                printf("send fail !");
                break;
            }
            if(strstr(buf,"quit") != NULL) //输入 “quit” 断开连接
            {
                break;
            }
        }
        close(client_sock_fd);
        return 0;
    }

    测试:此时,服务器只是接收数据,不能发送数据,因为接收和终端获取数据是两个阻塞函数,需要开启多进程或者多线程处理两个阻塞问题

      

  • 相关阅读:
    springboot2.1.3使用jdbcTemplate
    httpclient4.5.2 Post请求支持http和https
    springboot2.1.3+spring-session2.1.4分库处理
    mysql查看当前实时连接数
    springboot2.1.3+Junit4 单元测试
    subprocess.Popen()详解
    matplotlib 设置图形大小时 figsize 与 dpi 的关系
    matplotlib之subplot
    matplotlib.pyplot.plot()参数详解
    plt.rcParams属性总结
  • 原文地址:https://www.cnblogs.com/electronic/p/10981614.html
Copyright © 2011-2022 走看看