工作中,经常使用到TCP通讯,做个记录。后续需要的话,可以拿过来直接使用。
1. 客户端连接请求。
首先将socket设置为NON_BLOCK。然后调用connect函数。如果没有返回成功,判断返回值是否是阻塞中。如果是,调用poll或者select,判断socket是否可写。如果可写,那么表明连接已经建立。否则报失败。
int _connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen,
const struct timeval *ro_tv)
{
int rc = connect(sockfd, addr, addrlen);
#ifdef OS_WIN
int wsaError = 0;
if (rc == -1)
{
wsaError = WSAGetLastError();
}
if (wsaError == WSAEWOULDBLOCK || wsaError == WSAEINPROGRESS)
{
#else
if (rc == -1 && errno == EINPROGRESS)
{
#endif
fd_set wset;
int optval;
socklen_t optlen = sizeof(optval);
struct timeval tv = *ro_tv;
/* Wait to be available in writing */
FD_ZERO(&wset);
FD_SET(sockfd, &wset);
rc = select(sockfd + 1, NULL, &wset, NULL, &tv);
if (rc <= 0)
{
/* Timeout or fail */
return -1;
}
/* The connection is established if SO_ERROR and optval are set to 0 */
rc = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&optval, &optlen);
if (rc == 0 && optval == 0)
{
return 0;
}
else
{
errno = ECONNREFUSED;
return -1;
}
}
return rc;
}
该函数被tcp_connect调用
int tcp_connect(const char* destIP,const short & destPort,struct timeval response_timeout)
{
int rc;
int s=-1;
/* Specialized version of sockaddr for Internet socket address (same size) */
struct sockaddr_in addr;
int flags = SOCK_STREAM;
#ifdef OS_WIN
if (tcpip_init_win32() == -1)
{
return -1;
}
#endif
s = socket(AF_INET, flags, 0);
if (s == -1)
{
return -1;
}
rc = tcp_set_ipv4_options(s);
if (rc == -1)
{
close(s);
s = -1;
return -1;
}
if (debugFlag)
{
printf("Connecting to %s:%d\n", destIP, (unsigned short)destPort);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(destPort);
unsigned long testAdd = inet_addr(destIP);
if (testAdd == INADDR_NONE) //不是有效的ip可能就是域名
{
struct hostent *host;
struct in_addr **addr_list;
int i;
if (NULL == (host = gethostbyname(destIP)))
{
if (debugFlag)
{
printf("域名ip不存在 %s\n", destIP);
}
close(s);
s = -1;
return -1;
}
addr_list = (struct in_addr **)host->h_addr_list;
char realIp[16];
for (i = 0; addr_list[i] != NULL; i++)
{
// inet_ntoa()将long类型的IP地址转化为圆点的字符串形式,作用与inet_addr()相反
strcpy(realIp, inet_ntoa(*addr_list[i]));
if (debugFlag)
{
printf("real ip: %s\n", realIp);
}
addr.sin_addr.s_addr = inet_addr(realIp);
}
}
else
{
addr.sin_addr.s_addr = testAdd;
}
rc = _connect(s, (struct sockaddr *)&addr, sizeof(addr), &response_timeout);
if (rc == -1)
{
close(s);
s = -1;
return -1;
}
return s;
}
2. select操作
这个函数,目前只关注了read
int tcp_select(int s, fd_set *rset, struct timeval *tv,int& errorCode)
{
int s_rc;
if(NULL==rset)
{
return -1;
}
while ((s_rc = select(s + 1, rset, NULL, NULL, tv)) == -1)
{
errorCode = errno;
if (errorCode == EINTR)
{
if (debugFlag)
{
//fprintf(stderr, "A non blocked signal was caught\n");
printf("A non blocked signal was caught\n");
}
/* Necessary after an error */
FD_ZERO(rset);
FD_SET(s, rset);
}
else
{
return -1;
}
}
if (s_rc == 0)
{
//errno = ETIMEDOUT;
errorCode = ETIMEDOUT;
return -1;
}
return s_rc;
}
3. 服务端监听
int tcp_listen(const char* listenIP,unsigned short listenPort,int nb_connection)
{
int new_s;
int enable;
int flags;
struct sockaddr_in addr;
flags = SOCK_STREAM;
new_s = socket(AF_INET, flags, IPPROTO_TCP);
if (new_s == -1)
{
return -1;
}
enable = 1;
if (setsockopt(new_s, SOL_SOCKET, SO_REUSEADDR,
(char *)&enable, sizeof(enable)) == -1)
{
close(new_s);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(listenPort);
if (listenIP[0] == '0')
{
/* Listen any addresses */
addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
/* Listen only specified IP address */
addr.sin_addr.s_addr = inet_addr(listenIP);
}
if (bind(new_s, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
close(new_s);
return -1;
}
if (listen(new_s, nb_connection) == -1)
{
close(new_s);
return -1;
}
return new_s;
}
4. 服务端接收连接
int tcp_accept(int s)
{
struct sockaddr_in addr;
socklen_t addrlen;
int newSock = -1;
addrlen = sizeof(addr);
newSock = accept(s, (struct sockaddr *)&addr, &addrlen);
if (newSock == -1)
{
return -1;
}
if (debugFlag)
{
printf("The client connection from %s is accepted\n",
inet_ntoa(addr.sin_addr));
}
return newSock;
}
5. 关闭连接
void tcp_close(int& s)
{
if (s != -1)
{
shutdown(s, SHUT_RDWR);
close(s);
s = -1;
}
}
6. 接收指定长度的数据
int tcp_receive_msg(int s, char *msg, int length_to_read,struct timeval timeout,int& errorCode)
{
int rc;
fd_set rset;
int msg_length = 0;
/* Add a file descriptor to the set */
FD_ZERO(&rset);
FD_SET(s, &rset);
while (length_to_read != 0)
{
rc = tcp_select(s, &rset, &timeout,errorCode);
if (rc == -1)
{
_error_print("select");
return -1;
}
rc = recv(s, msg + msg_length, length_to_read,0);
if (rc == 0)
{
errorCode = ECONNRESET;
rc = -1;
}
if (rc == -1)
{
return -1;
}
/* Display the hex code of each character received */
if (debugFlag)
{
int i;
for (i=0; i < rc; i++)
printf("<%.2X>", (unsigned char)msg[msg_length + i]);
}
/* Sums bytes received */
msg_length += rc;
/* Computes remaining bytes */
length_to_read -= rc;
}
if (debugFlag)
printf("\n");
return 0;
}