Dieses Programm zeigt, wie man unter Windows einen Verbindungsaufruf nach einer definierten Wartezeit abbrechen kann, indem select() eingesetzt wird. Der Socket wird dabei zeitweise nicht-blockierend gemacht. Prinzipiell kann dies auch unter UNIX gemacht werden.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <winsock.h>
#define BUFFER_SIZE 1024
#define USE_SELECT 1
#define CONNECT_TIMEOUT 5
int main(int argc, char *argv[])
{
WSADATA wsa;
SOCKET sock;
const char *hostname;
const char *portnumber;
struct hostent *host;
struct sockaddr_in addr;
char request[BUFFER_SIZE];
char response[BUFFER_SIZE];
time_t t_start;
int ret;
#if (defined USE_SELECT) && (USE_SELECT == 1)
/* Variablen die nur für die select-Variante benötigt werden */
unsigned long opt;
fd_set fds;
struct timeval timeout;
#endif
if (argc != 3)
{
fprintf(stderr, "try %s <hostname> <port>\n", argv[0]);
return EXIT_FAILURE;
}
hostname = argv[1];
portnumber = argv[2];
/* Winsock-System starten */
if (WSAStartup(MAKEWORD(1, 1), &wsa) != 0)
{
fprintf(stderr, "Starting winsock failed\n");
return EXIT_FAILURE;
}
/* Hostnamen in IP-Adresse auflösen */
host = gethostbyname(hostname);
if (host == NULL)
{
fprintf(stderr, "Can't lookup %s: %lu\n",
hostname, WSAGetLastError());
return EXIT_FAILURE;
}
addr.sin_addr = *((struct in_addr*) (host->h_addr_list[0]));
/* Socket anlegen */
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
{
fprintf(stderr, "Can't create socket: %lu\n", WSAGetLastError());
return EXIT_FAILURE;
}
/* Verbindungsdaten zusammenstellen */
addr.sin_family = AF_INET;
addr.sin_port = htons((unsigned short)atoi(portnumber));
t_start = time(NULL);
#if (defined USE_SELECT) && (USE_SELECT == 1)
/* Die Variante mit select erfordert, daß der Socket nicht-blockierend
* gemacht wird. Dies kann (und wird in diesem Beispiel) nach dem Verbinden
* wieder rückgängig gemacht, sodaß man wie gewohnt mit dem Socket arbeiten
* kann.
*/
opt = 1;
ioctlsocket(sock, FIONBIO, &opt);
/* Den Verbindungsaufbau anstossen */
if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) == SOCKET_ERROR)
{
/* Das schlägt normalerweise fehl, wobei der Fehler WSAEWOULDBLOCK
* darauf hinweist, daß der Verbindungsaufbau durchaus noch Erfolgreich
* sein kann, und der Aufruf nur fehlgeschlagen ist, weil er andernfalls
* blockieren würde, was ja absichtlich deaktiviert wurde.
*/
if (WSAGetLastError() != WSAEWOULDBLOCK)
{
printf("*** connect took %u seconds ***\n", time(NULL) - t_start);
fprintf(stderr, "Can't connect to %s:%u: %lu\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), WSAGetLastError());
return EXIT_FAILURE;
}
}
/* Deskriptor-Set zurücksetzen und mit dem zu verbindenden Socket belegen */
FD_ZERO(&fds);
FD_SET(sock, &fds);
/* Den gewählte timeout-Wert einsetzen */
timeout.tv_sec = CONNECT_TIMEOUT;
timeout.tv_usec = 0;
/* Nun select aufrufen; dieses kehrt entweder nach Ablauf des Timeouts
* zurück, oder wenn der Socket zum Schreiben bereit ist, was genau dann
* passiert, wenn er erfolgreich verbunden wurde.
*/
ret = select(sock + 1, NULL, &fds, NULL, &timeout);
if (ret == SOCKET_ERROR)
{
fprintf(stderr, "Call to select failed: %lu\n", WSAGetLastError());
return EXIT_FAILURE;
}
/* Falls select zurückgekehrt ist, aber der zu verbindende Socket nicht
* im Deskriptor-Set vorhanden ist, war das Verbinden in der gegebenen
* Zeit nicht erfolgreich.
*/
if (FD_ISSET(sock, &fds) == 0)
{
printf("*** connect took %u seconds ***\n", time(NULL) - t_start);
fprintf(stderr, "Can't connect to %s:%u, timed out\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
return EXIT_FAILURE;
}
/* Der Socket kann nun wieder blockierend gemacht werden */
opt = 0;
ioctlsocket(sock, FIONBIO, &opt);
#else
/* Die Lösung ohne select ruft connect ganz normal auf */
if (connect(sock, (struct sockaddr*) &addr, sizeof(addr)) == SOCKET_ERROR)
{
printf("*** connect took %u seconds ***\n", time(NULL) - t_start);
fprintf(stderr, "Can't connect to %s:%u: %lu\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), WSAGetLastError());
return EXIT_FAILURE;
}
#endif
printf("connected to %s:%u!\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
/* Als Test wird ein kleines HTTP-Request abgesetzt, und dessen Antwort
* auf die Konsole ausgegeben.
*/
sprintf(request, "HEAD / HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: Close\r\n"
"\r\n", hostname);
ret = send(sock, request, strlen(request), 0);
if (ret == SOCKET_ERROR)
{
fprintf(stderr, "Sending request failed: %lu\n", WSAGetLastError());
return EXIT_FAILURE;
}
printf("request sent... (%i octets)\n");
ret = recv(sock, response, sizeof(response), 0);
if (ret == SOCKET_ERROR)
{
fprintf(stderr, "Receiving response failed: %lu\n", WSAGetLastError());
return EXIT_FAILURE;
}
printf("response read, %i octets:\n\n", ret);
puts("==================================================");
fwrite(response, 1, ret, stdout);
puts("==================================================");
closesocket(sock);
WSACleanup();
return EXIT_SUCCESS;
}