网络编程 -------- 3、TCP_UDP_UNIX

1、基于TCP的套接字编程流程 

    Server.c 
        socket 
        bind  (服务器的ip+端口)
        listen
        accept
        recv / send
        close

    Client.c 
        socket 
        connect (服务器的ip+端口)
        send / recv
        close

扩展:

        (1) 三路握手: TCP建立连接时
                1)SYN请求 (客户端-->服务器) 
                2)SYN+ACK应答 (服务器-->客户端) 
                3)ACK确认 (客户端-->服务器)


        (2) 四次挥手: TCP断开连接时 
                1)FIN请求 (客户端-->服务器)
                2)ACK应答 (服务器-->客户端)
                3)FIN请求 (服务器-->客户端)
                4)ACK应答 (客户端-->服务器)

2、基于UDP的套接字编程流程 

    Server.c 
        socket  
        bind  (服务器的ip+端口)
        recvfrom / sendto
        close

    Client.c 
        socket 
        //设置服务器的ip和端口
        sendto / recvfrom
        close


            利用UDP 实现简单的通信   UDP ---> SOCK_DGRAM 数据报套接字类型
                udp_server.c   接收数据 

                    int main( int argc, char *argv[] )
                    {
                        //1.创建套接字 UDP ---> SOCK_DGRAM 数据报套接字
                        int server_fd = socket( AF_INET, SOCK_DGRAM, 0 );
                        if( server_fd == -1 )
                        {
                            perror("socket server error ");
                            return -1;
                        }
                        printf("server_fd = %d\n", server_fd );

                        //2.绑定服务器的ip和端口
                        struct sockaddr_in  server_addr;
                        server_addr.sin_family = AF_INET;           //协议族 
                        server_addr.sin_port = htons( atoi(argv[2]) );    //端口号
                        inet_aton( argv[1], &server_addr.sin_addr );  //IP地址

                        int re = bind( server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr) );
                        if( re == -1 )
                        {
                            perror("bind server error ");
                            return -1;
                        }
                        printf("bind server success\n");

                        //3.通信 
                        while( 1 )
                        {
                            //接收数据 
                            char buf[128] = {0};
                            struct sockaddr_in  client_addr;
                            socklen_t len = sizeof(client_addr);

                            re = recvfrom( server_fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &len );
                            if( re >= 0 )
                            {
                                printf("%s : %s\n", inet_ntoa(client_addr.sin_addr), buf );
                            }
                            else 
                            {
                                perror("recvfrom server error ");
                                break;
                            }

                            //人为定义退出条件
                            if( buf[0] == '#' )
                            {
                                break;
                            }
                        }

                        //4.关闭套接字
                        close( server_fd );
                    }

                udp_client.c   发送数据  

                    int main( int argc, char *argv[] )
                    {
                        //1.创建套接字 UDP ---> SOCK_DGRAM 数据报套接字
                        int client_fd = socket( AF_INET, SOCK_DGRAM, 0 );
                        if( client_fd == -1 )
                        {
                            perror("socket client error ");
                            return -1;
                        }
                        printf("client_fd = %d\n", client_fd );

                        //2.设置服务器的ip和端口
                        struct sockaddr_in  server_addr;
                        server_addr.sin_family = AF_INET;           //协议族 
                        server_addr.sin_port = htons( atoi(argv[2]) );    //端口号
                        inet_aton( argv[1], &server_addr.sin_addr );  //IP地址

                        //3.通信 
                        while(1)
                        {
                            //发送数据 
                            char buf[128] = {0};
                            printf("input data : ");
                            fgets(buf, sizeof(buf), stdin);

                            int re = sendto( client_fd, buf, strlen(buf), 0, (struct sockaddr *)&server_addr, sizeof(server_addr) );
                            if( re == -1 )
                            {
                                perror("sendto error ");
                                break;
                            }

                            //人为定义退出条件
                            if( buf[0] == '#' )
                            {
                                break;
                            }
                        }

                        //4.关闭套接字
                        close( client_fd );
                    }

            

3、UNIX域协议

        UNIX域协议是利用socket编程接口 来实现 本地进程之间(客户端/服务器)的通信,它是进程间通信(IPC)的一种方式。它使用文件系统中的路径名来标识服务器和客户端。

        UNIX域协议的套接字: 
            SOCK_STREAM     --->  TCP 面向字节流 
            SOCK_DGRAM      --->  UDP 面向数据报  

        其编程接口 和 流程 与 ipv4协议族是一样的
        只不过 协议族为 AF_UNIX , 对应的地址结构体为 

            UNIX域协议地址结构体 ( man 7 unix )
                    #include <sys/un.h>

                struct sockaddr_un 
                {
                    sa_family_t  sun_family;        /* 协议族 AF_UNIX */
                    char         sun_path[108];     /* UNIX域协议的地址,在本地文件系统中的“绝对路径名” pathname */
                };

            #define UNIX_PATH  "/home/china/unix2418F"

        实现方法:  UDP / TCP 
       

练习: 
            利用UNIX域协议 实现简单的通信 (以UDP为例)

                unix_server.c   接收数据 

                    int main( int argc, char *argv[] )
                    {
                        //删除 
                        unlink( UNIX_PATH );

                        //1.创建套接字 AF_UNIX  
                        int server_fd = socket( AF_UNIX, SOCK_DGRAM, 0 );
                        if( server_fd == -1 )
                        {
                            perror("socket server error ");
                            return -1;
                        }

                        //2.绑定 服务器的地址 
                        struct sockaddr_un  server_addr;
                        server_addr.sun_family = AF_UNIX;           //协议族 
                        strcpy( server_addr.sun_path, UNIX_PATH );  //unix域协议的地址

                        int re = bind( server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr) );
                        if( re == -1 )
                        {
                            perror("bind server error ");
                            close( server_fd );
                            return -1;
                        }
                        printf("bind server success\n");

                        struct sockaddr_un  client_addr;
                        socklen_t len = sizeof(client_addr);

                        //3.通信 
                        while(1)
                        {
                            //接收数据 
                            char buf[128] = {0};
                            re = recvfrom( server_fd, buf, sizeof(buf), 0, (struct sockaddr *)&client_addr, &len );
                            if( re >= 0 )
                            {
                                printf("recv : %s\n", buf );
                            }
                            else 
                            {
                                perror("recvfrom server error ");
                                break;
                            }
                            
                            if( buf[0] == '#' )
                            {
                                break;
                            }
                        }

                        //4.关闭套接字
                        close( server_fd );
                    }


                unix_client.c   发送数据  

                    //1.创建套接字 
                    //2.设置服务器的地址 
                    //3.通信 
                    //4.关闭套接字


4、套接字选项  

    套接字选项是用来设置或获取套接字的一些特性的选项。

        每个套接字在不同的层次上(级别) 有不同的行为属性(选项) 

        有两个接口函数用来 获取和设置 套接字的选项: 

            getsockopt 
            setsockopt  

                NAME
                    getsockopt, setsockopt - get and set options on sockets
                SYNOPSIS
                    #include <sys/types.h>          /* See NOTES */
                    #include <sys/socket.h>

                    int getsockopt(int sockfd, int level, int optname,
                                    void *optval, socklen_t *optlen);
                    int setsockopt(int sockfd, int level, int optname,
                                    const void *optval, socklen_t optlen); 
                        功能:获取/设置套接字的选项 
                        参数: 
                            sockfd:指定要操作的套接字描述符 
                            level:级别,不同的选择在不同的级别上(查看资料)
                            optname:选项名 
                            optval: 通用指针 
                                        get 用来保存获取到的选项值 
                                        set 用来保存要设置的选项值 
                                             不同的选项 对应的类型是不一样的 
                                                如果为 int  ---> 0  禁用选项 
                                                            --> 非0 使能选项 
                            optlen: 
                                    get 指针,指向空间保存选项值的空间的长度 
                                    set 变量,用来指定要设置的选项值的长度 
                        返回值: 
                            成功,返回0
                            失败,返回-1,同时errno被设置
                     


            设置 端口号重用 

                 选项名: SO_REUSEPORT 
                 级别:  SOL_SOCKET
                 值的类型: int 

                 /设置端口号重用 
                 int n = 1;
                 setsockopt( server_sock, SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n) );

5、UDP的例子 (拓展)

    1)DNS : Domain Name System 域名系统  

            www.baidu.com  ----> 域名  

            DNS:是把一个域名 转换成 相应的ip地址的服务  

            DNS协议 传输层用到UDP协议 
                    DNS Server  
                        ip: 114.114.114.114 
                        端口  53

  
    2)NTP : Network Time Protocol 网络时间协议

  
            基于UDP的传输层协议 

                服务器: 
                    阿里云 :  182.92.12.11
                    端口: 123      

        利用TCP 写一个网络传输文件的程序  
            file_tcp_server.c   服务器 接收文件  
            file_tcp_client.c   客户端 发送文件  

                注意: 
                    通信双方 发送和接收的 数据、大小、顺序 要保持一致 
                    遵守通信双向的约定 ---> "协议"

                file_tcp_server.c   服务器 接收文件  

                    int recv_file( int sock_fd )
                    {
                        //切换路径 
                        chdir("../");

                        //接收文件名的长度大小 
                        int name_len = 0;
                        int re = recv( sock_fd, &name_len, sizeof(name_len), 0 );
                        if( re == -1 )
                        {
                            perror("recv name_len error "); 
                            return -1;
                        }
                        printf("recv name_len success , len = %d\n", name_len );

                        //接收文件名
                        char filename[128] = {0};
                        re = recv( sock_fd, filename, name_len, 0 );
                        if( re == -1 )
                        {
                            perror("recv filename error ");
                            return -1;
                        }
                        printf("recv filename success , filename = %s\n", filename );
                        

                        //接收文件的大小 
                        int file_size = 0;
                        re = recv( sock_fd, &file_size, sizeof(file_size), 0 );
                        if( re == -1 )
                        {
                            perror("recv file_size error ");
                            return -1;
                        }
                        printf("recv file_size success , file_size = %d\n", file_size );

                        /* 接收文件的内容
                            创建并打开文件
                            接收数据
                            写入文件
                            关闭文件
                        */
                        //创建并打开文件
                        int fd = open( filename, O_RDWR | O_CREAT | O_EXCL, 0666 );
                        if( fd == -1 )
                        {
                            if( errno == EEXIST )  //文件已经存在,就直接打开即可
                            {
                                fd = open( filename, O_RDWR | O_TRUNC );
                            }
                            else 
                            {
                                perror("open file error ");
                                return -1;
                            }
                        }

                        int write_size = 0;      //已经写入的字节数
                        while( write_size < file_size )
                        {
                            //先接收数据 
                            char buf[128] = {0};
                            re = recv( sock_fd, buf, 100, 0 );
                            if( re > 0 )
                            {
                                //写入文件 
                                write( fd, buf, re );
                                write_size += re;
                            }
                            else 
                            {
                                perror("recv file error ");
                                break;
                            }
                        }

                        if( write_size == file_size )
                        {
                            printf("recv file success\n");
                        }
                        else 
                        {
                            printf("recv file failed \n");
                        }
                        
                        //关闭文件 
                        close( fd );
                    }

                    int main( int argc, char *argv[] )
                    {
                        //1.创建套接字 
                        int server_fd = socket( AF_INET, SOCK_STREAM, 0 );
                        if( server_fd == -1 )
                        {
                            perror("socket server error ");
                            return -1;
                        }
                        printf("server_fd = %d\n", server_fd );

                        //2.绑定 服务器的ip和端口
                        struct sockaddr_in  server_addr;
                        server_addr.sin_family = AF_INET;                   //协议族 
                        server_addr.sin_port = htons( atoi(argv[2]) );    //端口号
                        inet_aton( argv[1], &server_addr.sin_addr );     //IP地址

                        //设置端口号重用
                        int n = 1;
                        setsockopt( server_fd, SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n) );

                        int re = bind( server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr) );
                        if( re == -1 )
                        {
                            perror("bind server error ");
                            close( server_fd );
                            return -1;
                        }
                        printf("bind server success\n");

                        //3.监听 
                        re = listen( server_fd, 5 );
                        if( re == -1 )
                        {
                            perror("listen server error ");
                            close( server_fd ); 
                            return -1;
                        }
                        printf("listen server success\n");

                        //4.接受连接 
                        struct sockaddr_in  client_addr;
                        socklen_t len = sizeof(client_addr);

                        int client_fd = accept( server_fd, (struct sockaddr *)&client_addr, &len );
                        if( client_fd == -1 )
                        {
                            perror("accept error ");
                            close( server_fd );
                            return -1;
                        }
                        printf("client_fd = %d\n", client_fd );
                        printf("client_ip = %s\n", inet_ntoa(client_addr.sin_addr) );


                        //5.通信 -->接收文件 
                        recv_file( client_fd );


                        //6.关闭套接字 
                        close( server_fd );
                    }

                file_tcp_client.c   客户端 发送文件  

                    int send_file( int sock_fd, char * filename )
                    {
                        //发送文件名的长度大小 
                        int name_len = strlen(filename);
                        int re = send( sock_fd, &name_len, sizeof(name_len), 0 );
                        if( re == -1 )
                        {
                            perror("send name_len error ");
                            return -1;
                        }
                        printf("send name_len success , len = %d\n", name_len );

                        //发送文件名 
                        re = send( sock_fd, filename, name_len, 0 );
                        if( re == -1 )
                        {
                            perror("send filename error ");
                            return -1;
                        }
                        printf("send filename success , filename = %s\n", filename );

                        //发送文件的大小
                        struct stat  st;
                        stat( filename, &st );  //获取文件属性-->文件大小 
                        int file_size = st.st_size;

                        re = send( sock_fd, &file_size, sizeof(file_size), 0 );
                        if( re == -1 )
                        {
                            perror("send file_size error ");
                            return -1;
                        }
                        printf("send file_size success , file_size = %d\n", file_size );

                        /* 发送文件的内容 
                            打开文件
                            读取
                            发送 
                            关闭文件 
                        */
                        //打开文件
                        int fd = open( filename, O_RDONLY );
                        if( fd == -1 )
                        {
                            perror("open file error ");
                            return -1;
                        }

                        int read_size = 0;      //已经读取到的字节数
                        while( read_size < file_size )
                        {
                            //先读取文件内容 
                            char buf[128] = {0};
                            re = read( fd, buf, 100 );
                            if( re > 0 )
                            {
                                //发送数据 
                                send( sock_fd, buf, re, 0 );
                                read_size += re;
                            }
                            else 
                            {
                                perror("read file error ");
                                break;
                            }
                        }

                        if( read_size == file_size )
                        {
                            printf("send file success\n");
                        }
                        else 
                        {
                            printf("send file failed \n");
                        }

                        //关闭文件 
                        close( fd );

                    }


                    int main( int argc, char *argv[] )
                    {
                        //1.创建套接字 
                        int client_fd = socket( AF_INET, SOCK_STREAM, 0 );
                        if( client_fd == -1 )
                        {
                            perror("socket client error "); 
                            return -1;
                        }
                        printf("client_fd = %d\n", client_fd );

                        //2.设置服务器的ip和端口
                        struct sockaddr_in  server_addr;
                        server_addr.sin_family = AF_INET;                   //协议族 
                        server_addr.sin_port = htons( atoi(argv[2]) );    //端口号
                        inet_aton( argv[1], &server_addr.sin_addr );     //IP地址

                        //3.连接服务器 
                        int re = connect( client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr) );
                        if( re == -1 )
                        {
                            perror("connect error ");
                            close( client_fd );
                            return -1;
                        }
                        printf("connect success\n");


                        //4.通信 --> 发送文件 
                        send_file( client_fd, argv[3] );
                        

                        //5.关闭套接字
                        close( client_fd );
                    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值