socket/select的使用(代码示例)

//创建socket
    /*
    **函数原型:
    **        int socket(int domain,int type, int protocol)
    **参数说明:
    **第一个参数:
    **        AF_INET IPv4网络通信
    **        AF_INET6 IPv6网络通信
    **        AF_PACKET     链路层通信
    **        AF_UNIX, AF_LOCAL 本地通信
    **第二个参数:
    **        SOCK_STREAM        字节流套接字(提供面向连接的稳定数据传输,即TCP协议→TCP 协议会控制你的数据按照顺序到达并且没有错误。)
    **                        理解:在所有数据传送前必须使用connect()来建立连接状态。
    **                        有一下几个特征:
    **                                ①:数据在传输过程中不会消失;
    **                                ②:数据是按照顺序传输的;
    **                                ③:数据的发送和接收不是同步的
    **        SOCK_DGRAM        数据报套接字(使用不连续不可靠的数据包连接)
    **                        理解:计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。
    **                        有以下特征:
    **                                ①:强调快速传输而非传输顺序;
    **                                ②:传输的数据可能丢失也可能损毁;    
    **                                ③:限制每次传输的数据大小;
    **                                ④:数据的发送和接收是同步的(也称“存在数据边界”)。
    **        SOCK_SEQPACKET    有序分组套接字(提供连续可靠的数据包连接。)
    **        SOCK_RAW        原始套接字(提供可靠的数据包连接。)
    **        SOCK_PACKET        与网络驱动程序直接通信。
    **第三个参数:
    **        可设置为0,表示选择当前family和type组合下protocol的系统默认值
    **        常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。
    **返回值:
    **        非负:成功,-1 :出错
    */
    ret= socket(AF_INET,SOCK_STREAM,0);
    if(ret< 0)
    {
        printf("***socket create fail ***\r\n");
        goto exit;
    }
    sock_fd= ret;

//设置Socket为阻塞/非阻塞模式
    /*
    **函数原型:
    **        int ioctl(int fd, ind cmd, …);
    **函数功能:
    **        ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。
    **返回值:    
    **        成功 0;失败-1
    */
    ioctl(sock_fd,FIONBIO,&sock_nbio);

//绑定本地网口(备注:该步骤必须执行,否则Socket无法成功建立连接)
    /*
    **函数原型:    
    **        int bind(int sockfd,const struct sockaddr * addr,socklen_t addrlen);
    **用法:
    **        主要永远服务器端;服务器的网络地址和端口号通常固定不变,客户端得知服务器的地址和端口号以后,可以主动向服务器请求连接。因此服务器需要调用bind()绑定地址。
    **参数说明:
    **第一个参数:
    **        sockfd表示socket文件的文件描述符,一般为socket函数的返回值;
    **第二个参数:
    **        addr表示服务器的通信地址,本质为struct sockaddr 结构体类型指针,struct sockaddr结构体定义如下
    **                                        struct sockaddr{
    **                                            sa_family_t sa_family;
    **                                            char    sa_data[14];
    **                                        };
    **第三个参数:
    **        addrlen表示参数addr的长度;addr参数可以接受多种类型的结构体,而这些结构体的长度各不相同,因此需要使用addrlen参数额外指定结构体长度;
    */
    ip4_local_addr.sin_family= AF_INET;
    ip4_local_addr.sin_port= htons(ql_soc_generate_port());
    ip4_local_addr.sin_addr= ip4_addr;//ip4_addr为获取到的拨号数据信息,可以通过ql_get_data_call_info()函数获取后赋给此变量
    ret= bind(sock_fd,(struct sockaddr*)&j,sizeof(ip4_local_addr));
    if(ret< 0)
    {
        printf("***bind fail***\r\n");
        goto exit;
    }
    
    
//建立Socket连接
    ret= connect(sock_fd,(struct sockaddr*)ip4_svr_addr,sizeof(struct sockaddr));
    printf("connect ret:%d,errno:%u\r\n",ret,errno);
    if(ret==-1 &&errno!=EINPROGRESS)
    {
        printf("***connect fail***\r\n");
        goto exit;
    }


//监听是否收到服务器建立连接的回应
    t.tv_sec= TCP_CONNECT_TIMEOUT_S;
    t.tv_usec= 0;
    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);
    //FD_SET:将sock_fd加入read_fds集合
    FD_SET(sock_fd,&read_fds);
    //FD_SET:将sock_fd加入write_fds集合
    FD_SET(sock_fd,&write_fds);
    /*
    **函数原型:
    **        int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
    **功能说明:
    **        使用select函数就可以实现非阻塞编程。 
    **        select函数是一个轮循函数,循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。(官方解释)
    **参数说明:
    **第一个参数:
    **        需要监视的文件描述符集中最大的文件描述符 + 1;
    **第二个参数:
    **        表示:如果在这个文件集中有一个文件可读,则返回一个大于0的值
    **最后一个参数:
    **        填NULL为阻塞,填0为非阻塞,其他为一段超时时间
    **个人理解:
    **        在超时这段时间里面,进行轮询,轮询到了就执行??(怎么执行的不知道,但是应该会有相应的动作),否则就退出
    **返回值:
    **        做好准备的文件描述符的个数,超时为0,错误为 -1.
    */
    ret= select(sock_fd+ 1, &read_fds,&write_fds,NULL,&t);
    printf("select ret:%d\r\n",ret);
    if(ret<=0)
    {
        printf("***select timeout or error***\r\n");
        goto exit;
    }
    if(!FD_ISSET(sock_fd,&read_fds)&&!FD_ISSET(sock_fd,&write_fds))
    {
        printf("***connect fail***\r\n");
        goto exit;
    }
    else if(FD_ISSET(sock_fd,&read_fds)&&FD_ISSET(sock_fd,&write_fds))//FD_ISSET:检测sock_fd是否在read_fds集合中,不在则返回0
    {
        optlen= sizeof(sock_error);
        ret= getsockopt(sock_fd,SOL_SOCKET,SO_ERROR,&sock_error,&optlen);
        if(ret==0 &&sock_error==0)
        {
            printf("connect success\r\n");
        }
        else
        {
            printf("***connect fail,sock_err= %d,errno= %u***\r\n",sock_error,errno);
            goto exit;
        }
    }
    else if(!FD_ISSET(sock_fd,&read_fds)&&FD_ISSET(sock_fd,&write_fds))
    {
        printf("connect success\r\n");
    }
    else if(FD_ISSET(sock_fd,&read_fds) && !FD_ISSET(sock_fd,&write_fds))
    {
        printf("***connect fail***\r\n");
        goto exit;
    }
    else
    {
        printf("***connect fail***\r\n");
        goto exit;
    }

//发送数据
    ret= send(sock_fd,(const void*)TCP_CLIENT_SEND_STR,strlen(TCP_CLIENT_SEND_STR),0);
    if(ret< 0)
    {
        printf("***send fail ***\r\n");
        goto exit;
    }

//接收服务器发送的数据
    t.tv_sec= TCP_RECV_TIMEOUT_S;
    t.tv_usec= 0;
    FD_ZERO(&read_fds);
    FD_SET(sock_fd,&read_fds);
    ret= select(sock_fd+ 1, &read_fds,NULL,NULL,&t);
    printf("selectret:%d\r\n",ret);
    if(ret<=0)
    {
        printf("***selecttimeoutorerror***\r\n");
        gotoexit;
    }
    if(FD_ISSET(sock_fd,&read_fds))
    {
        ret= recv(sock_fd,recv_buf,sizeof(recv_buf),0);
        if(ret> 0)
        {
            printf("recvdata:[%d]%s\r\n",ret,recv_buf);
        }
        else if(ret==0)
        {
            printf("***peerclosed***\r\n");
            gotoexit;
        }
        else
        {
            if(!(errno==EINTR|| errno==EWOULDBLOCK|| errno==EAGAIN))
            {
                printf("***erroroccurs***\r\n");
                gotoexit;
            }
            else
            {
                printf("waitfora while\r\n");
                ql_rtos_task_sleep_ms(20);
                goto_recv_;
            }
         }
    }
//关闭Socket连接
    close(sock_fd);

 

posted @ 2021-03-17 17:08  怎因一双媚眼惹尘埃  阅读(671)  评论(0编辑  收藏  举报