zoukankan      html  css  js  c++  java
  • linux c 笔记 网络编程(二)

    套接字

    struct sockaddr结构定义了一种通用的套接字地址,他在linux/socket.h中定义如下
    struct sockaddr
    {
        unsigned short  sa_family;  //套接字的协议簇地址类型,TCP/IP协议对于IPv4地址类型为AF_INET
        char            sa_data[14];//存储具体的协议地址
    };

    现在一般使用下面这个sockaddr_in结构(用来设置/获取地址信息):
    struct sockaddr_in
    {
        unsigned short sin_len;      //IPv4地址长度
        short int      sin_family;   //指代协议簇,在TCP套接字编程只能是AF_INET
        unsigned short sin_port;     //存储端口号(使用网络字节顺序),数据类型是一个16为的无符号整形类型
        struct         in_addr sin_addr;//存储IP地址,IP地址是一个in_add结构体(结构在下面)
        unsigned char  sin_zero[8];     //为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节
    };
    其中,in_addr这个数据结构:
    struct sockaddr_in sock;
    unsistruct sockaddr_in mysock;
    sock.sin_family = AF_INET;                  //TCP地址结构
    sock.sin_port = htons(80);                 //设置端口号为80
    sock.sin_addr.s_addr = inet_addr("202.205.3.195");    //设置IP地址
    memset(sock.sin_zero ,0 , sizeof(sock.sin_zero));    //将数组sin_zero清零


    memset函数的原型为
    memset(void * s ,int c ,size_t n);

    一.创建套接字
    #include <sys/types.h>
    #include <sys/socket.h>
    int socket(int domain, int type, int protocol);

    domain:用于指定创建套接字所使用的协议族,在头文件<linux/socket.h>中定义。有时候程序中会使用PF_INET,在头文件中AF_INET和PF_INET的数值是一致的。
    常见的协议族如下:
        AF_UNIX:创建只在本机内进行通信的套接字。    
        AF_INET:使用IPv4TCP/IP协议
        AF_INET6:使用IPv6 TCP/IP协议
    说明:AF_UNIX只能用于单一的UNIX系统进程间通信,而AF_INET是针对Interne的,因而可以允许在远程主机之间通信。一般把它赋为AF_INET。
    type:指明套接子通信的类型,对应的参数如下
        SOCK_STREAM:创建TCP流套接字
        SOCK_DGRAM:创建UDP数据报套接字
        SOCK_RAW:创建原始套接字
        protocol:指定某个协议的特定类型
    参数protocol通常设置为0,表示通过参数domain指定的协议族和参数type指定的套接字类型来确定使用的协议。当为原始套接字时,系统无法唯一的确定协议,此时就需要使用使用该参数指定所使用的协议。

    返回值:执行成功后返回一个新创建的套接字;若有错误发生则返回一个-1,错误代码存入errno中。


    下面用代码创建一个TCP套接字
    int sock_fd;
    sock_fd = socket(AF_INET ,SOCK_STREAM ,0);
    if(sock_fd < 0) {
        perror("socket");
        exit(1);
    }
    创建UDP协议的套接字为
    sock_fd = socket(AF_INET ,SOCK_DGRAM ,0);

    二.建立连接
        函数connect用来在一个指定的套接字上创建一个连接,该函数原型
        #include <sys/types.h>
        #include <sys/socket.h>
        int connect (int socket ,const struct sockaddr *serv_addr ,socket_t addrlen);

    sock_fd:建立套接字时返回的套接字文件描述符,调用socket()返回的。
    serv_addr:是一个指向数据结构sockaddr的指针,其中包括客户端需要连接的服务器的目的IP地址和端口号。
    addrlen:表示了第二了参数的大小,可以使用sizeof(struct sockaddr)

    执行成功后返回0,有错误发生则返回-1,错误代码存入errno中。

    通常一个面对连接的套接字(如TCP套接字)只能调用一次connect函数,而面对无连接的套接字(UDP套接字)则可以多次调用connect函数以改变与目的地址的绑定
    客户端在建立套接字之后,不需要进行地址绑定,就可以直接连接服务器。连接服务器的函数为connect(),此函数连接指定参数的服务器,例如IP地址,端口号。
    如果是TCP编程,则connect()函数用于服务器发出连接请求,服务器的IP地址和端口号由 参数serv_addr指定。
    如果是UDP编程,则connect函数并不建立真正的连接,它只是告诉内核与该套接字进行通信的目的地址(由第二个参数指定),只有该目的地址发来的数据才会被该socket接收。调用connect函数的好处是不必在每次发送和接收数据时都指定目的地址。

    该函数常见的用法如下
    struct sockaddr_in  serv_addr;
    memset (&serv_addr , 0 , sizeof(struct sockaddr_in) );     //将serv_addr的各个字段清零
    serv_addr.sin_family =AF_INET;
    serv_addr.sin_port = htons(80);                //htons是字节顺序转换函数,
    // inet_aton函数将一个字符串转换为一个网络地址,并把该网络的地址赋给第二个参数
    if(inet_aton("172.17.242.131", &serv_addr.sin_addr) < 0) {
        perror("inet_ation");
        exit(1);
    }
    //使用sock_fd套接字连接到由serv_addr指定的地址上,假设sock_fd已定义
    if(connect(sock_fd, (struct sockaddr *) &serv_addr ,sizeof(struct sockaddr_in )) <0 ) {
        perror("connect");
        exit(1);
    }

    注意 serv_addr强制类型转换为struct sockaddr类型

    三.绑定套接字
    函数bind()的作用是将一个套接字文件描述符与地址和端口绑定。
    函数bind用来将一个套接字和某个端口绑定在一起,函数的原型
    #include <sys/types.h>
    #include <sys/socket.h>
    int bind(int sockfd ,struct sockaddr *my_addr ,socklen_t addrlen );

    sockfd:sockfd是调用socket函数返回的文件描述符;
    addrlen是sockaddr结构的长度。
    my_addr: 是一个指向sockaddr结构的指针,它保存着本地套接字的地址(即端口和IP地址)信息。
    返回:成功返回0,失败返回-1.

    该函数的常用方法
    struct sockaddr_in  serv_addr;
    memset (&serv_addr ,0 , sizeof(struct sockaddr_in ));
    serv_addr.sin_family = AF_INEF;
    serv_addr.sin_port = htons(80);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if( bind(sock_fd) ,(struct sockaddr *)&serv_addr ,sizeof(struct sockaddr_in ) < 0) {
        perror("bind");
        exit(1);
    }

    四.在套接字上监听
    函数listen 把套接字转化为被动监听,listen在套接字函数中表示让一个套接字处于监听到来的连接请求的状态,该函数原型
    #include <sys/socket.h>
    int listen (int s , int backlog);
    无错误,返回0,
    否则,返回SOCKET ERROR,可以调用函数WSAGetLastError取得错误代码。

    listen函数使用主动连接套接字变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。
    listen函数一般在调用bind之后-调用accept之前调用。
    该函数常见用法
    #define LISTEN_NUM  12     //定义连接请求队列长度
    ....
    if(listen (sock_fd , LISTEN_NUM) < 0) {
        perror ("listen ");
        exit(1);
    }


    五.接受连接
    函数accept用来接受一个连接请求,该函数原型
    #include <sys/types.h>
    #include <sys/socket.h>
    int accept(int s ,struct sockaddr *addr ,socklen_t *addrlen);
    参数
    s:由函数socket创建,经函数bind绑定到本地某一端口上,然后通过函数listen转化而来的监听套接字.
    addr:用来保存发起连接请求的主机的地址和端口
    addrlen:是addr所指向的结构体的大小

    执行成功返回一个新的代表客户端的套接字,出错则返回-1,错误代码存入errno中,详细带错误代码手册参考man手册

    返回值:accept()函数的返回值是新连接的客户端套接字文件描述符,与客户端之间的通信是通过accept()返回的新套接字文件描述符来进行的,而不是通过建立套接字时的文件描述符。如果accept()函数发生错误,accept()会返回-1,通过errno可以得到错误值。

    如果参数sock_fd所指定的套接字被设置为阻塞方式(Linux下的默认方式),且连接请求队列为空,则accept()将被阻塞直到有连接请求到此为止;如果参数s所指定的套接字被设置为非阻塞方式,如果队列为空,accept将立即返回-1,errno被设置为EAGAIN.

    套接字为阻塞方式下该函数的常见方法
    int client_fd;
    int client_len;
    struct sockaddr_in  client_addr;
    ...
    client_len = sizeof(struct sockaddr_in);
    client_fd = accept(sock_fd , (struct sockaddr *)&client_addr , &client_len );
    if (conn_fd < 0) {
        perror("accpt");
        exit(1);
    }



  • 相关阅读:
    前端脚手架的那些事儿
    CSS重置默认样式reset.css代码模板
    Web 3.0 前瞻:基于区块链的下一代浏览器
    关键词定位是网站推广的基础
    6年架构师针对web前端小白,作出的职业规划建议
    和程序员约会的优点和缺点
    如何在软件开发中避免出现漏洞
    Linux下修改时区
    前端需要掌握的Nginx知识
    Nginx入门指南
  • 原文地址:https://www.cnblogs.com/kaylee-lr/p/4700544.html
Copyright © 2011-2022 走看看