//创建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);