#include <stdio.h>
#include <stdlib.h>
#include <WINSOCK2.H>
#include <windows.h>
#include <time.h>
#else
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#endif
typedef unsigned char UINT8;
typedef unsigned short UINT16;
typedef unsigned int UINT32;
#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib")
#endif
#ifdef _WIN32
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EINPROGRESS WSAEINPROGRESS
#define ETIMEDOUT WSAETIMEDOUT
#endif
#ifndef _WIN32
typedef int SOCKET;
typedef struct sockaddr_in SOCKADDR_IN;
#define closesocket close
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#endif
#ifndef _WIN32
int GetLastError(void)
{
return errno;
}
#endif
#ifdef _WIN32
BOOL InitTCPIP(void)
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) //winsock2.2版本
{
printf("Init TCP:WSAStartup failed!\n");
return FALSE;
}
if (wsaData.wVersion != 0x0202) //验证版本协商结果
{
WSACleanup();
printf("Init TCP:wsaData.wVersion failed!\n");
return FALSE;
}
return TRUE;
}
#endif
/*
当我们以非阻塞的方式来进行连接的时候,返回的结果如果是 -1,这并不代表这次连接发生了错误,
如果它的返回结果是 EINPROGRESS(windows下返回EWOULDBLOCK),那么就代表连接还在进行中。 后面可以通过poll或者select来
判断socket是否可写,如果可以写,说明连接完成了
步骤1: 设置非阻塞,启动连接
实现非阻塞 connect ,首先把 sockfd 设置成非阻塞的。这样调用 connect 可以立刻返回,根据返回值和 errno 处理三种情况:
(1) 如果返回 0,表示 connect 成功。
(2) 如果返回值小于 0, errno 为 EINPROGRESS, 表示连接建立已经启动但是尚未完成。这是期望的结果,不是真正的错误。
(3) 如果返回值小于0,errno 不是 EINPROGRESS,则连接出错了。
步骤2:判断可读和可写
然后把 sockfd 加入 select 的读写监听集合,通过 select 判断 sockfd
是否可写,处理三种情况:
(1) 如果连接建立好了,对方没有数据到达,那么 sockfd 是可写的
(2) 如果在 select 之前,连接就建立好了,而且对方的数据已到达,那么 sockfd 是可读和可写的。
(3) 如果连接发生错误,sockfd 也是可读和可写的。
判断 connect 是否成功,就得区别 (2) 和 (3),这两种情况下 sockfd 都是
可读和可写的,区分的方法是,调用 getsockopt 检查是否出错。
步骤3:使用 getsockopt 函数检查错误
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len)
在 sockfd 都是可读和可写的情况下,我们使用 getsockopt 来检查连接
是否出错。但这里有一个可移植性的问题。
如果发生错误,getsockopt 源自 Berkeley 的实现将在变量 error 中
返回错误,getsockopt 本身返回0;然而 Solaris 却让 getsockopt 返回 -1,
并把错误保存在 errno 变量中。所以在判断是否有错误的时候,要处理这两种情况。
*/
int connect_nonb(int sockfd, struct sockaddr *saptr, int salen, int nsec, int *err)
{
fd_set readset, writeset;
struct timeval tm;
int n, error, len;
#ifdef _WIN32
int nonblock = 1;
n = ioctlsocket(sockfd, FIONBIO, &nonblock);
if (n != 0)
{
(* err) = GetLastError();
return SOCKET_ERROR;
}
#else
int flag = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flag|O_NONBLOCK);
#endif
error = 0;
n = connect(sockfd, saptr, salen);
if (n != 0)
{
(* err) = GetLastError();
if ((* err) != EINPROGRESS && (* err) != EWOULDBLOCK)
{
return SOCKET_ERROR;
}
}
else
goto done;
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_SET(sockfd, &readset);
FD_SET(sockfd, &writeset);
tm.tv_sec = nsec;
tm.tv_usec = 0;
n = select(sockfd + 1, &readset, &writeset, NULL, &tm);
if (n == 0)
{
(* err) = ETIMEDOUT;
return SOCKET_ERROR;
}
if (FD_ISSET(sockfd, &readset) || FD_ISSET(sockfd, &writeset))
{
len = sizeof(error);
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len) <0)
{
(* err) = GetLastError();
return SOCKET_ERROR;
}
}
else
printf("select error : sockfd not set");
/*将套接字恢复为默认状态,即阻塞态*/
done:
#ifdef _WIN32
nonblock = 0;
n = ioctlsocket(sockfd, FIONBIO, &nonblock);
if (n != 0)
{
(* err) = GetLastError();
return SOCKET_ERROR;
}
#else
fcntl(sockfd, F_SETFL, flag);
if (error)
{
(* err) = error;
return SOCKET_ERROR;
}
#endif
return 0;
}
int main(int argc, char **argv)
{
char *ipaddr;
SOCKADDR_IN addrSrv;
UINT16 port;
SOCKET sock;
int err;
if (argc != 3)
{
printf("input parameter's number error");
return 1;
}
ipaddr = argv[1];
port = (UINT16)atoi(argv[2]);
printf("%s %d\n", ipaddr, port);
#ifdef _WIN32
if (!InitTCPIP())
{
printf("InitTCPIP error\n");
return 1;
}
#endif
addrSrv.sin_family = AF_INET;
addrSrv.sin_addr.s_addr = inet_addr(ipaddr);
addrSrv.sin_port = htons(port);
if((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
printf("create socket error\n");
return 1;
}
if (connect_nonb(sock, (struct sockaddr *)&addrSrv, sizeof(addrSrv), 3, &err) == SOCKET_ERROR)
{
printf("FAILURE\n");
return 1;
}
closesocket(sock);
printf("SUCCESS\n");
return 0;
}
以下为纯linux下实现:
实现方法二:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd[2];
if(argc <2)
{
printf("param less\n");
return -1;
}
alarm(3);
pipe(fd);
write(fd[1], "\035", 1); /* ^] */
write(fd[1], "close\n", 6);
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
execlp("telnet", "telnet", argv[1], argv[2], 0);
}
实现方法三:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd[2];
if(argc <2)
{
printf("param less\n");
return -1;
}
alarm(3);
pipe(fd);
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
execlp("telnet", "telnet", argv[1], argv[2], 0);
}
实现方法四:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd[2];
if(argc <2)
{
printf("param less\n");
return -1;
}
alarm(3);
pipe(fd);
execlp("telnet", "telnet", argv[1], argv[2], 0);
}