// // Created by YY on 2019/9/29. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include "net.h" #include "../jni_log.h" #define DEBUG(fmt, args...) LOGD(" <%s>: " fmt, __func__, ##args) #define PARSE_BUFF_SIZE 4096 using namespace std; CTcpPort::CTcpPort() { m_iServiceType = TYPE_CLIENT; m_sIp.clear(); m_nPort = 0; m_connected = false; m_pClientSocket = 0; m_pServerSocket = 0; event_func = NULL; event_func_context = NULL; receive_data_func = NULL; receive_data_func_context = NULL; } CTcpPort::~CTcpPort() { CloseTcpPort(); } bool CTcpPort::IsOpen(void) { if (m_iServiceType == TYPE_SERVER) { if (m_pServerSocket > 0) { return true; } else { return false; } } else { if (m_pClientSocket > 0) { return true; } else { return false; } } } bool CTcpPort::OpenTcpPort(const char *ip, int port) { if (IsOpen()) { return true; } m_sIp = ip; m_nPort = port; pthread_t platform_pid; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached pthread_create(&platform_pid, &attr, TcpConnectThread, this); return true; } /************************************************************* host_name:domain name net_addr:return numbers-and-dots notation */ int CTcpPort::GetHostIP(const char *host_name, char *net_addr) { struct hostent *hptr; char **pptr; char str[32]; bool found_first = false; if ((hptr = gethostbyname(host_name)) == NULL) { DEBUG("gethostbyname error\n"); return -1; } DEBUG("official hostname: %s\n", hptr->h_name); for (pptr = hptr->h_aliases; *pptr != NULL; pptr++) { DEBUG("alias:%s\n", *pptr); } switch(hptr->h_addrtype) { case AF_INET: { for (pptr = hptr->h_addr_list; *pptr != NULL; pptr++) { DEBUG("addrsss:%s\n", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str))); if (!found_first) { strcpy(net_addr, str); found_first = true; } } break; } case AF_INET6: default: DEBUG("unknown address type\n"); break; } if (found_first == true) { return 0; } return -2; } bool CTcpPort::is_domain_name(const char *ip) { regex pattern0("^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$"); string target(ip); return !regex_match(target, pattern0); } int CTcpPort::socket_set_keepalive(int fd) { int alive, idle, cnt, intv; // int value, value_len; // getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &value_len); // DEBUG("keepalive 0 %d", value); // value = 128; // setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)); // DEBUG("keepalive 1 %d", value); // getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &value_len); // DEBUG("keepalive 1 %d", value); /* Set: use keepalive on fd, default 0 */ alive = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &alive, sizeof(alive)) != 0) { DEBUG("TCP Set keepalive error"); return -1; } /* 20 Seconds not data, send keeplive packet, default 7200 */ idle = 20; if (setsockopt (fd, SOL_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)) != 0) { DEBUG("TCP Set keepalive idle error"); return -1; } /* If not recv respond, After 5 seconds retry, default 75 */ intv = 5; if (setsockopt (fd, SOL_TCP, TCP_KEEPINTVL, &intv, sizeof(intv)) != 0) { DEBUG("TCP Set keepalive intv error"); return -1; } /* If try 9 times and fail, we consider the tcp is disconnected, default 9 */ cnt = 9; if (setsockopt (fd, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)) != 0) { DEBUG("TCP Set keepalive cnt error"); return -1; } /* int timeout = 10000; // 10秒 if (setsockopt (fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout)) != 0) { DEBUG("TCP Set keepalive timeout error"); return -1; }*/ DEBUG("TCP Set keepalive OK"); return 0; } int CTcpPort::tcp_connect(char *ip, uint16_t port) { struct sockaddr_in server_sockaddr; int soc; int ret, fds_ret; struct timeval tv; fd_set rdfds; long arg; int error_value; socklen_t error_value_len; DEBUG("tcp_connect... %s : %d", ip, port); error_value_len = sizeof( error_value ); if((soc = socket(PF_INET, SOCK_STREAM, 0)) == -1) { DEBUG("%s: socket", __func__); return -1; } server_sockaddr.sin_family = AF_INET; server_sockaddr.sin_addr.s_addr = inet_addr(ip);//htonl(ip);//inet_addr("192.168.1.37"); server_sockaddr.sin_port = htons(port); bzero(&server_sockaddr.sin_zero, 8); // Set non-blocking if( (arg = fcntl(soc, F_GETFL, NULL)) < 0) { DEBUG("%s: fcntl( F_GETFL ) error", __func__); goto TCP_CONNECT_1; } if( fcntl(soc, F_SETFL, arg | O_NONBLOCK) < 0) { DEBUG( "%s: fcntl( F_SETFL ) error", __func__); goto TCP_CONNECT_1; } ret = connect(soc, (struct sockaddr*)&server_sockaddr, sizeof(struct sockaddr)); if(ret < 0) { if(errno != EINPROGRESS) //It usually return this error! { /* * connect error: Connection refused * * perror( "connect error" ); */ goto TCP_CONNECT_1; } } else if(ret == 0) { goto TCP_CONNECT_0; } tv.tv_sec = 10; //Connect Timeout tv.tv_usec = 0; FD_ZERO(&rdfds); FD_SET(soc, &rdfds); fds_ret = select(soc + 1, NULL, &rdfds, NULL, &tv); if(fds_ret < 0) { DEBUG( "%s: TCP select error", __func__); goto TCP_CONNECT_1; } else if(fds_ret == 0) { DEBUG("%s: TCP Connect Timeout %ld", __func__, tv.tv_sec); goto TCP_CONNECT_1; } else if(FD_ISSET(soc, &rdfds)) { if(getsockopt(soc, SOL_SOCKET, SO_ERROR, &error_value, &error_value_len) < 0) { goto TCP_CONNECT_1; } } else { DEBUG("%s: some error occur in tcp_connect()", __func__); goto TCP_CONNECT_1; } TCP_CONNECT_0: // if ( fcntl( soc, F_SETFL, arg ) < 0 ) // { // perror( "fcntl( F_SETFL ) error" ); // goto TCP_CONNECT_1; // } if ( error_value != 0 ) { /* * Error: Connection refused * * fprintf( stderr, "Error: %s", strerror( error_value ) ); */ goto TCP_CONNECT_1; } if(socket_set_keepalive(soc) != 0) { goto TCP_CONNECT_1; } arg &= ~O_NONBLOCK; if( fcntl(soc, F_SETFL, arg) < 0) { DEBUG( "%s: fcntl( F_SETFL ) error", __func__); goto TCP_CONNECT_1; } DEBUG("%s: tcp connected %s: %d", __func__, ip, port); return( soc ); TCP_CONNECT_1: close( soc ); return( -1 ); } void *CTcpPort::TcpConnectThread(void *p) { CTcpPort *pCTcpPort = (CTcpPort *)p; char net_addr[32] = {0}; if (pCTcpPort->is_domain_name(pCTcpPort->m_sIp.c_str())) { if (pCTcpPort->GetHostIP(pCTcpPort->m_sIp.c_str(), net_addr) != 0) { goto CONNECT_END; } } else { strcpy(net_addr, pCTcpPort->m_sIp.c_str()); } pCTcpPort->m_pClientSocket = pCTcpPort->tcp_connect(net_addr, pCTcpPort->m_nPort); if (pCTcpPort->m_pClientSocket > 0) { if (pCTcpPort->event_func != NULL) { pCTcpPort->event_func(0, pCTcpPort, pCTcpPort->event_func_context); } pthread_t platform_pid; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//detached pthread_create(&platform_pid, &attr, TcpListenThread, p); } else { if (pCTcpPort->event_func != NULL) { pCTcpPort->event_func(-1, pCTcpPort, pCTcpPort->event_func_context); } } CONNECT_END: pthread_exit(NULL); } void *CTcpPort::TcpListenThread(void *p) { int fds_ret; struct timeval tv; fd_set rdfds; fd_set exfds; uint8_t RxBuf[PARSE_BUFF_SIZE]; CTcpPort *pCTcpPort = (CTcpPort *)p; while (pCTcpPort->m_pClientSocket > 0) { tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&rdfds); //clean FD_SET(pCTcpPort->m_pClientSocket, &rdfds); //set fds_ret = select(pCTcpPort->m_pClientSocket + 1, &rdfds, NULL, NULL, &tv); if (fds_ret < 0) { break; } else if(fds_ret == 0) { //Occur failure(such as line disconnect) } else if(FD_ISSET(pCTcpPort->m_pClientSocket, &rdfds)) { int recvLen = recv(pCTcpPort->m_pClientSocket, RxBuf, sizeof(RxBuf), 0); if (recvLen == 0) { LOGW("tcp error TCP disconnected 0 errno = %d", errno); break; } else if (recvLen < 0) { if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) { } else { LOGW("tcp error TCP disconnected errno = %d", errno);; break; } } if (recvLen > 0) { if (pCTcpPort->receive_data_func != NULL) { pCTcpPort->receive_data_func(RxBuf, recvLen, pCTcpPort, pCTcpPort->receive_data_func_context); } } } } pCTcpPort->CloseTcpPort(); if (pCTcpPort->event_func) { pCTcpPort->event_func(-1, pCTcpPort, pCTcpPort->event_func_context); } pthread_exit(NULL); } void CTcpPort::set_event_callback(void (*callback)(int, void *, void *), void *context) { event_func = callback; event_func_context = context; } void CTcpPort::set_data_callback(void (*callback)(void *, int, void *, void *), void *context) { receive_data_func = callback; receive_data_func_context = context; } int CTcpPort::WriteTCP(const uint8_t * buf, uint32_t len) { int ret = 0; int fds_ret; struct timeval tv; fd_set rdfds; if (m_pClientSocket <= 0) return -2; /******************************************************* TCP send with nonblock, if occur failure(such as line disconnect...), will send timeout, so terminate TCP. */ tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&rdfds); //clean FD_SET(m_pClientSocket, &rdfds); //set fds_ret = select(m_pClientSocket + 1, NULL, &rdfds, NULL, &tv); if (fds_ret < 0) { DEBUG("tcp error send select error"); return -1; } else if(fds_ret == 0) { DEBUG("tcp error Occur failure(such as line disconnect)"); //Occur failure(such as line disconnect) ret = -1; } else if(FD_ISSET(m_pClientSocket, &rdfds)) { ret = send(m_pClientSocket, buf, len, 0); if(ret == -1) { DEBUG("tcp error TCP Send Error"); } } else { DEBUG("tcp error tcp send has error\n"); } return ret; } bool CTcpPort::CloseTcpPort(void) { DEBUG("DisconnectTCP fd = %d", m_pClientSocket); if (m_pClientSocket > 0) { shutdown(m_pClientSocket, SHUT_RDWR); close(m_pClientSocket); m_pClientSocket = 0; return true; } return false; }