socket套接字

Socket套接字
    Socket是一个编程接口(网络编程接口)。
    作用是用来实现网络上不同主机上的应用程序之间进行双向通信。
    套接字是一种特殊的文件描述符,也就意味着我们使用套接字实现网络通信可以使用write/read。
    
    要实现互联网通信,至少需要一对套接字,其中一个运行在客户端(client socket),一个运行
    服务器端(server socket).
        
    Socket可以分为三类:
        1)    流式套接字(SOCK_STRAM)
            流式套接字用于提供面向连接、可靠的数据传输服务。
            主要针对传输层协议为TCP协议的应用。
            
        2)    数据报套接字(SOCK_DGRAM)
            数据报套接字提供一种无连接的服务(并不能保证数据传输的可靠性)。
            主要针对传输层协议为UDP协议的应用。
            
        3)    原始套接字(SOCK_RAW)
            原始套接字可以直接跳过传输层读取没有处理的IP数据包。
            而流式套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。
            因此,如果要访问其他协议发送的数据必须要使用原始套接字。
            
    1.    TCP套接字的编程流程
        1)    TCP网络应用程序的数据传输的大概过程
            建立连接:--->加微信
                三次握手
                
            发送/接受网络数据:--->聊天
                write/send/sendto
                read/recv/recvfrom
            
            关闭连接:--->删除好友
                四次挥手
            
            三次握手:
                客户端                            服务器端
                你好,我希望和你建立连接
                                            好的,我已经准备好了,可以建立连接
                好的,马上和你连接
                连接成功
                                        
                为什么要有三次握手的机制?
                通信双方成功通信的前提条件是双方要能够建立连接。
                那么双方能够成功建立连接的前提条件又是什么呢?
                必须双方都能够收和发。
                所以三次握手实际上就是一个测试能不能够建立连接的过程。
                
            三次握手的具体过程:
                1)    序号:Seq(Sequence Number)
                    序号占32bits,用来标识从计算机A发送到计算机B的数据包的序号
                    计算机发送数据时需要对此进行标记。
                    
                2)    确认号(ACK:Acknowledge Number)
                    确认号也是占32bits,客户端和服务器都可以发送,Ack = Seq + 1
                    
                3)    标志位
                    每一个标志位占用一个bits,共有6个标志位。
                    分别是URG,ACK,PSH,RST,SYN,FIN,具体的含义如下:
                        ACK:确认序号有效
                        RST:重置连接
                        SYN:建立一个新连接(synchronous)
                        FIN:断开一个连接(finish)
                        
            四次挥手:
                我们建立连接是需要消耗掉一些资源的,同样的,我们在断开连接的时候
                必须去释放掉资源。
                
            情景模拟:
                客户端                                    服务器端
                我发送完了,我想断开连接
                                                ok,我知道了,稍等一下,我要准备一下
                                                过一会儿之后
                                                好的,我已经处理完了,可以断开连接了
                OK,拜拜,下次见
                成功断开连接
                
            具体的过程:
                请见图<四次挥手原理机制>
                
        2)    TCP编程的流程
            TCP Server
                socket:创建套接字
                bind:把一个套接字和网络地址绑定在一起
                    如果你想让其他人来主动连接你或者联系你,
                    你就需要绑定一个地址,并且需要把这个地址告诉要连接你的人
                listen:让套接字进行监听模式
                accept:接受客户端的连接 
                    多次调用accept就可以与不同的客户端建立连接。
                    
                write/send/sendto or read/recv/recvfrom
                
                close/shutdown:“四次挥手”
                
            TCP Client:
                socket
                bind:可绑定可不绑定
                connect:主动与TCP server建立连接--->“三次握手”
                
                write/send/sendto or read/recv/recvfrom
                
                close/shutdown:“四次挥手”
                
    2.    socket具体的API函数
        1)    socket : 创建一个套接字
            int socket(int domain,int type,int protocol);
                domain:指定域或者协议域。
                    socket接口不仅仅只局限于TCP/IP,它也可以用于Bluetooth,本地通信。。
                    那么每一种网络通信下面都有自己的许多协议,我们就把IPv4下面的所有的
                    网络协议,归到了一个域:
                        AF_INET : IPv4协议族
                        AF_INET6 :IPv6协议族
                        AF_BULETOOTH
                        AF_UNIX/AF_LOCAL:Unix域协议族
                        
                type:指定要创建的套接字的类型
                    SOCK_STRAM : 流式套接字 ---> TCP
                    SOCK_DGRAM : 数据报套接字 ---> UDP
                    SOCK_RAW : 原始套接字
                
                protocol:指定具体的应用层的协议,可以指定为0(不知名的私有应用协议)
                
                返回值:成功返回一个套接字的描述符(> 0,文件描述符)
                    失败返回-1,同时errno被设置。
                    
        2)    网络地址结构体
            socket接口它不仅仅可以用于以太网(IPv4),也可以用于IPv6,同时也可以用于bluetooth
            ,不同的协议族,他们的地址是不一样的。
            
            但是我们在所有的socket进行网络编程时,必须要指定对方的网络地址,所以
            我们需要有一个统一的标准对这些地址进行一个描述。
        
            struct sockaddr
            {
                sa_family_t sa_family;//指定协议(地址)族
                                        //sa_family_t--->unsigned short
                char sa_data[14];//包含套接字中的目标地址和端口信息
            };
            缺陷:IP地址和端口号混在一起保存在sa_data这个数组中的。
            
            struct sockaddr_in
            {
                sa_family_t sa_family;//指定协议(地址)族
                                        //sa_family_t--->unsigned short
                u_int16_t sin_port;//端口号
                struct in_addr sin_addr;//IP地址
                char sin_zero[8];//填充8个字节,无实际意义的,只是为了和其他协议族的地址
                                    //结构体大小一样
            };
            typedef uint32_t in_addr_t;
            struct in_addr
            {
                in_addr_t s_addr;
            }
            
            这个结构体将端口号和IP地址分开保存的。
            
            如:
            struct sockaddr_in local;
            local.sin_family = AF_INET;
            local.sin_addr.s_addr = "192.168.31.30";//error
            
        3)    IP地址转换函数
            IP地址是以点分十进制的形式存在的。
            
            函数将点分十进制形式存在IP地址转换为in_addr_t类型
                
            #include <sys/socket.h>
            #include <netinet/in.h>
            #include <arpa/inet.h>

            a:addr 点分十进制字符串
            n:network 网络地址
            aton:将点分十进制字符串IP地址---> struct in_addr
            
            int inet_aton(const char *cp, struct in_addr *inp);
                cp:指向要转换的点分十进制IP地址字符串
                    如:"192.168.31.30"
                inp:指向一个IP的地址结构体
                    用来保存转换后的IP地址(32bits的数)。

            in_addr_t inet_addr(const char *cp);
                inet_addr把cp指向的“点分十进制形式的IP字符串”转换成IP的网络地址结构体in_addr_t

            in_addr_t inet_network(const char *cp);
                功能与inet_addr类似

            char *inet_ntoa(struct in_addr in);
                把IP结构体struct in_addr转换成一个点分十进制的IP字符串
                
            例子:
                struct sockaddr_in local;
                local.sin_family = AF_INET;
                local.sin_addr.s_addr = "192.168.31.30";//error
                local.sin_addr.s_addr = inet_addr("192.168.31.30");
                inet_aton("192.168.31.30",&local.sin_addr);
                local.sin_port = 7878;//error
                
        4)    整数在主机字节序与网络字节序之间的转换函数
            #include <arpa/inet.h>
            
            h:host     n:network
            l:long     s:short

            uint32_t htonl(uint32_t hostlong);
                把一个32bits的整数的主机字节序转换成网络字节序

            uint16_t htons(uint16_t hostshort);
                把一个16bits的整数的主机字节序转换成网络字节序

            uint32_t ntohl(uint32_t netlong);
                把一个32bits的整数的网络字节序转换成主机字节序

            uint16_t ntohs(uint16_t netshort);
                把一个16bits的整数的网络字节序转换成主机字节序

                struct sockaddr_in local;
                local.sin_family = AF_INET;
                local.sin_addr.s_addr = "192.168.31.30";//error
                local.sin_addr.s_addr = inet_addr("192.168.31.30");
                inet_aton("192.168.31.30",&local.sin_addr);
                local.sin_port = 7878;//error        
                local.sin_port = htons(7878);//error        
            
        5)    bind:把一个IPv4的网络地址绑定到一个socket上面去
            #include <sys/types.h>          /* See NOTES */
            #include <sys/socket.h>

            int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
                sockfd:要绑定地址的套接字的描述符
                addr:通用网络地址结构的指针
                addrlen:指定第二个实参指向的地址结构体的长度
            
                返回值:成功返回0,失败返回-1
                
        6)    listen:让套接字进入监听模式
            NAME
                listen - listen for connections on a socket

            SYNOPSIS
                #include <sys/types.h>          /* See NOTES */
                #include <sys/socket.h>

                int listen(int sockfd, int backlog);
                    sockfd:要进入监听模式的套接字的描述符
                    backlog:同时能够处理连接请求的数目
                        比如:5,10.....
                    
                    返回值:成功返回0,失败返回-1,同时errno被设置。
                    
                调用成功之后,套接字将会变成"监听套接字"。
            
        7)    accept    
            #include <sys/types.h>          /* See NOTES */
            #include <sys/socket.h>

            int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
                用于TCP server接收一个来自客户端的TCP连接建立的请求。
                sockfd:一个监听套接字
                addr:网络地址结构体的指针,用来保存客户端的地址信息的。
                addrlen:网络地址结构体的长度类型的指针,
                用来保存客户端地址结构体的长度。
                返回值:成功返回与该客户端的连接套接字描述符,后续与该客户端进行数据交换
                都是通过该连接套接字描述符。失败返回-1,同时errno被设置。

        8)    connect
        #include <sys/types.h>          /* See NOTES */
        #include <sys/socket.h>

        int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
            sockfd:套接字的文件描述符
            addr:对方的地址,tcp server的地址。
            addrlen:对方的地址结构体的大小
            返回值:成功返回0,失败返回-1,同时errno被设置。
        
        9)    往套接字上发送数据
            write/send/sendto
            TCP都可以使用这三个函数,而UDP只能用sendto
            1.    write
                略
            2.    send
                #include <sys/types.h>
                #include <sys/socket.h>

                ssize_t send(int sockfd, const void *buf, size_t len, int flags);
                    sockfd:你要往哪一个套接字上发送数据
                    buf:你要发送的数据的地址
                    len:你要发送的数据的长度
                    flags:一般为0.
                    返回值:成功返回实际发送的字节数,失败返回-1,同时errno被设置。
            
            3.    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                const struct sockaddr *dest_addr, socklen_t addrlen);
                前面四个参数与send一样。
                dest_addr:指定接收方的地址,是一个网络地址结构体。
                    如果TCP协议通信,此处你可以省略。
            
                addrlen:
                    dest_addr指向的那个地址结构体的的长度
                返回值:成功返回实际发送的字节数,失败返回-1,同时errno被设置。

        10)    从套接字上接收数据
            read/read/recv/recvfrom
            1.    read
                略
                read(fd,buf,sizeof(buf))
            
            2.    recv
                ssize_t recv(int sockfd, void *buf, size_t len, int flags);
                    recv前面的三个参数与read类似。
                    flags:一般为0。
                    返回值:成功返回读取到的字节的个数,失败返回-1,同时errno被设置。
                
                ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

                    src_addr:用来保存发送发的地址

                    addrlen:用来保存发送者地址结构体信息的长度
                    
                    返回值:成功返回实际接收到的字节数,失败返回-1.同时errno被设置。
                    
        11)    关闭套接字(close/shutdown)
            #include <sys/socket.h>

            int shutdown(int sockfd, int how);
                sockfd:要关闭的套接字的文件描述符
                how:关闭方式,有以下三种:
                    SHUT_RD:关闭读
                    SHUT_WR:关闭写
                    SHUT_RDWR:关闭读写--->close(sockfd);
                返回值:
                    成功返回0,失败返回-1,同时errno被设置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值