//
|
// Created by YY on 2019/9/29.
|
//
|
|
#include <netdb.h>
|
#include <cstring>
|
#include <netinet/tcp.h>
|
#include <unistd.h>
|
#include <netinet/in.h>
|
#include <cstdio>
|
#include <cerrno>
|
#include <sys/select.h>
|
#include <arpa/inet.h>
|
#include <regex>
|
#include <pthread.h>
|
|
#include "net.h"
|
#include "../jni_log.h"
|
|
#define DEBUG(fmt, args...) LOGD("<net> <%s>: " fmt, __func__, ##args)
|
|
using namespace std;
|
|
/*************************************************************
|
host_name:domain name
|
net_addr:return numbers-and-dots notation
|
*/
|
int 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;
|
}
|
|
static bool 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);
|
}
|
|
static int 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;
|
}
|
|
static int 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 );
|
}
|
|
static int udp_connect(const char *ip, uint16_t port) {
|
struct sockaddr_in server_sockaddr;
|
int soc;
|
int opt;
|
int arg;
|
|
if ((soc = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
|
return -1;
|
}
|
|
/*Enable send broadcast packet*/
|
opt = 1;
|
if (setsockopt(soc, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0) {
|
perror("setsockopt");
|
close(soc);
|
return -1;
|
}
|
|
// Set non-blocking
|
if( (arg = fcntl(soc, F_GETFL, NULL)) < 0 ) {
|
close(soc);
|
return -1;
|
}
|
|
if ( fcntl(soc, F_SETFL, arg | O_NONBLOCK) < 0 ) {
|
close(soc);
|
return -1;
|
}
|
|
server_sockaddr.sin_family = AF_INET;
|
server_sockaddr.sin_addr.s_addr = inet_addr(ip);//htonl(INADDR_ANY);
|
server_sockaddr.sin_port = htons(port);
|
bzero(&server_sockaddr.sin_zero, 8);
|
|
return soc;
|
}
|
|
int WriteTCP(int fd, const uint8_t * buf, uint32_t len)
|
{
|
int ret = 0;
|
int fds_ret;
|
struct timeval tv;
|
fd_set rdfds;
|
|
if (fd < 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(fd, &rdfds); //set
|
|
fds_ret = select(fd + 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(fd, &rdfds)) {
|
ret = send(fd, buf, len, 0);
|
if(ret == -1) {
|
DEBUG("tcp error TCP Send Error");
|
}
|
} else {
|
DEBUG("tcp error tcp send has error\n");
|
}
|
|
return ret;
|
}
|
|
int ReadTCP(int fd, uint8_t * buf, uint32_t len)
|
{
|
if (fd < 0) {
|
return -2;
|
}
|
|
int recvLen = recv(fd, buf, len, 0); //read socket data from server
|
|
if (recvLen == 0) {
|
LOGW("tcp error TCP disconnected 0 errno = %d", errno);
|
return -1;
|
} else if (recvLen < 0) {
|
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
|
return 0;
|
} else {
|
LOGW("tcp error TCP disconnected errno = %d", errno);;
|
return -1;
|
}
|
}
|
|
return recvLen;
|
}
|
|
int ConnectTCP(const char *ip, uint16_t port)
|
{
|
char net_addr[32] = {0};
|
|
if (is_domain_name(ip)) {
|
if (GetHostIP(ip, net_addr) != 0) {
|
return -3;
|
}
|
} else {
|
strcpy(net_addr, ip);
|
}
|
|
return tcp_connect(net_addr, port);
|
}
|
|
void DisconnectTCP(int fd)
|
{
|
DEBUG("DisconnectTCP fd = %d", fd);
|
if (fd >= 0) {
|
shutdown(fd, SHUT_RDWR);
|
close(fd);
|
}
|
}
|
|
int EstablishUDP(const char *ip, uint16_t port)
|
{
|
char net_addr[32] = {0};
|
|
if (is_domain_name(ip)) {
|
if (GetHostIP(ip, net_addr) != 0) {
|
return -3;
|
}
|
} else {
|
strcpy(net_addr, ip);
|
}
|
|
return udp_connect(net_addr, port);
|
}
|
|
void RemoveUDP(int fd)
|
{
|
close(fd);
|
}
|
|
int WriteUDP(int fd, char *ip, uint16_t port, const uint8_t * buf, uint32_t len)
|
{
|
int ret = 0;
|
int fds_ret;
|
struct timeval tv;
|
fd_set rdfds;
|
|
if (fd < 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(fd, &rdfds); //set
|
|
fds_ret = select(fd + 1, NULL, &rdfds, NULL, &tv);
|
|
if (fds_ret < 0)
|
{
|
DEBUG("UDP send select error");
|
return -1;
|
}
|
else if(fds_ret == 0)
|
{
|
DEBUG("Occur failure(such as line disconnect)");
|
ret = -1;
|
}
|
else if(FD_ISSET(fd, &rdfds))
|
{
|
struct sockaddr_in server_sockaddr;
|
|
server_sockaddr.sin_family = AF_INET;
|
server_sockaddr.sin_addr.s_addr = inet_addr(ip);//htonl(INADDR_ANY);
|
server_sockaddr.sin_port = htons(port);
|
bzero(&server_sockaddr.sin_zero, 8);
|
|
ret = sendto(fd, buf, len, 0, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr_in));
|
|
if(ret == -1)
|
{
|
DEBUG("UDP Send Error");
|
}
|
}
|
else
|
{
|
DEBUG("UDP send has error\n");
|
}
|
|
return ret;
|
}
|
|
int ReadUDP(int fd, uint8_t * buf, uint32_t len)
|
{
|
struct sockaddr_in from_addr;
|
socklen_t from_len;
|
|
int recvLen = recvfrom(fd, buf, len, 0,
|
(struct sockaddr *) &from_addr,
|
&from_len);//read socket data from server//
|
|
if (recvLen == 0) {
|
return 0;
|
} else if (recvLen < 0) {
|
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN) {
|
return 0;
|
} else {
|
DEBUG("UDP ERROR!! = %d", errno);
|
return -1;
|
}
|
}
|
|
return recvLen;
|
}
|