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);