English Amiga Board


Go Back   English Amiga Board > Coders > Coders. Language > Coders. C/C++

 
 
Thread Tools
Old 24 April 2017, 22:03   #1
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 585
Floppy disk Misunderstanding Sockets

Hi all!

I hope that you could help me understand socket and point me what's wrong with my code I have been trying to implement a kind of "ping" function without success

Essentially, I want the code to connect and select (?) an IP address and tell me if the address is alive or to timeout and tell me that the address is dead.

Here is my code (without some error checks). The problems are:
  • either I do not IoctlSocket(sock, FIONBIO, (void *)&one); and then I can connect to an alive IP but then I must wait a long timeout (on connect(...)) for down/non-existing IPs
  • or I do IoctlSocket(sock, FIONBIO, (void *)&one); and then I already receive "0" output: no difference between alive and a down/non-existing IPs
If I change the port from 80 to 2000, I receive an error 10061 on down IP but still 0 on non-existing IPs...


Code:
_http_ping_server("91.205.187.246", "80"); // Alive and well
_http_ping_server("132.207.170.31", "80"); // Down but existing
_http_ping_server("133.200.160.13", "80"); // Non-existing



// From http://stackoverflow.com/questions/2597608/c-socket-connection-timeout/2597774#2597774
static int _http_ping_server(
    IN char *addr,
    IN char *port)
{
    struct sockaddr_in *remote   = NULL;
    int                 result   = 0;
    int                 sock     = -1;
    fd_set              fd_read;
    fd_set              fd_write;
    fd_set              fd_excpt;
    struct timeval      tv;
    int                 so_error = 0;
    long                len      = 0;
    long                one      = 1;

    // Create TCP socket
    if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
        goto _RETURN_ERROR;
    }
    // Set non-blocking mode
    // IoctlSocket(sock, FIONBIO, (void *)&one);

    // Set remote->sin_addr.s_addr
    remote = (struct sockaddr_in *)malloc(sizeof(struct sockaddr_in));
    remote->sin_family = AF_INET;
    _inet_pton(AF_INET, addr, (void *)(&(remote->sin_addr.s_addr)));
    remote->sin_port = htons(atoi(port));

    // Connect to server
    if(connect(sock, (struct sockaddr *)remote, sizeof(struct sockaddr)) < 0)
    {
        printf("HERE0 %d\n", errno);

        FD_ZERO(&fd_read);
        FD_ZERO(&fd_write);
        FD_ZERO(&fd_excpt);
        tv.tv_sec  = 3;
        tv.tv_usec = 0;
        printf("HERE1 %ld\n", select(sock + 1, &fd_read, &fd_write, &fd_excpt, &tv));

        len = sizeof(so_error);
        getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len);
        printf("HERE2 %d\n", so_error);
        printf("HERE3 %ld\n", FD_ISSET(sock, &fd_read));
        printf("HERE4 %ld\n", FD_ISSET(sock, &fd_write));
        printf("HERE5 %ld\n", FD_ISSET(sock, &fd_excpt));
    }
    else
    {
        printf("CONNECTED\n");
    }
    ...
Help!
tygre is offline  
Old 24 April 2017, 22:48   #2
paraj
Registered User

 
Join Date: Feb 2017
Location: Denmark
Posts: 78
Looks like you're missing a call to FD_SET() before select()
paraj is offline  
Old 25 April 2017, 01:59   #3
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 585
Hi Paraj!

Thanks, I added the calls to FD_SET() but same behaviour...

What puzzles me in particular is that using port 80 yields "0" everywhere while using port 2000 makes the call to select() sets so_errno to 10061 but not when the IP address is non-existing???

I must be missing something here?

Cheers!
tygre is offline  
Old 25 April 2017, 22:07   #4
cla
dev

cla's Avatar
 
Join Date: Aug 2014
Location: Copenhagen
Age: 43
Posts: 63
Send a message via ICQ to cla
Quote:
I must be missing something here?
What you are trying to do is actually a TCP ping instead of the traditional ICMP ping. That is, if it is possible to establish a TCP connection (SYN,SYN-ACK,ACK and so on).

Port 1 to 1024 are for historical reasons classified as privileged ports. Depending on your TCP/IP stack this could explain the difference in behavior, even if it sounds odd.

As far as I can tell, select should only return 0 on timeout.

Here is a working example with BSD sockets of what you want to do. Look for the bold line.

Code:
/*
 * tcping.c
 *
 * Copyright (c) 2002-2008 Marc Kirchner <mail(at)marc(dash)kirchner(dot)de>
 *
 * tcping is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * tcping is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with ms++. If not, see <http://www.gnu.org/licenses/>.
 *
 * tcping does a nonblocking connect to test if a port is reachable.
 * Its exit codes are:
 *     -1 an error occured
 *     0  port is open
 *     1  port is closed
 *     2  user timeout
 */

#define VERSION 1.3.5

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netdb.h>

void usage();

int main (int argc, char *argv[]) {

	int sockfd;
	struct sockaddr_in addr;
	struct hostent *host;
	int error = 0;
	int ret;
	socklen_t errlen;
	struct timeval timeout;
	fd_set fdrset, fdwset;
	int verbose=1;
	int c;
	char *cptr;
	long timeout_sec=0, timeout_usec=0;
	int port=0;

	if (argc < 3)  {
		usage(argv[0]);
	}
	
	while((c = getopt(argc, argv, "qt:u:")) != -1) {
		switch(c) {
			case 'q':
				verbose = 0;
				break;
			case 't':
				cptr = NULL;
				timeout_sec = strtol(optarg, &cptr, 10);
				if (cptr == optarg)
					usage(argv[0]);
				break;
			case 'u':
				cptr = NULL;
				timeout_usec = strtol(optarg, &cptr, 10);
				if (cptr == optarg)
					usage(argv[0]);
				break;
			default:
				usage(argv[0]);
				break;
		}
	}
	
	sockfd = socket (AF_INET, SOCK_STREAM, 0);

	memset(&addr, 0, sizeof(addr));

	if ((host = gethostbyname(argv[optind])) == NULL) {
		if (verbose)
#ifdef HAVE_HSTRERROR
			fprintf(stderr, "error: %s\n", hstrerror(h_errno));
#else
			fprintf(stderr, "error: host not found");
#endif
		exit(-1);
	}
	
	memcpy(&addr.sin_addr, host->h_addr_list[0], host->h_length);
	addr.sin_family = host->h_addrtype; /* always AF_INET */
	if (argv[optind+1]) {
		cptr = NULL;
		port = strtol(argv[optind+1], &cptr, 10);
		if (cptr == argv[optind+1])
			usage(argv[0]);
	} else {
		usage(argv[0]);
	}
	addr.sin_port = htons(port);

	fcntl(sockfd, F_SETFL, O_NONBLOCK);
	if ((ret = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr))) != 0) {
		if (errno != EINPROGRESS) {
#ifdef HAVE_SOLARIS
			/* solaris immediately returns ECONNREFUSED on local ports */
			if (errno == ECONNREFUSED) {
				if (verbose) 
					fprintf(stdout, "%s port %s closed.\n", argv[optind], argv[optind+1]);
				close(sockfd);
				return(1);
			} else {
#endif	
				if (verbose)
					fprintf(stderr, "error: %s port %s: %s\n", argv[optind], argv[optind+1], strerror(errno));
				return (-1);
#ifdef HAVE_SOLARIS
			}
#endif	
		}

		FD_ZERO(&fdrset);
		FD_SET(sockfd, &fdrset);
		fdwset = fdrset;

		timeout.tv_sec=timeout_sec + timeout_usec / 1000000;
		timeout.tv_usec=timeout_usec % 1000000;

		if ((ret = select(sockfd+1, &fdrset, &fdwset, NULL, timeout.tv_sec+timeout.tv_usec > 0 ? &timeout : NULL)) == 0) {
			/* timeout */
			close(sockfd);
			if (verbose)
				fprintf(stdout, "%s port %s user timeout.\n", argv[optind], argv[optind+1]);
			return(2);
		}
		if (FD_ISSET(sockfd, &fdrset) || FD_ISSET(sockfd, &fdwset)) {
			errlen = sizeof(error);
			if ((ret=getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &errlen)) != 0) {
				/* getsockopt error */
				if (verbose)
					fprintf(stderr, "error: %s port %s: getsockopt: %s\n", argv[optind], argv[optind+1], strerror(errno));
				close(sockfd);
				return(-1);
			}
			if (error != 0) {
				if (verbose) 
					fprintf(stdout, "%s port %s closed.\n", argv[optind], argv[optind+1]);
				close(sockfd);
				return(1);
			}
		} else {
			if (verbose)
				fprintf(stderr, "error: select: sockfd not set\n");
			exit(-1);
		}
	}
	/* OK, connection established */
	close(sockfd);
	if (verbose)
		fprintf(stdout, "%s port %s open.\n", argv[optind], argv[optind+1]);
	return 0;
}

void usage(char *prog) {
	fprintf(stderr, "error: Usage: %s [-q] [-t timeout_sec] [-u timeout_usec] <host> <port>\n", prog);
		exit(-1);
}
From https://github.com/mkirchner/tcping/...aster/tcping.c

Last edited by cla; 27 April 2017 at 18:54.
cla is offline  
Old 25 April 2017, 22:27   #5
nogginthenog
Amigan

 
Join Date: Feb 2012
Location: London
Posts: 677
Quote:
Originally Posted by cla View Post
Port 1 to 1024 are for historical reason classified as privileged ports. Depending on your TCP/IP stack this could explain the different behavior, even if it sounds odd.
To add to this, only root can listen on ports 1 to 1024. What this means for Amiga stacks I have no idea!
nogginthenog is offline  
Old 25 April 2017, 22:47   #6
cla
dev

cla's Avatar
 
Join Date: Aug 2014
Location: Copenhagen
Age: 43
Posts: 63
Send a message via ICQ to cla
Quote:
Originally Posted by nogginthenog View Post
To add to this, only root can listen on ports 1 to 1024. What this means for Amiga stacks I have no idea!
There are several implementation. I bet the 4 main AmigaOS does not share much code. And only a few people know how much Roadshow shares with say AmiTCP.

But thats another story
cla is offline  
Old 25 April 2017, 23:50   #7
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 585
Hi Cla and Nogginthenog!

Thank you so much Cla for your help!
I am going to try tonight!

Thanks Nogginthenog for the precision!

Will keep you posted!
Cheers!
tygre is offline  
Old 28 April 2017, 19:41   #8
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 585
Hi Cla and all!

So, I have tried your example but without any luck

Below is the code that I used. I believe the problem is with the functions fnctl() and IoctlSocket():
  • If I use fnctl(), connect() will succeed for available IPs/ports but timeout (about 10 sec.) for non-existing IPs. It seems that the socket is not in non-blocking mode? It prints "error: 133.200.160.12 port 80: 0" (errno = 0) on an non-existing IP.
  • If I use IoctlSocket(), "IoctlSocket returns 0", connect() returns "immediately" != 0 but errno = 0 and it prints "error: ..." for available and unavailable IPs/ports. The socket is in non-blocking or, more likely, broken, because connect() returns an error immediately.

I must be missing something!?
Thanks!

Code:
	int                 sockfd;
	struct sockaddr_in  addr;
	struct hostent     *host;
	int                 error        = 0;
	int                 ret;
	long                errlen;
	struct timeval      timeout;
	fd_set              fdrset;
	fd_set              fdwset;
	int                 c;
	char               *cptr;
	long                timeout_sec  = 0;
	long		        timeout_usec = 0;
	long                one          = 1;

	sockfd = socket (AF_INET, SOCK_STREAM, 0);

	memset(&addr, 0, sizeof(addr));

	if ((host = gethostbyname(ip)) == NULL) {
		fprintf(stderr, "error: host not found");
		return RETURN_ERROR;
	}

	memcpy(&addr.sin_addr, host->h_addr_list[0], host->h_length);
	addr.sin_family = host->h_addrtype; /* always AF_INET */
	addr.sin_port = htons(atoi(port));

	// ret = fcntl(sockfd, F_GETFL, 0);
	// fcntl(sockfd,       F_SETFL, ret | O_NONBLOCK);
	printf("IoctSocket returns %ld\n", IoctlSocket(sockfd, FIONBIO, (void *)&one));

	if ((ret = connect(sockfd, (struct sockaddr *) &addr, sizeof(addr))) != 0)
	{
		if (errno != EINPROGRESS)
		{
			if (errno == ECONNREFUSED) {
				fprintf(stdout, "%s port %s closed.\n", ip, port);
				close(sockfd);
				return(1);
			}
			else
			{
				fprintf(stderr, "error: %s port %s: %d\n", ip, port, errno);
				return RETURN_ERROR;
			}
		}
		fprintf(stdout, "%s port %s THERE.\n", ip, port);

		FD_ZERO(&fdrset);
		FD_SET(sockfd, &fdrset);
		fdwset = fdrset;

		timeout_sec = 0;
		timeout_usec = 3;
		
		timeout.tv_sec=timeout_sec + timeout_usec / 1000000;
		timeout.tv_usec=timeout_usec % 1000000;

		if ((ret = select(sockfd+1, &fdrset, &fdwset, NULL, timeout.tv_sec+timeout.tv_usec > 0 ? &timeout : NULL)) == 0) {
			/* timeout */
			close(sockfd);
			fprintf(stdout, "%s port %s user timeout.\n", ip, port);
			return RETURN_ERROR;
		}
		if (FD_ISSET(sockfd, &fdrset) || FD_ISSET(sockfd, &fdwset)) {
			errlen = sizeof(error);
			if ((ret=getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &errlen)) != 0) {
				/* getsockopt error */
				fprintf(stderr, "error: %s port %s: getsockopt: %d\n", ip, port, errno);
				close(sockfd);
				return RETURN_ERROR;
			}
			if (error != 0) {
				fprintf(stdout, "%s port %s closed.\n", ip, port);
				close(sockfd);
				return RETURN_ERROR;
			}
		} else {
			fprintf(stderr, "error: select: sockfd not set\n");
			return RETURN_ERROR;
		}
	}
	/* OK, connection established */
	close(sockfd);
	fprintf(stdout, "%s port %s open.\n", ip, port);
	return RETURN_OK;
tygre is offline  
Old 30 April 2017, 17:40   #9
cla
dev

cla's Avatar
 
Join Date: Aug 2014
Location: Copenhagen
Age: 43
Posts: 63
Send a message via ICQ to cla
Hi Tygre and all!

I don't know if you are using AmiTCP? I tried to do non-blocking TCP socket with version 4.x but I never managed to it to work. Non-blocking UDP sockets are no problem but I doubt AmiTCP supports non-blocking TCP socket.

While I was working on some other network programs using the AmiTCP TCP/IP stack, I discovered several socket flags which was not supported. SO_RCVTIMEO was one of them. If you are really serious about socket programming on AmigaOS 3.x I think you should try Roadshow.

Otherwise accepting a 10 seconds timeout is probably the best solution.
cla is offline  
Old 30 April 2017, 21:31   #10
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 585
Hi!

Quote:
Originally Posted by cla View Post
I don't know if you are using AmiTCP? I tried to do non-blocking TCP socket with version 4.x but I never managed to it to work. Non-blocking UDP sockets are no problem but I doubt AmiTCP supports non-blocking TCP socket.
Ah, so this explains that: I am using Miami / WinUAE bsddsocket Then, could you point me to use UDP instead of TCP to implement a simple "ping", please?

Quote:
Originally Posted by cla View Post
While I was working on some other network programs using the AmiTCP TCP/IP stack, I discovered several socket flags which was not supported. SO_RCVTIMEO was one of them. If you are really serious about socket programming on AmigaOS 3.x I think you should try Roadshow.

Otherwise accepting a 10 seconds timeout is probably the best solution.
Will give it a try!

Thanks in advance!
tygre is offline  
Old 01 May 2017, 01:18   #11
cla
dev

cla's Avatar
 
Join Date: Aug 2014
Location: Copenhagen
Age: 43
Posts: 63
Send a message via ICQ to cla
Quote:
Originally Posted by tygre View Post
Hi!
Ah, so this explains that: I am using Miami / WinUAE bsddsocket Then, could you point me to use UDP instead of TCP to implement a simple "ping", please?
Well, the code is:
Code:
socket(AF_INET, SOCK_DGRAM, 0)
But it wont work since you wont get a reply from the host. UDP does not rely on states and does not implement a 3-Way Handshake like TCP does.

What you are missing is documentation for the TCP/IP stack that you are using. I also had troubles with the UAE bsd socket library since it turned out to be different from AmiTCP (which I used as a reference).

There used to be some bsdsocket.library documentation available at www.kuchinka.cz but it is not available anymore.

Maybe related to UAE bsdsocket.library inconsistensy:
http://eab.abime.net/showthread.php?t=86532

And here are the official (AmigaOS 4.1) docs:
http://wiki.amigaos.net/amiga/autodo...socket.doc.txt
cla is offline  
Old 03 September 2017, 21:54   #12
tygre
Returning fan!

tygre's Avatar
 
Join Date: Jan 2011
Location: Montréal, QC, Canada
Posts: 585
Hi Cla and all!

Thanks Cla for pointing out this implementation of ping for AmigaOS

I'm going to try it out, hopefully I'll manage to make it work

Cheers!
tygre is offline  
 


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Similar Threads
Thread Thread Starter Forum Replies Last Post
Anyone knows where to get 68000 sockets? amigasith support.Hardware 42 08 December 2014 23:10
Sockets Simms angled for A4000D Cosmos MarketPlace 13 15 March 2010 16:35
SIMM sockets with metal clips amigakit.com MarketPlace 0 07 November 2008 16:48
23 pin D-SUB female sockets manicx support.Hardware 8 09 October 2004 01:15
What, if anything, do the extra ROM sockets on the A1000 do? Computolio support.Hardware 4 16 September 2004 03:24

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 13:55.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2018, vBulletin Solutions Inc.
Page generated in 0.09595 seconds with 15 queries