网络编程 -------- 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 );
                    }
相关推荐
杨德杰28 分钟前
QT网络(一):主机信息查询
网络·qt
007php0071 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
yang_shengy1 小时前
【JavaEE】网络(6)
服务器·网络·http·https
zquwei2 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring
Aimin20222 小时前
路由器做WPAD、VPN、透明代理中之间一个
网络
群联云防护小杜3 小时前
如何给负载均衡平台做好安全防御
运维·服务器·网络·网络协议·安全·负载均衡
爱码小白3 小时前
网络编程(王铭东老师)笔记
服务器·网络·笔记
蜜獾云3 小时前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
柒烨带你飞3 小时前
路由器转发数据报的封装过程
网络·智能路由器
东方隐侠安全团队-千里4 小时前
网安瞭望台第17期:Rockstar 2FA 故障催生 FlowerStorm 钓鱼即服务扩张现象剖析
网络·chrome·web安全