美文网首页程序员
socket 编程(一)——基本概念

socket 编程(一)——基本概念

作者: geverway | 来源:发表于2017-04-19 20:46 被阅读229次

    在类 Unix 的操作系统中,I/O 操作都是通过读写文件描述符(file descriptor)来进行的。文件描述符是类 Unix 系统中的一个重要概念。socket 操作的是网络 I/O,所以也沿用这种设计思路。socket 有对应的套接字描述符 sockfd。很多用于操作文件描述符 fd 的操作都可以用于操作 sockfd。就如常用的文件操作 write()/read() 就可以用于 sockfd,来实现数据的发送和接收。

    在接着介绍 socket 之前,先跳出来了解一下文件描述符

    文件描述符(File descriptor,后文用 fd 替代),是一个非负整数。当进程打开或者创建一个文件时,系统内核就会向进程返回一个 fd 用来指代这个文件。对文件读写时,就将 fd 作为参数传递给 write() / read() 函数。

    在 Unix shell 中,文件描述符0与进程标准输入相关联,文件描述符1与标准输出相关联,文件描述符2与标准错误相关联。

    下面这张图描述了,进程、文件描述符以及文件间的关系:

    file-descriptor.png

    继续介绍 socket

    socket 地址

    /*==================================
     * socket 的地址结构,作为 bind(), 
     * connect(),accept() 的参数
     *================================*/
    struct sockaddr {
       unsigned short   sa_family;
       char             sa_data[14];
    };
    
    /*==================================
     * sockaddr_in 是 IPv4 因特网地址,
     * 是具体的 sockaddr。使用时需要强制
     * 转换成 sockaddr
     * sin_family = AF_INET
     *================================*/
    struct sockaddr_in {
       short int            sin_family;
       unsigned short int   sin_port;
       struct in_addr       sin_addr;
       unsigned char        sin_zero[8];
    };
    
    /*==================================
     * 4字节的 IPv4 地址
     *================================*/
    struct in_addr {
       unsigned long s_addr;
    };
    

    这些地址是用于 socket 函数的。sockaddr_in 是专用于 IPv4 通信的。IPv6 的地址结构,这里未列出

    socket 的地址分为 4类:

    • AF_INET, IPv4 因特网域, 用于不同 pc 通过网络来通信
    • AF_INET6, IPv6 因特网域
    • AF_UNIX, (AF_LOCAL)UNIX 域, 用于同一台 pc 下不同进程间通信
    • AF_UNSPEC, 未指定

    可以看出 socket 不仅可以使用 ip 地址作为 sockaddr,还能使用其他协议族的地址。这正和 socket 的设计目标一致:同样的接口既可以用于计算机间通信还能用于计算机内通信

    socket 函数

    #include <sys/socket.h>
    
    //建立连接
    int socket(int domain, int type, int protocal);
    int bind(int sockfd, const struct sockaddr *addr, socklent_t len);
    int connect(int sockfd, const struct sockaddr *addr, socklen_t lent);
    int listen(int sockfd, int backlog);
    int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
    
    //数据传输
    ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);
    ssize_t sendto(int sockfd, const void *buf, size_t nbytes, int flags, const struct      sockaddr *destaddr, socklen_t destlen);
    ssize_t sendmsg(int socklen, const struct msghdr *msg, int flags);
    ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
    ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, struct      sockaddr *restrict addr, socklen_t *restrict addrlen);
    ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
    
    //关闭连接
    #include <unistd.h>
    int close(int sockfd);
    #include <sys/socket.h>
    int shutdown(int sockfd, int how);
    
    

    note:

    1、strict 关键字, 用于告知编译器, strict 修饰的指针所指向的内容,只能通过这个指针修改。没有其他修改途径,有利于后面的代码优化

    上面将 socket 相关函数分为了三类: 建立连接、传输数据、关闭连接。这么分的目的是为了和 TCP 传输的过程相对应。尽管 socket 并不仅仅用于 tcp 传输,这里为了和我们熟知的网络知识关联起来,就以 tcp 连接为例来介绍 socket

    对于一个 c/s 模型的服务来说,client 和 server 间的通信可以简化以下两个部分:

    client

    • sockfd = socket( AF_INET, SOCK_STREAM, 0 )
    • connect( sockfd, &serv_addr, sizeof(serv_addr) )
    • wirte( sockfd, buffer, strlen(buffer) );

    客户端创建一个 socket ,返回一个套接字描述符 sockfd。接着,connect() 函数,向服务器发起连接。这个步骤可以看成,client 和 server 进行 tcp 三次握手。

    server

    • sockfd = socket( AF_INET, SOCK_STREAM, 0 )
    • bind(sockfd, &serv_addr, sizeof(serv_addr) )
    • listen(sockfd, 1024);
    • newsockfd = accept(sockfd, &cli_addr, sizeof(cli_addr) );

    服务端创建一个 socket,返回一个套接字描述符 sockfd。bind( ) 将服务器的地址和其中一个端口绑定到 sockfd 上。调用 listen( ) ,开始在绑定的端口上监听来自客户端的连接。当有新的客户端连接到来时,调用 accept( ) 创建一个新的套接字描述符 newsockfd , 来处理这个新连接。之前的 sockfd 继续监听是否有新连接。

    这个过程,可以与下面这张图对应起来

    socket_client_server.jpg

    在前文中,介绍了最为典型的 socket 类型 Stream Sockets。stream socket 主要用于 tcp 传输

    除此之外,socket 还有其他 3种类型

    • Data gram Sockets ,用于 udp 传输
    • Raw Sockets ,用来访问底层协议,主要用来开发新的协议
    • Sequenced Packet Sockets,和 stream sockets 相似,但是它保留了边界

    参考

    [1] Unix Socket Tutorial

    [2] UNIX高级环境编程(2)FIle I/O - 原子操作、共享文件描述符和I/O控制函数

    相关文章

      网友评论

        本文标题:socket 编程(一)——基本概念

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