• 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 ***
    ");
            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***
    ");
            goto exit;
        }
        
        
    //建立Socket连接
        ret= connect(sock_fd,(struct sockaddr*)ip4_svr_addr,sizeof(struct sockaddr));
        printf("connect ret:%d,errno:%u
    ",ret,errno);
        if(ret==-1 &&errno!=EINPROGRESS)
        {
            printf("***connect fail***
    ");
            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
    ",ret);
        if(ret<=0)
        {
            printf("***select timeout or error***
    ");
            goto exit;
        }
        if(!FD_ISSET(sock_fd,&read_fds)&&!FD_ISSET(sock_fd,&write_fds))
        {
            printf("***connect fail***
    ");
            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
    ");
            }
            else
            {
                printf("***connect fail,sock_err= %d,errno= %u***
    ",sock_error,errno);
                goto exit;
            }
        }
        else if(!FD_ISSET(sock_fd,&read_fds)&&FD_ISSET(sock_fd,&write_fds))
        {
            printf("connect success
    ");
        }
        else if(FD_ISSET(sock_fd,&read_fds) && !FD_ISSET(sock_fd,&write_fds))
        {
            printf("***connect fail***
    ");
            goto exit;
        }
        else
        {
            printf("***connect fail***
    ");
            goto exit;
        }
    
    //发送数据
        ret= send(sock_fd,(const void*)TCP_CLIENT_SEND_STR,strlen(TCP_CLIENT_SEND_STR),0);
        if(ret< 0)
        {
            printf("***send fail ***
    ");
            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
    ",ret);
        if(ret<=0)
        {
            printf("***selecttimeoutorerror***
    ");
            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
    ",ret,recv_buf);
            }
            else if(ret==0)
            {
                printf("***peerclosed***
    ");
                gotoexit;
            }
            else
            {
                if(!(errno==EINTR|| errno==EWOULDBLOCK|| errno==EAGAIN))
                {
                    printf("***erroroccurs***
    ");
                    gotoexit;
                }
                else
                {
                    printf("waitfora while
    ");
                    ql_rtos_task_sleep_ms(20);
                    goto_recv_;
                }
             }
        }
    //关闭Socket连接
        close(sock_fd);
  • 相关阅读:
    kafka 幂等生产者及事务(kafka0.11之后版本新特性)
    git 忽略 .idea文件
    Java Scala 混合编程导致 编译失败 ,【找不到符号】问题解决
    Starting sshd: /var/empty/sshd must be owned by root and not group or world-writable.
    Spark RDD持久化、广播变量和累加器
    PSQLException: FATAL: no pg_hba.conf entry for host "127.0.0.1", user "ambari", database "ambari", SSL off
    PostgreSQL:Java使用CopyManager实现客户端文件COPY导入
    ThreadLocal的使用及原理分析
    gradlew和gradle的区别
    Managing Large State in Apache Flink®: An Intro to Incremental Checkpointing
  • 原文地址:https://www.cnblogs.com/Jlord/p/14550487.html
Copyright © 2020-2023  润新知