/* * Copyright (c) 2008-2016 Allwinner Technology Co. Ltd. * All rights reserved. * * File : CdxSocketUtil.c * Description : SocketUtil * History : * */ #include #include #include #include #include #include #include #include #include #include #define CONFIG_KEEP_ALIVE (1) #if (CONFIG_CMCC == OPTION_CMCC_YES) #define TCP_KEEP_IDLE_SECS (1) // cmcc do not need 1 min #else #define TCP_KEEP_IDLE_SECS (10) // 如该连接在60秒内没有任何数据往来,则进行探测 #endif #define TCP_KEEP_INTERVAL_SECS (5) // 探测时发包的时间间隔为5 秒 #define TCP_KEEP_COUNT (3) // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发. cdx_err CdxMakeSocketBlocking(cdx_int32 s, cdx_bool blocking) { // Make socket non-blocking. int flags = fcntl(s, F_GETFL, 0); if (flags == -1) { return errno; } if (blocking) { flags &= ~O_NONBLOCK; } else { flags |= O_NONBLOCK; } flags = fcntl(s, F_SETFL, flags); return flags == -1 ? errno : CDX_SUCCESS; } static cdx_void CdxSocketRecvBufSize(cdx_int32 s, int size) { int ret; ret = setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); CDX_FORCE_CHECK(ret == 0); return ; } cdx_void CdxSocketMakePortPair(cdx_int32 *rtpSocket, cdx_int32 *rtcpSocket, cdx_uint32 *rtpPort) { cdx_uint32 start, port; const cdx_int32 recvBufSize = 256 *1024; *rtpSocket = socket(AF_INET, SOCK_DGRAM, 0); CDX_FORCE_CHECK(*rtpSocket > 0); CdxSocketRecvBufSize(*rtpSocket, recvBufSize); *rtcpSocket = socket(AF_INET, SOCK_DGRAM, 0); CDX_FORCE_CHECK(*rtcpSocket > 0); CdxSocketRecvBufSize(*rtcpSocket, recvBufSize); start = (rand() * 1000)/ RAND_MAX + 15550; start &= ~1; struct sockaddr_in tmpAddr; int ret = 0; for (port = start; port < 65536; port += 2) { memset(tmpAddr.sin_zero, 0x00, sizeof(tmpAddr.sin_zero)); tmpAddr.sin_port = htons(port); tmpAddr.sin_family = AF_INET; tmpAddr.sin_addr.s_addr = htonl(INADDR_ANY); ret = bind(*rtpSocket, (const struct sockaddr *)&tmpAddr, sizeof(tmpAddr)); if (ret < 0) { continue; } tmpAddr.sin_port = htons(port + 1); ret = bind(*rtcpSocket, (const struct sockaddr *)&tmpAddr, sizeof(tmpAddr)); if ( ret == 0) { *rtpPort = port; return; } } CDX_TRESPASS(); return ; } cdx_int32 CdxSockAddrConstruct(struct sockaddr_in *dest, cdx_int8 *ip, cdx_int32 port) { struct hostent* hp = NULL; bzero(dest, sizeof(*dest)); dest->sin_family = AF_INET; dest->sin_port = htons(port); hp = (struct hostent *)gethostbyname(ip);//-block? if(!hp) { CDX_LOGE("get host fail."); return -1; } memcpy(&dest->sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); return 0; } cdx_int32 CdxSockSetBlocking(cdx_int32 sockfd, cdx_int32 blocking) { // Make socket non-blocking. cdx_int32 flags = fcntl(sockfd, F_GETFL, 0); if (flags == -1) { return errno; } if (blocking) { flags &= ~O_NONBLOCK; } else { flags |= O_NONBLOCK; } flags = fcntl(sockfd, F_SETFL, flags); return flags == -1 ? errno : 0; } cdx_int32 CdxSockIsBlocking(cdx_int32 sockfd) { cdx_int32 flags = fcntl(sockfd, F_GETFL, 0); return !(flags & O_NONBLOCK); } //nRecvBufLen: socket recv buf len to set. 0: use default. cdx_int32 CdxAsynSocket(int domain, cdx_int32 *nRecvBufLen) { cdx_int32 sockfd; cdx_int32 ret; socklen_t optlen; #if CONFIG_KEEP_ALIVE cdx_int32 keepalive = 1; // 开启keepalive属性 cdx_int32 keepidle = TCP_KEEP_IDLE_SECS; // 如该连接在60秒内没有任何数据往来,则进行探测 cdx_int32 keepinterval = TCP_KEEP_INTERVAL_SECS; // 探测时发包的时间间隔为5 秒 // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发. cdx_int32 keepcount = TCP_KEEP_COUNT; #endif sockfd = socket(domain, SOCK_STREAM, 0); if(sockfd < 0) { CDX_LOGE("errno(%d)", errno); return -1; } ret = CdxSockSetBlocking(sockfd, 0); CDX_FORCE_CHECK(ret == 0); if (nRecvBufLen != NULL) { if(*nRecvBufLen > 0) { ret = setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, nRecvBufLen, sizeof(*nRecvBufLen)); CDX_FORCE_CHECK(ret == 0); } ret = getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, nRecvBufLen, &optlen); //CDX_FORCE_CHECK(ret == 0); //CDX_LOGV("sock recv buf len set to: %d, return %d", nRecvBufLen, *nRecvBufLenRet); } #if CONFIG_KEEP_ALIVE setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive)); setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle)); setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval)); setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount)); #endif return sockfd; } cdx_void CdxSockDisableTcpKeepalive(cdx_int32 sockfd) { int optval; socklen_t optlen = sizeof(int); if(getsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { logd("getsockopt failed. errno=%d", errno); } //logd("SO_KEEPALIVE is %s, optval=%d, optlen=%d", (optval ? "ON" : "OFF"), optval, optlen); optval = 0; optlen = sizeof(optval); if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { logd("setsockopt failed."); } if(getsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { logd("getsockopt failed."); } return; } cdx_int32 CdxSockAsynConnect(cdx_int32 sockfd, const struct sockaddr *addr, socklen_t addrlen, cdx_long timeoutUs, cdx_int32 *pForceStop) { cdx_int32 ret, ioErr; fd_set ws; struct timeval tv; cdx_long loopTimes = 0, i = 0; if (timeoutUs == 0) { loopTimes = ((cdx_ulong)(-1L))>> 1; } else { loopTimes = timeoutUs/CDX_SELECT_TIMEOUT; } ret = connect(sockfd, (struct sockaddr *)addr, addrlen); if (ret == 0) { //success return 0; } if (errno != EINPROGRESS) { CDX_LOGE("<%s,%d>select err(%d)", __FUNCTION__, __LINE__, errno); return -1; } for (i = 0; i < loopTimes; i++) { if (pForceStop && *pForceStop) { CDX_LOGE("<%s,%d>force stop", __FUNCTION__, __LINE__); return -2; } FD_ZERO(&ws); FD_SET(sockfd, &ws); tv.tv_sec = 0; tv.tv_usec = CDX_SELECT_TIMEOUT; ret = select(sockfd + 1, NULL, &ws, NULL, &tv); if (ret > 0) { int nError = 0; socklen_t nLen = sizeof(nError); ret = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &nError, &nLen); // if error happened, Solaris's getsockopt return -1, // and set pending error to errno. Berkeley's getsockopt return 0, // and set pending error to nError. if(ret < 0 || nError) { if(nError) { errno = nError; } CDX_LOGE("connect err(%d)", errno); return -1; } // connect success return 0; } else if (ret == 0) { continue; } else { ioErr = errno; if (EINTR == ioErr) { continue; } CDX_LOGE("<%s,%d>select err(%d)", __FUNCTION__, __LINE__, errno); return -1; } } //time out return -1; } cdx_ssize CdxSockAsynSend(cdx_int32 sockfd, const void *buf, cdx_size len, cdx_long timeoutUs, cdx_int32 *pForceStop) { fd_set ws; fd_set errs; struct timeval tv; cdx_ssize ret = 0, sendSize = 0; cdx_long loopTimes = 0, i = 0; cdx_int32 ioErr; if (timeoutUs == 0) { loopTimes = ((unsigned long)(-1L))>> 1; } else { loopTimes = timeoutUs/CDX_SELECT_TIMEOUT; } for (i = 0; i < loopTimes; i++) { if (pForceStop && *pForceStop) { CDX_LOGE("<%s,%d>force stop", __FUNCTION__, __LINE__); return sendSize>0 ? sendSize : -2; } FD_ZERO(&ws); FD_SET(sockfd, &ws); FD_ZERO(&errs); FD_SET(sockfd, &errs); tv.tv_sec = 0; tv.tv_usec = CDX_SELECT_TIMEOUT; ret = select(sockfd + 1, NULL, &ws, &errs, &tv); if (ret < 0) { ioErr = errno; if (EINTR == ioErr) { continue; } CDX_LOGE("<%s,%d>select err(%d)", __FUNCTION__, __LINE__, errno); return -1; } else if (ret == 0) { //("timeout\n"); continue; } while (1) { if (pForceStop && *pForceStop) { CDX_LOGE("<%s,%d>force stop", __FUNCTION__, __LINE__); return sendSize>0 ? sendSize : -2; } if(FD_ISSET(sockfd, &errs)) { CDX_LOGE("<%s,%d>errs ", __FUNCTION__, __LINE__); break; } if(!FD_ISSET(sockfd, &ws)) { CDX_LOGW("select > 0, but sockfd is not ready?"); break; } ret = send(sockfd, ((char *)buf) + sendSize, len - sendSize, 0); if (ret < 0) { if (EAGAIN == errno) { //buffer not ready break; } else { CDX_LOGE("<%s,%d>send err(%d)", __FUNCTION__, __LINE__, errno); return -1; } } else if (ret == 0) { //buffer is full? break; } else // ret > 0 { sendSize += ret; if ((cdx_size)sendSize == len) { return sendSize; } } } } return sendSize; } //forcestop: return -2 //error: return -1 cdx_ssize CdxSockAsynRecv(cdx_int32 sockfd, void *buf, cdx_size len, cdx_long timeoutUs, cdx_int32 *pForceStop) { fd_set rs; fd_set errs; struct timeval tv; cdx_ssize ret = 0, recvSize = 0; cdx_long loopTimes = 0, i = 0; cdx_int32 ioErr; // cdx_int32 num = 0; if (CdxSockIsBlocking(sockfd)) { CDX_LOGE("<%s,%d>err, blocking socket", __FUNCTION__, __LINE__); return -1; } if (timeoutUs == 0) { loopTimes = ((unsigned long)(-1L))>> 1; } else { loopTimes = timeoutUs/CDX_SELECT_TIMEOUT; } for (i = 0; i < loopTimes; i++) { if (pForceStop && *pForceStop) { CDX_LOGE("<%s,%d>force stop", __FUNCTION__, __LINE__); return recvSize>0 ? recvSize : -2; } FD_ZERO(&rs); FD_SET(sockfd, &rs); FD_ZERO(&errs); FD_SET(sockfd, &errs); tv.tv_sec = 0; tv.tv_usec = CDX_SELECT_TIMEOUT; ret = select(sockfd + 1, &rs, NULL, &errs, &tv); if (ret < 0) { ioErr = errno; if (EINTR == ioErr) { continue; } CDX_LOGE("<%s,%d>select err(%d)", __FUNCTION__, __LINE__, ioErr); return -1; } else if (ret == 0) { //("timeout\n"); //CDX_LOGV("xxx timeout, select again..."); continue; } while (1) { if (pForceStop && *pForceStop) { CDX_LOGE("<%s,%d>force stop.recvSize(%ld)", __FUNCTION__, __LINE__, recvSize); return recvSize>0 ? recvSize : -2; } if(FD_ISSET(sockfd,&errs)) { CDX_LOGE("<%s,%d>errs ", __FUNCTION__, __LINE__); break; } if(!FD_ISSET(sockfd, &rs)) { CDX_LOGV("select > 0, but sockfd is not ready?"); break; } ret = recv(sockfd, ((char *)buf) + recvSize, len - recvSize, 0); if (ret < 0) { ioErr = errno; if (EAGAIN == ioErr) { break; } else { CDX_LOGE("<%s,%d>recv err(%d)", __FUNCTION__, __LINE__, errno); return -1; } } else if (ret == 0)//socket is close by peer? { CDX_LOGD("xxx recvSize(%ld),sockfd(%d), want to read(%lu)," "errno(%d), socket is closed by peer?", recvSize, sockfd, len, errno); return recvSize; } else // ret > 0 { recvSize += ret; if ((cdx_size)recvSize == len) { return recvSize; } } } } return recvSize; } cdx_ssize CdxSockNoblockRecv(cdx_int32 sockfd, void *buf, cdx_size len) { return recv(sockfd, buf, len, 0); }