Is there a way to program socket communications in gfortran on Windows? I'm
guessing that this can be achieved via gcc, so a better question might be has
someone seen or developed Fortran code to handle sockets via C?
|
|
0
|
|
|
|
Reply
|
Gib
|
8/30/2010 9:02:35 AM |
|
On 30 aug, 11:02, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> Is there a way to program socket communications in gfortran on Windows? =
=A0I'm
> guessing that this can be achieved via gcc, so a better question might be=
has
> someone seen or developed Fortran code to handle sockets via C?
It is certainly doable. I did something in that direction in a work-
related
project (not with gcc/gfortran though). I have been meaning to do such
a module
for my Flibs project (flibs.sf.net), but have not gotten around to it.
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
Arjen
|
8/30/2010 11:32:51 AM
|
|
On Aug 30, 12:02=A0pm, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> Is there a way to program socket communications in gfortran on Windows? =
=A0I'm
> guessing that this can be achieved via gcc, so a better question might be=
has
> someone seen or developed Fortran code to handle sockets via C?
I saw some code here http://genepi.qimr.edu.au/staff/davidD/, but I
never tried it myself.
Victor.
|
|
0
|
|
|
|
Reply
|
Victor
|
8/30/2010 5:03:02 PM
|
|
Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
> Is there a way to program socket communications in gfortran on Windows? I'm
> guessing that this can be achieved via gcc, so a better question might be has
> someone seen or developed Fortran code to handle sockets via C?
Here's some stuff I did some time ago. The Unix version was from about
to 2 decades ago and I did a Windows port about a decade later. This was
specifically done for CVF, but I doubt there would be much if any
difference for other Windows Fortran compilers.
The stuff attached here is just the C wrappers that make the socket
stuff reasonably callable from Fortran. It is pretty simple minded;
there's a tcp_connect, tcp_close, tcp_read, and tcp_write. This is all
from before f2003 C interop (as mentioned, the original Unix versions
date back 2 decades).
The tcp_connect is just for the client end. My server was always on a
Unix box, so I didn't need to do the server-side stuff for Windows.
/* $Id: clientc.c,v 1.1.1.1 2003/03/06 19:38:16 maine Exp $ */
/* $Name: $ */
/* clientc.c
* open a tcp connection for a getData client.
*
* The read/write/close routines are used in both clients and servers
* and are in a separate file.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for MS Windows MS C and CVF.
* Differences from nag version are:
* Different system include files.
* Different procedure name mangling convention in the defines
* Specify __stdcall in procedure headers.
* Add extern decl for write_error_sub (to get the __stdcall).
* Different placement of implicit string length argument.
* Have to call Win-specific WSAStartup or nothing works.
* Cast *port (perhaps appropriate for nag version also).
* Used SOCKET_ERROR and INVALID_SOCKET to test for errors per MS
docs,
* though the bsd-style test did appear to work.
*
* 11 Oct 91, Richard Maine: Version 1.0
* 17 Jul 01, Richard Maine: MS Windows port.
*/
#include <sys/types.h>
#include <winsock.h>
/* Defines to make routines fortran-callable */
/*
* Would probably be better to return an error message to the
* calling routine instead of calling write_error_msg from in here.
*/
static int started = 0;
#define write_error_msg WRITE_ERROR_SUB
#define tcp_connect TCP_CONNECT
extern __stdcall WRITE_ERROR_SUB (char * msg, int n);
/* Wrapper to call fortran error message routine.
* We want to avoid standard c i/o. */
void printerrormsg(msg)
char *msg;
{
write_error_msg(msg,strlen(msg));
}
/* Connect to a specified host and port.
* Return a socket number in sock. */
void __stdcall tcp_connect(sock, host_name, host_name_len, port, error)
int *sock, *port, *error;
char *host_name;
int host_name_len;
{
char cstring[129];
int i;
struct hostent *host_ent;
struct sockaddr_in host;
int keepalive;
char *from, *to;
WORD wver;
WSADATA wsaData;
*error = 1;
/* Start up Windows sockets */
if (started == 0) {
started = 1;
wver = MAKEWORD(2,0);
i = WSAStartup(wver, &wsaData);
}
/* Make host_name into a c string */
for (i=0; (i<host_name_len) && (i<127) && (host_name[i] != ' '); i++)
cstring[i] = host_name[i];
cstring[i] = '\0';
/* Find the host IP address */
host.sin_family = AF_INET;
host_ent = gethostbyname(cstring);
if (host_ent==0) {printerrormsg("Can't find host address."); return;}
/* Avoid bcopy/memcpy dependence. */
from = (char *) host_ent->h_addr;
to = (char *) &host.sin_addr;
for (i=0; i<host_ent->h_length; i++) *to++ = *from++;
/* Set the port */
host.sin_port = htons((unsigned short int) *port);
/* Create a socket */
*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (*sock==INVALID_SOCKET) {printerrormsg("Can't create socket.");
return;}
/* Attempt connection */
if (connect(*sock, (struct sockaddr *) &host, sizeof(host)) ==
SOCKET_ERROR)
{printerrormsg("Connect failed."); close(*sock); return;}
/* Enable keepalive packets to check socket connection. */
keepalive = 1;
setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, 4);
*error = 0;
}
/* $Id: tcpc.c,v 1.1.1.1 2003/03/06 19:38:16 maine Exp $ */
/* $Name: $ */
/* tcpc.c
* tcp communication routines for getData.
*
* The read/write/close here are used in both clients and servers.
* These routines must not directly or indirectly reference the
* fortran write_error_msg routine because some versions of it
* may call these routines.
*
* The routines for opening a tcp connection are different for
* clients and servers. Furthermore, the client one may
* call write_error_msg. Thus those routines are in separate files.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for MS Windows MS C and CVF.
* Differences from nag version are:
* Different system include files.
* Different procedure name mangling convention in the defines
* Specify __stdcall in procedure headers.
* Use closesocket instead of close.
* Used SOCKET_ERROR to test for errors per MS docs,
* though the bsd-style test did appear to work.
*
* 11 Oct 91, Richard Maine: Version 1.0
* 17 Jul 01, Richard Maine: MS Windows port.
*/
#include <winsock.h>
/* Defines to make routines fortran-callable */
#define tcp_close TCP_CLOSE
#define tcp_read TCP_READ
#define tcp_write TCP_WRITE
void __stdcall tcp_close(sock)
int *sock;
{
shutdown(*sock,2); /* clears any pending i/o. */
closesocket(*sock);
}
/* Write to a tcp socket. */
void __stdcall tcp_write(sock, buf, buflen, error)
int *sock, *buflen, *error;
char *buf;
{
int i;
i = send(*sock,buf,*buflen,0);
*error = (i == SOCKET_ERROR);
}
/* Read from a tcp socket. */
void __stdcall tcp_read(sock, buf, buflen, error)
int *sock, *buflen, *error;
char *buf;
{
int nleft, nread;
char *bufptr;
bufptr = buf;
nleft = *buflen;
*error = 1;
while (nleft > 0)
{
nread = recv(*sock, bufptr, nleft, 0);
if (nread == SOCKET_ERROR) return;
nleft -= nread;
bufptr += nread;
}
*error = 0;
}
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
|
|
0
|
|
|
|
Reply
|
nospam47 (9742)
|
8/30/2010 5:54:20 PM
|
|
Victor Podsechin wrote:
> On Aug 30, 12:02 pm, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
>> Is there a way to program socket communications in gfortran on Windows? I'm
>> guessing that this can be achieved via gcc, so a better question might be has
>> someone seen or developed Fortran code to handle sockets via C?
>
> I saw some code here http://genepi.qimr.edu.au/staff/davidD/, but I
> never tried it myself.
> Victor.
Hi Victor. This is Linux/Unix code, and MinGW doesn't like it at all.
|
|
0
|
|
|
|
Reply
|
Gib
|
8/30/2010 9:02:02 PM
|
|
Richard Maine wrote:
> Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
>
>> Is there a way to program socket communications in gfortran on Windows? I'm
>> guessing that this can be achieved via gcc, so a better question might be has
>> someone seen or developed Fortran code to handle sockets via C?
>
> Here's some stuff I did some time ago. The Unix version was from about
> to 2 decades ago and I did a Windows port about a decade later. This was
> specifically done for CVF, but I doubt there would be much if any
> difference for other Windows Fortran compilers.
>
> The stuff attached here is just the C wrappers that make the socket
> stuff reasonably callable from Fortran. It is pretty simple minded;
> there's a tcp_connect, tcp_close, tcp_read, and tcp_write. This is all
> from before f2003 C interop (as mentioned, the original Unix versions
> date back 2 decades).
>
> The tcp_connect is just for the client end. My server was always on a
> Unix box, so I didn't need to do the server-side stuff for Windows.
<code snipped>
Thanks Richard, this will be helpful.
|
|
0
|
|
|
|
Reply
|
Gib
|
8/30/2010 9:03:10 PM
|
|
Hi Richard, a couple of minor mods and this is working with MinGW/gcc/gfortran.
No need for __stdcall, or uppercase, but underscores need to be appended to C
function names.
Thanks a lot.
Gib
|
|
0
|
|
|
|
Reply
|
Gib
|
8/31/2010 12:47:20 AM
|
|
Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
> Hi Richard, a couple of minor mods and this is working with
> MinGW/gcc/gfortran. No need for __stdcall, or uppercase, but underscores
> need to be appended to C function names.
> Thanks a lot.
You're welcome. Good to hear. I've never actually used MinGW, so I
wasn't at all sure of that part, but it seemed plausible that the native
Windows stuff should be a good starting point.
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
|
|
0
|
|
|
|
Reply
|
nospam
|
8/31/2010 1:44:07 AM
|
|
Gib Bogle wrote:
> Victor Podsechin wrote:
>> On Aug 30, 12:02 pm, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
>>> Is there a way to program socket communications in gfortran on
>>> Windows? I'm
>>> guessing that this can be achieved via gcc, so a better question
>>> might be has
>>> someone seen or developed Fortran code to handle sockets via C?
>>
>> I saw some code here http://genepi.qimr.edu.au/staff/davidD/, but I
>> never tried it myself.
>> Victor.
>
> Hi Victor. This is Linux/Unix code, and MinGW doesn't like it at all.
I think you want to start thinking along these lines. I follow the
Stevens and Rago development in _Advanced Programming in the Unix
Environment_. You can too. As a matter of fact, you might be a wiz
with this material relative to me, as I don't have a full year yet as a
linux user, so I don't really see a lot of the big picture.
http://www.apuebook.com/
The source is free, tested, ... . There's a chapter on sockets that's
still way over my head, but there for the download.
if you can get this source running, you can see it done clearly enough
in C that you can fish out what fortran calls you would issue through
the iso c binding.
$ gcc -D_GNU_SOURCE pathfunc.o -Iinclude lib/error.o dir1.c -o out
$ ./out
usage: ftw <starting-pathname>
$ ./out /home/dan/source
regular files = 5541, 86.63 %
directories = 647, 10.12 %
block special = 0, 0.00 %
char special = 0, 0.00 %
FIFOs = 0, 0.00 %
symbolic links = 208, 3.25 %
sockets = 0, 0.00 %
$ cat dir1.c
#include "apue.h"
#include <dirent.h>
#include <limits.h>
/* function type that is called for each filename */
typedef int Myfunc(const char *, const struct stat *, int);
static Myfunc myfunc;
static int myftw(char *, Myfunc *);
static int dopath(Myfunc *);
static long nreg, ndir, nblk, nchr, nfifo, nslink, nsock, ntot;
int
main(int argc, char *argv[])
{
int ret;
if (argc != 2)
err_quit("usage: ftw <starting-pathname>");
ret = myftw(argv[1], myfunc); /* does it all */
ntot = nreg + ndir + nblk + nchr + nfifo + nslink + nsock;
if (ntot == 0)
ntot = 1; /* avoid divide by 0; print 0 for all counts */
printf("regular files = %7ld, %5.2f %%\n", nreg,
nreg*100.0/ntot);
printf("directories = %7ld, %5.2f %%\n", ndir,
ndir*100.0/ntot);
printf("block special = %7ld, %5.2f %%\n", nblk,
nblk*100.0/ntot);
printf("char special = %7ld, %5.2f %%\n", nchr,
nchr*100.0/ntot);
printf("FIFOs = %7ld, %5.2f %%\n", nfifo,
nfifo*100.0/ntot);
printf("symbolic links = %7ld, %5.2f %%\n", nslink,
nslink*100.0/ntot);
printf("sockets = %7ld, %5.2f %%\n", nsock,
nsock*100.0/ntot);
exit(ret);
}
/*
* Descend through the hierarchy, starting at "pathname".
* The caller's func() is called for every file.
*/
#define FTW_F 1 /* file other than directory */
#define FTW_D 2 /* directory */
#define FTW_DNR 3 /* directory that can't be read */
#define FTW_NS 4 /* file that we can't stat */
static char *fullpath; /* contains full pathname for every file */
static int /* we return whatever func() returns */
myftw(char *pathname, Myfunc *func)
{
int len;
fullpath = path_alloc(&len); /* malloc's for PATH_MAX+1 bytes */
/* ({Prog pathalloc}) */
strncpy(fullpath, pathname, len); /* protect against */
fullpath[len-1] = 0; /* buffer overrun */
return(dopath(func));
}
/*
* Descend through the hierarchy, starting at "fullpath".
* If "fullpath" is anything other than a directory, we lstat() it,
* call func(), and return. For a directory, we call ourself
* recursively for each name in the directory.
*/
static int /* we return whatever func() returns */
dopath(Myfunc* func)
{
struct stat statbuf;
struct dirent *dirp;
DIR *dp;
int ret;
char *ptr;
if (lstat(fullpath, &statbuf) < 0) /* stat error */
return(func(fullpath, &statbuf, FTW_NS));
if (S_ISDIR(statbuf.st_mode) == 0) /* not a directory */
return(func(fullpath, &statbuf, FTW_F));
/*
* It's a directory. First call func() for the directory,
* then process each filename in the directory.
*/
if ((ret = func(fullpath, &statbuf, FTW_D)) != 0)
return(ret);
ptr = fullpath + strlen(fullpath); /* point to end of fullpath */
*ptr++ = '/';
*ptr = 0;
if ((dp = opendir(fullpath)) == NULL) /* can't read directory */
return(func(fullpath, &statbuf, FTW_DNR));
while ((dirp = readdir(dp)) != NULL) {
if (strcmp(dirp->d_name, ".") == 0 ||
strcmp(dirp->d_name, "..") == 0)
continue; /* ignore dot and dot-dot */
strcpy(ptr, dirp->d_name); /* append name after slash */
if ((ret = dopath(func)) != 0) /* recursive */
break; /* time to leave */
}
ptr[-1] = 0; /* erase everything from slash onwards */
if (closedir(dp) < 0)
err_ret("can't close directory %s", fullpath);
return(ret);
}
static int
myfunc(const char *pathname, const struct stat *statptr, int type)
{
switch (type) {
case FTW_F:
switch (statptr->st_mode & S_IFMT) {
case S_IFREG: nreg++; break;
case S_IFBLK: nblk++; break;
case S_IFCHR: nchr++; break;
case S_IFIFO: nfifo++; break;
case S_IFLNK: nslink++; break;
case S_IFSOCK: nsock++; break;
case S_IFDIR:
err_dump("for S_IFDIR for %s", pathname);
/* directories should have type = FTW_D */
}
break;
case FTW_D:
ndir++;
break;
case FTW_DNR:
err_ret("can't read directory %s", pathname);
break;
case FTW_NS:
err_ret("stat error for %s", pathname);
break;
default:
err_dump("unknown type %d for pathname %s", type, pathname);
}
return(0);
}
// gcc -D_GNU_SOURCE pathfunc.o -Iinclude lib/error.o dir1.c -o out
$
! end listing of dir1.c
You need 2 other translation units to make this work. I'll list them
after the sig. They're both significant pieces of writing. pathalloc.c
and apue.h are what the textbook guys think are the best implementations
for, respectively, allocating a path and doing anything.
I was very interested the other day to see how steve and james were able
to make directories using fortran's c bindings.
--
Uno
$ cat path_alloc.c
#include "apue.h"
#include <errno.h>
#include <limits.h>
#ifdef PATH_MAX
static int pathmax = PATH_MAX;
#else
static int pathmax = 0;
#endif
#define SUSV3 200112L
static long posix_version = 0;
/* If PATH_MAX is indeterminate, no guarantee this is adequate */
#define PATH_MAX_GUESS 1024
char *
path_alloc(int *sizep) /* also return allocated size, if nonnull */
{
char *ptr;
int size;
if (posix_version == 0)
posix_version = sysconf(_SC_VERSION);
if (pathmax == 0) { /* first time through */
errno = 0;
if ((pathmax = pathconf("/", _PC_PATH_MAX)) < 0) {
if (errno == 0)
pathmax = PATH_MAX_GUESS; /* it's indeterminate */
else
err_sys("pathconf error for _PC_PATH_MAX");
} else {
pathmax++; /* add one since it's relative to root */
}
}
if (posix_version < SUSV3)
size = pathmax + 1;
else
size = pathmax;
if ((ptr = malloc(size)) == NULL)
err_sys("malloc error for pathname");
if (sizep != NULL)
*sizep = size;
return(ptr);
}
// gcc -c -D_GNU_SOURCE -Iinclude path_alloc.c -o pathfunc.o
$
$ cd include
$ ls
apue.h
$ cat apue.h
/* Our own header, to be included before all standard system headers */
#ifndef _APUE_H
#define _APUE_H
#if defined(SOLARIS)
#define _XOPEN_SOURCE 500 /* Single UNIX Specification, Version 2 for
Solaris 9 */
#define CMSG_LEN(x) _CMSG_DATA_ALIGN(sizeof(struct cmsghdr)+(x))
#elif !defined(BSD)
#define _XOPEN_SOURCE 600 /* Single UNIX Specification, Version 3 */
#endif
#include <sys/types.h> /* some systems still require this */
#include <sys/stat.h>
#include <sys/termios.h> /* for winsize */
#ifndef TIOCGWINSZ
#include <sys/ioctl.h>
#endif
#include <stdio.h> /* for convenience */
#include <stdlib.h> /* for convenience */
#include <stddef.h> /* for offsetof */
#include <string.h> /* for convenience */
#include <unistd.h> /* for convenience */
#include <signal.h> /* for SIG_ERR */
#define MAXLINE 4096 /* max line length */
/*
* Default file access permissions for new files.
*/
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
/*
* Default permissions for new directories.
*/
#define DIR_MODE (FILE_MODE | S_IXUSR | S_IXGRP | S_IXOTH)
typedef void Sigfunc(int); /* for signal handlers */
#if defined(SIG_IGN) && !defined(SIG_ERR)
#define SIG_ERR ((Sigfunc *)-1)
#endif
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
/*
* Prototypes for our own functions.
*/
char *path_alloc(int *); /* {Prog pathalloc} */
long open_max(void); /* {Prog openmax} */
void clr_fl(int, int); /* {Prog setfl} */
void set_fl(int, int); /* {Prog setfl} */
void pr_exit(int); /* {Prog prexit} */
void pr_mask(const char *); /* {Prog prmask} */
Sigfunc *signal_intr(int, Sigfunc *); /* {Prog signal_intr_function} */
int tty_cbreak(int); /* {Prog raw} */
int tty_raw(int); /* {Prog raw} */
int tty_reset(int); /* {Prog raw} */
void tty_atexit(void); /* {Prog raw} */
#ifdef ECHO /* only if <termios.h> has been included */
struct termios *tty_termios(void); /* {Prog raw} */
#endif
void sleep_us(unsigned int); /* {Ex sleepus} */
ssize_t readn(int, void *, size_t); /* {Prog readn_writen} */
ssize_t writen(int, const void *, size_t); /* {Prog readn_writen} */
void daemonize(const char *); /* {Prog daemoninit} */
int s_pipe(int *); /* {Progs streams_spipe sock_spipe} */
int recv_fd(int, ssize_t (*func)(int,
const void *, size_t));/* {Progs recvfd_streams
recvfd_sockets} */
int send_fd(int, int); /* {Progs sendfd_streams sendfd_sockets} */
int send_err(int, int,
const char *); /* {Prog senderr} */
int serv_listen(const char *); /* {Progs servlisten_streams
servlisten_sockets} */
int serv_accept(int, uid_t *); /* {Progs servaccept_streams
servaccept_sockets} */
int cli_conn(const char *); /* {Progs cliconn_streams cliconn_sockets} */
int buf_args(char *, int (*func)(int,
char **)); /* {Prog bufargs} */
int ptym_open(char *, int); /* {Progs3 ptyopen_streams ptyopen_bsd
ptyopen_linux} */
int ptys_open(char *); /* {Progs3 ptyopen_streams ptyopen_bsd
ptyopen_linux} */
#ifdef TIOCGWINSZ
pid_t pty_fork(int *, char *, int, const struct termios *,
const struct winsize *); /* {Prog ptyfork} */
#endif
int lock_reg(int, int, int, off_t, int, off_t); /* {Prog lockreg} */
#define read_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len))
#define readw_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len))
#define write_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len))
#define writew_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len))
#define un_lock(fd, offset, whence, len) \
lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len))
pid_t lock_test(int, int, off_t, int, off_t); /* {Prog locktest} */
#define is_read_lockable(fd, offset, whence, len) \
(lock_test((fd), F_RDLCK, (offset), (whence), (len)) == 0)
#define is_write_lockable(fd, offset, whence, len) \
(lock_test((fd), F_WRLCK, (offset), (whence), (len)) == 0)
void err_dump(const char *, ...); /* {App misc_source} */
void err_msg(const char *, ...);
void err_quit(const char *, ...);
void err_exit(int, const char *, ...);
void err_ret(const char *, ...);
void err_sys(const char *, ...);
void log_msg(const char *, ...); /* {App misc_source} */
void log_open(const char *, int, int);
void log_quit(const char *, ...);
void log_ret(const char *, ...);
void log_sys(const char *, ...);
void TELL_WAIT(void); /* parent/child from {Sec race_conditions} */
void TELL_PARENT(pid_t);
void TELL_CHILD(pid_t);
void WAIT_PARENT(void);
void WAIT_CHILD(void);
#endif /* _APUE_H */
$
|
|
0
|
|
|
|
Reply
|
Uno
|
8/31/2010 6:34:38 AM
|
|
Gib Bogle wrote:
> Hi Richard, a couple of minor mods and this is working with
> MinGW/gcc/gfortran. No need for __stdcall, or uppercase, but
> underscores need to be appended to C function names.
> Thanks a lot.
> Gib
Post it, bogart!
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
8/31/2010 6:37:09 AM
|
|
I'll post the code in three pieces.
Here is tcpc.c
/* $Id: tcpc.c,v 1.1.1.1 2003/03/06 19:38:16 maine Exp $ */
/* $Name: $ */
/* tcpc.c
* tcp communication routines for getData.
*
* The read/write/close here are used in both clients and servers.
* These routines must not directly or indirectly reference the
* fortran write_error_msg routine because some versions of it
* may call these routines.
*
* The routines for opening a tcp connection are different for
* clients and servers. Furthermore, the client one may
* call write_error_msg. Thus those routines are in separate files.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for MS Windows MS C and CVF.
* Differences from nag version are:
* Different system include files.
* Different procedure name mangling convention in the defines
* Specify __stdcall in procedure headers.
* Use closesocket instead of close.
* Used SOCKET_ERROR to test for errors per MS docs,
* though the bsd-style test did appear to work.
*
* 11 Oct 91, Richard Maine: Version 1.0
* 17 Jul 01, Richard Maine: MS Windows port.
*/
#include <winsock.h>
/* Defines to make routines fortran-callable */
#define tcp_close tcp_close_
#define tcp_read tcp_read_
#define tcp_write tcp_write_
void tcp_close(sock)
int *sock;
{
shutdown(*sock,2); /* clears any pending i/o. */
closesocket(*sock);
}
/* Write to a tcp socket. */
void tcp_write(sock, buf, buflen, error)
int *sock, *buflen, *error;
char *buf;
{
int i;
i = send(*sock,buf,*buflen,0);
*error = (i == SOCKET_ERROR);
}
/* Read from a tcp socket. */
void tcp_read(sock, buf, buflen, error)
int *sock, *buflen, *error;
char *buf;
{
int nleft, nread;
char *bufptr;
bufptr = buf;
nleft = *buflen;
*error = 1;
//while (nleft > 0)
// {
nread = recv(*sock, bufptr, nleft, 0);
if (nread == SOCKET_ERROR) return;
// nleft -= nread;
// bufptr += nread;
// }
*error = 0;
}
|
|
0
|
|
|
|
Reply
|
Gib
|
8/31/2010 10:13:42 AM
|
|
client.c
/* Connect to a specified host and port.
* Return a socket number in sock. */
void tcp_connect(sock, host_name, host_name_len, port, error)
int *sock, *port, *error;
char *host_name;
int host_name_len;
{
char cstring[129];
int i;
struct hostent *host_ent;
struct sockaddr_in host;
int keepalive;
char *from, *to;
WORD wver;
WSADATA wsaData;
*error = 1;
/* Start up Windows sockets */
if (started == 0) {
started = 1;
wver = MAKEWORD(2,0);
i = WSAStartup(wver, &wsaData);
printf("did WSAStartup\n");
}
/* Make host_name into a c string */
for (i=0; (i<host_name_len) && (i<127) && (host_name[i] != ' '); i++)
cstring[i] = host_name[i];
cstring[i] = '\0';
/* Find the host IP address */
host.sin_family = AF_INET;
host_ent = gethostbyname(cstring);
if (host_ent==0) {printerrormsg("Can't find host address."); return;}
/* Avoid bcopy/memcpy dependence. */
from = (char *) host_ent->h_addr;
to = (char *) &host.sin_addr;
for (i=0; i<host_ent->h_length; i++) *to++ = *from++;
/* Set the port */
host.sin_port = htons((unsigned short int) *port);
/* Create a socket */
*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (*sock==INVALID_SOCKET) {printerrormsg("Can't create socket.");
return;
}
/* Attempt connection */
if (connect(*sock, (struct sockaddr *) &host, sizeof(host)) == SOCKET_ERROR)
{printerrormsg("Connect failed."); closesocket(*sock); return;}
/* Enable keepalive packets to check socket connection. */
keepalive = 1;
setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, 4);
*error = 0;
}
|
|
0
|
|
|
|
Reply
|
Gib
|
8/31/2010 10:15:03 AM
|
|
This test program was designed to communicate with a test TCP server program
(Python). It tests sending and receiving messages. At least you can see how
the various C functions are called.
The C code is compiled with gcc:
gcc -c tcpc.c
gcc -c client.c
then built with the Fortran code, linking libws2_32.a:
gfortran fortest.f90 tcpc.o client.o -lws2_32
fortest.f90
! To test Richard Maine's TCP C code
subroutine write_error_sub(msg)
character*(*) :: msg
write(*,*) msg
end subroutine
program main
implicit none
integer :: sock, host_name_len, msglen, ndata, error
integer :: port = 5000
character*(128) :: host_name = 'localhost'
character*(128) :: msg
character*(128) :: data
character :: ans
!external :: tcp_connect
host_name_len = len_trim(host_name)
write(*,*) 'call tcp_connect'
call tcp_connect(sock, host_name, host_name_len, port, error)
write(*,*) 'error: ',error
if (error == 0) then
write(*,*) 'Connection succeeded'
msg = "Hello from fortest"
msglen = len_trim(msg)
call tcp_write(sock,msg,msglen,error)
write(*,*) "sent msg: error: ",error
do
ndata = 128
data = ' '
call tcp_read(sock,data,ndata,error)
if (error /= 0) then
write(*,*) 'tcp_read error: ',error
exit
endif
if ( data(1:2) == 'q ' .or. data(1:2) == 'Q ') then
write(*,*) 'got Q'
exit
else
write(*,*) "RECEIVED: " , trim(data)
write(*,*) "Input text to send ( q or Q to Quit):"
read(*,'(a)') msg
msglen = len_trim(msg)
call tcp_write(sock,msg,msglen,error)
if (msg(1:2) == 'Q ' .or. msg(1:2) == 'q ') then
exit
endif
endif
enddo
call tcp_close(sock)
endif
end program
|
|
0
|
|
|
|
Reply
|
Gib
|
8/31/2010 10:25:07 AM
|
|
On 31 aug, 12:25, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> This test program was designed to communicate with a test TCP server prog=
ram
> (Python). =A0It tests sending and receiving messages. =A0At least you can=
see how
> the various C functions are called.
>
> The C code is compiled with gcc:
> gcc -c tcpc.c
> gcc -c client.c
> then built with the Fortran code, linking libws2_32.a:
> gfortran fortest.f90 tcpc.o client.o -lws2_32
>
> fortest.f90
>
> ! To test Richard Maine's TCP C code
>
> subroutine write_error_sub(msg)
> character*(*) :: msg
> write(*,*) msg
> end subroutine
>
> program main
> implicit none
> integer :: sock, host_name_len, msglen, ndata, error
> integer :: port =3D 5000
> character*(128) :: host_name =3D 'localhost'
> character*(128) :: msg
> character*(128) :: data
> character :: ans
> !external :: tcp_connect
>
> host_name_len =3D len_trim(host_name)
> write(*,*) 'call tcp_connect'
> call tcp_connect(sock, host_name, host_name_len, port, error)
> write(*,*) 'error: ',error
> if (error =3D=3D 0) then
> =A0 =A0 =A0write(*,*) 'Connection succeeded'
> =A0 =A0 =A0msg =3D "Hello from fortest"
> =A0 =A0 =A0msglen =3D len_trim(msg)
> =A0 =A0 =A0call tcp_write(sock,msg,msglen,error)
> =A0 =A0 =A0write(*,*) "sent msg: error: ",error
> =A0 =A0 =A0do
> =A0 =A0 =A0 =A0 =A0ndata =3D 128
> =A0 =A0 =A0 =A0 =A0data =3D ' '
> =A0 =A0 =A0 =A0 =A0call tcp_read(sock,data,ndata,error)
> =A0 =A0 =A0 =A0 =A0if (error /=3D 0) then
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) 'tcp_read error: ',error
> =A0 =A0 =A0 =A0 =A0 =A0 =A0exit
> =A0 =A0 =A0 =A0 =A0endif
> =A0 =A0 =A0 =A0 =A0if ( data(1:2) =3D=3D 'q ' .or. data(1:2) =3D=3D 'Q ')=
then
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) 'got Q'
> =A0 =A0 =A0 =A0 =A0 =A0 =A0exit
> =A0 =A0 =A0 =A0 =A0else
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) "RECEIVED: " , trim(data)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) "Input text to send ( q or Q to Qui=
t):"
> =A0 =A0 =A0 =A0 =A0 =A0 =A0read(*,'(a)') msg
> =A0 =A0 =A0 =A0 =A0 =A0 =A0msglen =3D len_trim(msg)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0call tcp_write(sock,msg,msglen,error)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0if (msg(1:2) =3D=3D 'Q ' .or. msg(1:2) =3D=3D =
'q ') then
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit
> =A0 =A0 =A0 =A0 =A0 =A0 =A0endif
> =A0 =A0 =A0 =A0 =A0endif
> =A0 =A0 =A0enddo
> =A0 =A0 =A0call tcp_close(sock)
> endif
> end program
Interesting - would it be alright with you, Gib and Richard, if I put
this
(with due credits) in my Flibs project? (I will try and make it a bit
more
general wrt Fortran/C interfacing)
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
8/31/2010 12:07:09 PM
|
|
Arjen Markus <arjen.markus895@gmail.com> wrote:
> Interesting - would it be alright with you, Gib and Richard, if I put
> this
> (with due credits) in my Flibs project? (I will try and make it a bit
> more
> general wrt Fortran/C interfacing)
I have no objection. You might find that you want to fiddle it a little
for general use, as there are some aspects I simplified, but that's up
to you. I see that Gib did a litle of that kind of thing, which is fine.
Notably, my apps had higher-level protocol that established exactly how
much data to expect from a read, which is why I had the loop reading
fragments until it got that much.
Note that the WSAStartup thing is purely Windows. That's a piece I would
never have guessed just by fiddling.
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
|
|
0
|
|
|
|
Reply
|
nospam
|
8/31/2010 4:18:08 PM
|
|
Arjen Markus wrote:
> Interesting - would it be alright with you, Gib and Richard, if I put
> this
> (with due credits) in my Flibs project? (I will try and make it a bit
> more
> general wrt Fortran/C interfacing)
>
> Regards,
>
> Arjen
That's perfectly OK with me. Maybe next time someone who searches, like me, for
gfortran and sockets on Windows, will find this. As you point out, it needs to
be made a bit more general.
If you felt inspired, you might extend it to handle the server case (just a bit
more complicated). Like Richard I need only the client at this stage.
Gib
|
|
0
|
|
|
|
Reply
|
Gib
|
8/31/2010 10:28:53 PM
|
|
Here is some more C code that I found online, which shows how to handle both
Windows and Unix environments, and also how to set up a TCP server. I don't
understand the significance of the Winsock.h/Winsock2.h distinction.
sockets.c (contributed by ReyBrujo)
#ifdef WIN32
#include <Winsock2.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#define closesocket close
#endif
#include <stdio.h>
#include <stdlib.h>
#define D_PORT 4344
#define D_HOST "localhost"
#define D_QUEUE 32
#define D_SOCKETS 16
#define D_INFO 256
int main(int argc, char **argv) {
struct timeval tv;
struct sockaddr_in addr;
struct hostent *host;
unsigned int descriptor;
int result;
int index;
int cycle = 0;
int delay = 0;
unsigned int sockets[D_SOCKETS];
int sockets_index = 0;
unsigned int maximum;
char buffer[D_INFO];
fd_set input;
/* read the delay if any */
if (argc > 1)
delay = atol(argv[1]);
else
delay = 0;
#ifdef WIN32
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
#endif /* WIN32 */
/* create a socket */
descriptor = socket(PF_INET, SOCK_STREAM, 0);
if (descriptor == -1) {
perror("socket");
return (1);
}
/* get information about the host */
memset(&addr, 0, sizeof(addr));
host = gethostbyname(D_HOST);
if (host == NULL) {
perror("gethostbyname");
closesocket(descriptor);
#ifdef WIN32
WSACleanup();
#endif
return (1);
}
/* bind the socket to an address and port */
memcpy(&addr.sin_addr, host->h_addr_list[0], sizeof(host->h_addr_list[0]));
addr.sin_family = AF_INET;
addr.sin_port = htons(D_PORT);
result = bind(descriptor, (struct sockaddr *)&addr, sizeof(addr));
if (result == -1) {
perror("bind");
closesocket(descriptor);
#ifdef WIN32
WSACleanup();
#endif
return (1);
}
/* listen for connections */
result = listen(descriptor, D_QUEUE);
if (result == -1) {
perror("listen");
closesocket(descriptor);
#ifdef WIN32
WSACleanup();
#endif
return (1);
}
memset(sockets, 0, sizeof(sockets));
maximum = descriptor;
result = 0;
while (result != -1) {
FD_ZERO(&input);
FD_SET(descriptor, &input);
for (result = 0; result < sockets_index; result++)
FD_SET(sockets[result], &input);
tv.tv_sec = delay;
tv.tv_usec = 0;
if (delay == -1)
result = select(maximum + 1, &input, NULL, NULL, NULL);
else
result = select(maximum + 1, &input, NULL, NULL, &tv);
switch (result) {
/* error in select */
case -1:
perror("select");
break;
/* nothing to process */
case 0:
break;
/* a number of sockets are ready for reading */
default:
/* check if the descriptor set is our listening one */
if (FD_ISSET(descriptor , &input)) {
sockets[sockets_index] = accept(descriptor, NULL, NULL);
if (sockets[sockets_index] == -1) {
perror("accept");
}
else {
if (sockets[sockets_index] > maximum)
maximum = sockets[sockets_index];
sockets_index++;
}
}
/* one of the sockets is sending data. Find it */
else {
for (index = 0; index < sockets_index; index++) {
if (FD_ISSET(sockets[index], &input)) {
memset(buffer, 0, sizeof(buffer));
/* read information from socket */
result = recv(sockets[index], buffer,
sizeof(buffer), 0);
if (result == -1)
perror("recv");
else
printf("Received %d bytes from descriptor %d:
%s\n", result, sockets[index], buffer);
}
}
}
}
printf("%d\r", cycle++);
}
for (result = 0; result < sockets_index; result++) {
closesocket(sockets[sockets_index]);
}
closesocket(descriptor);
#ifdef WIN32
WSACleanup();
#endif
return (0);
}
|
|
0
|
|
|
|
Reply
|
Gib
|
8/31/2010 10:38:20 PM
|
|
Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
> If you felt inspired, you might extend it to handle the server case (just
> a bit more complicated). Like Richard I need only the client at this
> stage.
Should anyone care, here's the comparable stuff I had for my servers. I
did not port this to Windows, and it hasn't even been used on a wide
variety of Unix systems - just the few we were using for servers. It
started out on SunOS 4, ported to Solaris 2, and I think I recall
experimenting with this on Linux.
I wrote this stuff about 20 years ago. It is currently still in wide use
at NASA Dryden (and I don't see any sign of that changing soon).
/* $Id: serverc.c,v 1.2 2005/12/28 22:49:13 maine Exp $ */
/* $Name: $ */
/* serverc.c
* listen for a connection attempt to a getData server.
*
* The read/write/close routines are used in both clients and servers
* and are in a separate file.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for SunOS 4.1.x with NAG f90 1.2.
* Note, specifically that Sun Fortran puts character length
* information at the end of the argument list instead of right
* after the corresponding arguments.
*
* 28 Jun 93, Richard Maine: Version 1.5.
* 19 Nov 96, Richard Maine: casts to avoid gcc warnings.
* 16 Apr 98, Richard Maine: fix sol2 sigchld handling.
* 2 Dec 98, Richard Maine: fix addr len in gethostbyaddr.
* 21 Dec 05, Richard Maine: avoid sun-specific in_addr structure.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
/* Defines to make routines fortran-callable */
#define tcp_listen tcp_listen_
/* handler for SIGCHLD signal
* Needed so that child processes can be decently buried when they die.
* USG systems may need signal handler reestablished after use?
* but we are not using this for SOL2. */
void child_handler(sig)
int sig;
{
wait3(0, WNOHANG, 0);
return;
}
/* Listen for connection attempts on the specified port.
* Fork a child for each connection.
* Return connection information.
* Normal return value for child is 0.
* Positive return values are errors in parent.
* Negative return values are errors in child. */
void tcp_listen(port ,sock, client_ip, client_port, client_name,
server_ip, connect_num, error, client_name_len)
int *port, *sock, *client_port, *connect_num, *error;
int client_ip[3], server_ip[3];
char *client_name;
int client_name_len;
{
struct sockaddr_in server_addr, client_addr;
int lsock; /* Socket for listening */
int addr_size = sizeof(server_addr);
int addr_len;
int pid;
int keepalive;
struct hostent *host_ent;
int i;
unsigned char *ip_byte;
/* Divorce ourselves from controlling terminal. */
/* See Stevens, Chapter 13. */
if (fork()>0) exit(0); /* Parent returns; child continues. */
setsid(); /* Become session leader */
/* Create a socket, bind it to specified port and listen. */
*connect_num = 0;
lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lsock<0) {*error=1; return;}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(*port);
if (bind(lsock, (struct sockaddr *) &server_addr, addr_size) < 0)
{*error=2; return;}
if (listen(lsock,5) < 0) {*error=3; return;}
/* Accept connections and fork server instances. */
#ifdef SOL2
signal(SIGCHLD, SIG_IGN);
#else
signal(SIGCHLD, child_handler);
#endif
for (;;) {
addr_len = addr_size;
*sock = accept(lsock, (struct sockaddr *) &client_addr, &addr_len);
/* For reasons unclear to me, we get a lot of accept failures. */
/* perror says something about interrupted system call. */
/* We just ignore them for now. */
/* Can count them by moving the connect_num incrementation. */
if (*sock<0) continue; /* Connect failed */
*connect_num = *connect_num + 1;
/* Child process. Return connection data. */
if (fork()==0) {
close(lsock);
ip_byte = (unsigned char *) &client_addr.sin_addr;
client_ip[0] = *(ip_byte++);
client_ip[1] = *(ip_byte++);
client_ip[2] = *(ip_byte++);
client_ip[3] = *(ip_byte++);
*client_port = ntohs(client_addr.sin_port);
host_ent = gethostbyaddr((char *) &client_addr.sin_addr.s_addr,
sizeof(client_addr.sin_addr.s_addr), AF_INET);
for (i=0; i<client_name_len; i++) client_name[i] = ' ';
if (host_ent) {
for (i=0; (i<client_name_len) && (host_ent->h_name[i]!=0); i++)
client_name[i] = host_ent->h_name[i];
}
addr_len = addr_size;
getsockname(*sock, (struct sockaddr *) &server_addr, &addr_len);
ip_byte = (unsigned char *) &server_addr.sin_addr;
server_ip[0] = *(ip_byte++);
server_ip[1] = *(ip_byte++);
server_ip[2] = *(ip_byte++);
server_ip[3] = *(ip_byte++);
signal(SIGCHLD, SIG_DFL);
/* We want an error return from writes to closed sockets so
* we can shut down cleanly and log the termination.
* Without this, we quitely die.
*/
signal (SIGPIPE, SIG_IGN);
/* Enable keepalive packets to check socket connection. */
keepalive = 1;
setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive,
4);
/* Allow SIGIO/SIGURG to signal this process. */
/* Not currently needed. Retain as comment. */
/* if (fcntl(*sock, F_SETOWN, getpid()) < 0) return(-1); */
/* if (fcntl(*sock, F_SETFL, FASYNC) < 0) return (-2); */
*error=0;
return;
}
/* Parent process. Loop back for further connections. */
close(*sock);
}
}
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
|
|
0
|
|
|
|
Reply
|
nospam
|
8/31/2010 11:05:31 PM
|
|
On 1 sep, 00:38, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> Here is some more C code that I found online, which shows how to handle b=
oth
> Windows and Unix environments, and also how to set up a TCP server. =A0I =
don't
> understand the significance of the Winsock.h/Winsock2.h distinction.
>
> sockets.c (contributed by ReyBrujo)
>
> #ifdef WIN32
> =A0 =A0 #include <Winsock2.h>
> #else
> =A0 =A0 =A0#include <sys/types.h>
> =A0 =A0 =A0#include <sys/socket.h>
>
> =A0 =A0 =A0#define closesocket close
> #endif
> #include <stdio.h>
> #include <stdlib.h>
>
> #define D_PORT =A0 =A0 =A04344
> #define D_HOST =A0 =A0 =A0"localhost"
> #define D_QUEUE =A0 =A0 32
> #define D_SOCKETS =A0 16
> #define D_INFO =A0 =A0 =A0256
>
> int main(int argc, char **argv) {
> =A0 =A0 =A0struct timeval tv;
> =A0 =A0 =A0struct sockaddr_in addr;
> =A0 =A0 =A0struct hostent *host;
> =A0 =A0 =A0unsigned int descriptor;
> =A0 =A0 =A0int result;
> =A0 =A0 =A0int index;
> =A0 =A0 =A0int cycle =3D 0;
> =A0 =A0 =A0int delay =3D 0;
> =A0 =A0 =A0unsigned int sockets[D_SOCKETS];
> =A0 =A0 =A0int sockets_index =3D 0;
> =A0 =A0 =A0unsigned int maximum;
> =A0 =A0 =A0char buffer[D_INFO];
> =A0 =A0 =A0fd_set input;
>
> =A0 =A0 =A0/* =A0read the delay if any =A0*/
> =A0 =A0 =A0if (argc > 1)
> =A0 =A0 =A0 =A0 =A0delay =3D atol(argv[1]);
> =A0 =A0 =A0else
> =A0 =A0 =A0 =A0 =A0delay =3D 0;
>
> #ifdef WIN32
> =A0 =A0 =A0WSADATA wsaData;
> =A0 =A0 =A0WSAStartup(MAKEWORD(2, 2), &wsaData);
> #endif =A0/* =A0WIN32 =A0*/
>
> =A0 =A0 =A0/* =A0create a socket =A0*/
> =A0 =A0 =A0descriptor =3D socket(PF_INET, SOCK_STREAM, 0);
> =A0 =A0 =A0if (descriptor =3D=3D -1) {
> =A0 =A0 =A0 =A0 =A0perror("socket");
> =A0 =A0 =A0 =A0 =A0return (1);
> =A0 =A0 =A0}
>
> =A0 =A0 =A0/* =A0get information about the host =A0*/
> =A0 =A0 =A0memset(&addr, 0, sizeof(addr));
> =A0 =A0 =A0host =3D gethostbyname(D_HOST);
> =A0 =A0 =A0if (host =3D=3D NULL) {
> =A0 =A0 =A0 =A0 =A0perror("gethostbyname");
> =A0 =A0 =A0 =A0 =A0closesocket(descriptor);
> #ifdef WIN32
> =A0 =A0 =A0 =A0 =A0WSACleanup();
> #endif
> =A0 =A0 =A0 =A0 =A0return (1);
> =A0 =A0 =A0}
>
> =A0 =A0 =A0/* =A0bind the socket to an address and port =A0*/
> =A0 =A0 =A0memcpy(&addr.sin_addr, host->h_addr_list[0], sizeof(host->h_ad=
dr_list[0]));
> =A0 =A0 =A0addr.sin_family =3D AF_INET;
> =A0 =A0 =A0addr.sin_port =A0 =3D htons(D_PORT);
> =A0 =A0 =A0result =3D bind(descriptor, (struct sockaddr *)&addr, sizeof(a=
ddr));
> =A0 =A0 =A0if (result =3D=3D -1) {
> =A0 =A0 =A0 =A0 =A0perror("bind");
> =A0 =A0 =A0 =A0 =A0closesocket(descriptor);
> #ifdef WIN32
> =A0 =A0 =A0 =A0 =A0WSACleanup();
> #endif
> =A0 =A0 =A0 =A0 =A0return (1);
> =A0 =A0 =A0}
>
> =A0 =A0 =A0/* =A0listen for connections =A0*/
> =A0 =A0 =A0result =3D listen(descriptor, D_QUEUE);
> =A0 =A0 =A0if (result =3D=3D -1) {
> =A0 =A0 =A0 =A0 =A0perror("listen");
> =A0 =A0 =A0 =A0 =A0closesocket(descriptor);
> #ifdef WIN32
> =A0 =A0 =A0 =A0 =A0WSACleanup();
> #endif
> =A0 =A0 =A0 =A0 =A0return (1);
> =A0 =A0 =A0}
>
> =A0 =A0 =A0memset(sockets, 0, sizeof(sockets));
> =A0 =A0 =A0maximum =3D descriptor;
>
> =A0 =A0 =A0result =3D 0;
> =A0 =A0 =A0while (result !=3D -1) {
> =A0 =A0 =A0 =A0 =A0FD_ZERO(&input);
> =A0 =A0 =A0 =A0 =A0FD_SET(descriptor, &input);
> =A0 =A0 =A0 =A0 =A0for (result =3D 0; result < sockets_index; result++)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0FD_SET(sockets[result], &input);
>
> =A0 =A0 =A0 =A0 =A0tv.tv_sec =A0=3D delay;
> =A0 =A0 =A0 =A0 =A0tv.tv_usec =3D 0;
> =A0 =A0 =A0 =A0 =A0if (delay =3D=3D -1)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0result =3D select(maximum + 1, &input, NULL, N=
ULL, NULL);
> =A0 =A0 =A0 =A0 =A0else
> =A0 =A0 =A0 =A0 =A0 =A0 =A0result =3D select(maximum + 1, &input, NULL, N=
ULL, &tv);
> =A0 =A0 =A0 =A0 =A0switch (result) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0/* =A0error in select =A0*/
> =A0 =A0 =A0 =A0 =A0 =A0 =A0case -1:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0perror("select");
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 break;
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0/* =A0nothing to process =A0*/
> =A0 =A0 =A0 =A0 =A0 =A0 =A0case 0:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0break;
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0/* =A0a number of sockets are ready for readin=
g =A0*/
> =A0 =A0 =A0 =A0 =A0 =A0 =A0default:
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* =A0check if the descriptor set is o=
ur listening one =A0*/
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (FD_ISSET(descriptor , &input)) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sockets[sockets_index] =3D acc=
ept(descriptor, NULL, NULL);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (sockets[sockets_index] =3D=
=3D -1) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0perror("accept");
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (sockets[sockets_in=
dex] > maximum)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0maximum =3D so=
ckets[sockets_index];
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0sockets_index++;
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* =A0one of the sockets is sending da=
ta. Find it =A0*/
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0for (index =3D 0; index < sock=
ets_index; index++) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (FD_ISSET(sockets[i=
ndex], &input)) {
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0memset(buffer,=
0, sizeof(buffer));
>
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0/* =A0read inf=
ormation from socket =A0*/
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0result =3D rec=
v(sockets[index], buffer,
> sizeof(buffer), 0);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if (result =3D=
=3D -1)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0perror=
("recv");
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0else
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0printf=
("Received %d bytes from descriptor %d:
> %s\n", result, sockets[index], buffer);
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
> =A0 =A0 =A0 =A0 =A0}
>
> =A0 =A0 =A0 =A0 =A0printf("%d\r", cycle++);
> =A0 =A0 =A0}
>
> =A0 =A0 =A0for (result =3D 0; result < sockets_index; result++) {
> =A0 =A0 =A0 =A0 =A0closesocket(sockets[sockets_index]);
> =A0 =A0 =A0}
>
> =A0 =A0 =A0closesocket(descriptor);
> #ifdef WIN32
> =A0 =A0 =A0WSACleanup();
> #endif
>
> =A0 =A0 =A0return (0);
>
>
>
> }- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -
Wonderful, this looks very promising. The tough part is getting
started
with such a programming task, but with these implementations it ought
to be lighter work. (Will keep you up-to-date as to the progress I
make
with it - which may be slow ;))
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
9/1/2010 6:57:53 AM
|
|
On 8/31/2010 3:25 AM, Gib Bogle wrote:
> This test program was designed to communicate with a test TCP server
> program (Python). It tests sending and receiving messages. At least you
> can see how the various C functions are called.
>
> The C code is compiled with gcc:
> gcc -c tcpc.c
> gcc -c client.c
> then built with the Fortran code, linking libws2_32.a:
> gfortran fortest.f90 tcpc.o client.o -lws2_32
>
Where did you find that library? This is brand spanking new from
equation.com, which I thought were mingw-based:
Dir for this script: C:\eq\source\
C:\eq\source>dir
Volume in drive C has no label.
Volume Serial Number is 942A-AD55
Directory of C:\eq\source
09/01/2010 03:35 PM <DIR> .
09/01/2010 03:35 PM <DIR> ..
05/14/2010 02:19 PM 218 run1.bat
1 File(s) 218 bytes
2 Dir(s) 32,663,818,240 bytes free
C:\eq\source>gfortran anything
gfortran: anything: No such file or directory
C:\eq\source>cd ..
C:\eq>dir
Volume in drive C has no label.
Volume Serial Number is 942A-AD55
Directory of C:\eq
09/01/2010 03:32 PM <DIR> .
09/01/2010 03:32 PM <DIR> ..
09/01/2010 03:35 PM 823 32 bit.lnk
09/01/2010 03:26 PM <DIR> bin
08/04/2004 07:00 AM 388,608 cmd.exe
09/01/2010 03:26 PM <DIR> i686-pc-mingw32
09/01/2010 03:26 PM <DIR> include
09/01/2010 03:26 PM <DIR> lib
09/01/2010 03:26 PM <DIR> libexec
09/01/2010 03:26 PM <DIR> manual
09/01/2010 03:26 PM <DIR> share
09/01/2010 03:35 PM <DIR> source
2 File(s) 389,431 bytes
10 Dir(s) 32,663,818,240 bytes free
C:\eq>cd include
C:\eq\include>dir
Volume in drive C has no label.
Volume Serial Number is 942A-AD55
Directory of C:\eq\include
09/01/2010 03:26 PM <DIR> .
09/01/2010 03:26 PM <DIR> ..
09/01/2010 03:26 PM 13,917 ansidecl.h
09/01/2010 03:26 PM 196,800 bfd.h
09/01/2010 03:26 PM 29,513 bfdlink.h
09/01/2010 03:26 PM <DIR> c++
09/01/2010 03:26 PM 16,736 dis-asm.h
09/01/2010 03:26 PM 2,198 symcat.h
5 File(s) 259,164 bytes
3 Dir(s) 32,663,818,240 bytes free
C:\eq\include>cd ..
C:\eq>cd lib
C:\eq\lib>dir
Volume in drive C has no label.
Volume Serial Number is 942A-AD55
Directory of C:\eq\lib
09/01/2010 03:26 PM <DIR> .
09/01/2010 03:26 PM <DIR> ..
09/01/2010 03:26 PM <DIR> gcc
09/01/2010 03:26 PM 823,008 libbfd.a
09/01/2010 03:26 PM 933 libbfd.la
09/01/2010 03:26 PM 5,338,594 libgfortran.a
09/01/2010 03:26 PM 1,103 libgfortran.la
09/01/2010 03:26 PM 292,254 libgomp.a
09/01/2010 03:26 PM 1,091 libgomp.la
09/01/2010 03:26 PM 154 libgomp.spec
09/01/2010 03:26 PM 774,684 libiberty.a
09/01/2010 03:26 PM 609,982 libopcodes.a
09/01/2010 03:26 PM 945 libopcodes.la
09/01/2010 03:26 PM 36,262 libssp.a
09/01/2010 03:26 PM 1,088 libssp.la
09/01/2010 03:26 PM 2,020 libssp_nonshared.a
09/01/2010 03:26 PM 1,118 libssp_nonshared.la
09/01/2010 03:26 PM 8,672,836 libstdc++.a
09/01/2010 03:26 PM 2,400 libstdc++.a-gdb.py
09/01/2010 03:26 PM 943 libstdc++.la
09/01/2010 03:26 PM 647,358 libsupc++.a
09/01/2010 03:26 PM 942 libsupc++.la
19 File(s) 17,207,715 bytes
3 Dir(s) 32,663,818,240 bytes free
C:\eq\lib>cd gcc
C:\eq\lib\gcc>dir
Volume in drive C has no label.
Volume Serial Number is 942A-AD55
Directory of C:\eq\lib\gcc
09/01/2010 03:26 PM <DIR> .
09/01/2010 03:26 PM <DIR> ..
09/01/2010 03:26 PM <DIR> i686-pc-mingw32
0 File(s) 0 bytes
3 Dir(s) 32,663,818,240 bytes free
C:\eq\lib\gcc>cd i686-pc-mingw32
C:\eq\lib\gcc\i686-pc-mingw32>dir
Volume in drive C has no label.
Volume Serial Number is 942A-AD55
Directory of C:\eq\lib\gcc\i686-pc-mingw32
09/01/2010 03:26 PM <DIR> .
09/01/2010 03:26 PM <DIR> ..
09/01/2010 03:26 PM <DIR> 4.5.1
0 File(s) 0 bytes
3 Dir(s) 32,663,818,240 bytes free
C:\eq\lib\gcc\i686-pc-mingw32>cd 4.5.1
C:\eq\lib\gcc\i686-pc-mingw32\4.5.1>dir
Volume in drive C has no label.
Volume Serial Number is 942A-AD55
Directory of C:\eq\lib\gcc\i686-pc-mingw32\4.5.1
09/01/2010 03:26 PM <DIR> .
09/01/2010 03:26 PM <DIR> ..
09/01/2010 03:26 PM 966 crtbegin.o
09/01/2010 03:26 PM 734 crtend.o
09/01/2010 03:26 PM 3,503 crtfastmath.o
09/01/2010 03:26 PM <DIR> finclude
09/01/2010 03:26 PM <DIR> include
09/01/2010 03:26 PM <DIR> include-fixed
09/01/2010 03:26 PM <DIR> install-tools
09/01/2010 03:26 PM 550,218 libgcc.a
09/01/2010 03:26 PM 87,434 libgcov.a
09/01/2010 03:26 PM 4,350 libgfortranbegin.a
09/01/2010 03:26 PM 1,144 libgfortranbegin.la
7 File(s) 648,349 bytes
6 Dir(s) 32,663,818,240 bytes free
C:\eq\lib\gcc\i686-pc-mingw32\4.5.1>
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/1/2010 10:44:00 PM
|
|
Uno wrote:
> On 8/31/2010 3:25 AM, Gib Bogle wrote:
>> This test program was designed to communicate with a test TCP server
>> program (Python). It tests sending and receiving messages. At least you
>> can see how the various C functions are called.
>>
>> The C code is compiled with gcc:
>> gcc -c tcpc.c
>> gcc -c client.c
>> then built with the Fortran code, linking libws2_32.a:
>> gfortran fortest.f90 tcpc.o client.o -lws2_32
>>
>
> Where did you find that library? This is brand spanking new from
> equation.com, which I thought were mingw-based:
Did you install MinGW?
I've installed both mingw-4.4 and mingw64-4.5. In the 32-bit installation
libws2_32.a is in mingw-4.4\lib, while in the 64=bit version it's in
mingw64-4.5\x86_64-w64-mingw32 (unless I've made a typo). My gfortran is
installed as part of mingw64-4.5.
BTW I'm using Code:Blocks as the IDE, and I guess it's smart enough to find the
various libraries. I've just started playing with CB, and I'm quite impressed.
There is a bit of a funny trick to getting it to use gfortran, since in
principle it knows about only C compilers. You have to tell it to make a copy
of the GCC C compiler, then rename and customise that copy so it uses gfortran.
It isn't as powerful as MSVS, but it's doing the job for me so far. The great
thing, of course, is that it's free and multi-platform.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/2/2010 12:31:11 AM
|
|
On 2 sep, 02:31, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> Uno wrote:
> > On 8/31/2010 3:25 AM, Gib Bogle wrote:
> >> This test program was designed to communicate with a test TCP server
> >> program (Python). It tests sending and receiving messages. At least yo=
u
> >> can see how the various C functions are called.
>
> >> The C code is compiled with gcc:
> >> gcc -c tcpc.c
> >> gcc -c client.c
> >> then built with the Fortran code, linking libws2_32.a:
> >> gfortran fortest.f90 tcpc.o client.o -lws2_32
>
> > Where did you find that library? =A0This is brand spanking new from
> > equation.com, which I thought were mingw-based:
>
> Did you install MinGW?
> I've installed both mingw-4.4 and mingw64-4.5. =A0In the 32-bit installat=
ion
> libws2_32.a is in mingw-4.4\lib, while in the 64=3Dbit version it's in
> mingw64-4.5\x86_64-w64-mingw32 (unless I've made a typo). =A0My gfortran =
is
> installed as part of mingw64-4.5.
>
> BTW I'm using Code:Blocks as the IDE, and I guess it's smart enough to fi=
nd the
> various libraries. =A0I've just started playing with CB, and I'm quite im=
pressed.
> =A0 There is a bit of a funny trick to getting it to use gfortran, since =
in
> principle it knows about only C compilers. =A0You have to tell it to make=
a copy
> of the GCC C compiler, then rename and customise that copy so it uses gfo=
rtran.
> =A0 It isn't as powerful as MSVS, but it's doing the job for me so far. =
=A0The great
> thing, of course, is that it's free and multi-platform.
I have installed the equation.com version of gfortran and it comes
with the
libws_32.a library, alright. But perhaps not the very latest
version ...
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
9/2/2010 6:58:25 AM
|
|
On 31 aug, 12:25, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> This test program was designed to communicate with a test TCP server prog=
ram
> (Python). =A0It tests sending and receiving messages. =A0At least you can=
see how
> the various C functions are called.
>
> The C code is compiled with gcc:
> gcc -c tcpc.c
> gcc -c client.c
> then built with the Fortran code, linking libws2_32.a:
> gfortran fortest.f90 tcpc.o client.o -lws2_32
>
> fortest.f90
>
> ! To test Richard Maine's TCP C code
>
> subroutine write_error_sub(msg)
> character*(*) :: msg
> write(*,*) msg
> end subroutine
>
> program main
> implicit none
> integer :: sock, host_name_len, msglen, ndata, error
> integer :: port =3D 5000
> character*(128) :: host_name =3D 'localhost'
> character*(128) :: msg
> character*(128) :: data
> character :: ans
> !external :: tcp_connect
>
> host_name_len =3D len_trim(host_name)
> write(*,*) 'call tcp_connect'
> call tcp_connect(sock, host_name, host_name_len, port, error)
> write(*,*) 'error: ',error
> if (error =3D=3D 0) then
> =A0 =A0 =A0write(*,*) 'Connection succeeded'
> =A0 =A0 =A0msg =3D "Hello from fortest"
> =A0 =A0 =A0msglen =3D len_trim(msg)
> =A0 =A0 =A0call tcp_write(sock,msg,msglen,error)
> =A0 =A0 =A0write(*,*) "sent msg: error: ",error
> =A0 =A0 =A0do
> =A0 =A0 =A0 =A0 =A0ndata =3D 128
> =A0 =A0 =A0 =A0 =A0data =3D ' '
> =A0 =A0 =A0 =A0 =A0call tcp_read(sock,data,ndata,error)
> =A0 =A0 =A0 =A0 =A0if (error /=3D 0) then
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) 'tcp_read error: ',error
> =A0 =A0 =A0 =A0 =A0 =A0 =A0exit
> =A0 =A0 =A0 =A0 =A0endif
> =A0 =A0 =A0 =A0 =A0if ( data(1:2) =3D=3D 'q ' .or. data(1:2) =3D=3D 'Q ')=
then
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) 'got Q'
> =A0 =A0 =A0 =A0 =A0 =A0 =A0exit
> =A0 =A0 =A0 =A0 =A0else
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) "RECEIVED: " , trim(data)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0write(*,*) "Input text to send ( q or Q to Qui=
t):"
> =A0 =A0 =A0 =A0 =A0 =A0 =A0read(*,'(a)') msg
> =A0 =A0 =A0 =A0 =A0 =A0 =A0msglen =3D len_trim(msg)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0call tcp_write(sock,msg,msglen,error)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0if (msg(1:2) =3D=3D 'Q ' .or. msg(1:2) =3D=3D =
'q ') then
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0exit
> =A0 =A0 =A0 =A0 =A0 =A0 =A0endif
> =A0 =A0 =A0 =A0 =A0endif
> =A0 =A0 =A0enddo
> =A0 =A0 =A0call tcp_close(sock)
> endif
> end program
There is an interfacing problem here:
In calls like:
call tcp_write(sock,msg,msglen,error)
the argument msglen is not needed. The C source does define it, but
that
is because most Fortran implementations pass the length of a string as
a hidden argument. This argument is visible on the C side, but it is
an integer _value_, not as with all other arguments a pointer.
The above call should be:
call tcp_write(sock,msg,error)
(Unfortunately, the position of the hidden argument depends on the
Fortran compiler you use, and possibly on flags you pass. That is
one of the things I want to generalise in this code ;). F2003 uses
a different solution: by regarding character strings as arrays of
characters (len=3D1))
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
9/2/2010 7:03:15 AM
|
|
Arjen Markus <arjen.markus895@gmail.com> wrote:
> call tcp_write(sock,msg,msglen,error)
>
> the argument msglen is not needed....
[explains the usual issue with Fortran/C character passing]
> The above call should be:
>
> call tcp_write(sock,msg,error)
Except that I think you'll find that won't work because my tcp_write
expects msglen to be passed by address instead of by value as will
probably happen with most implementations (as you correctly describe).
This is intentional. The documentation I provided in the post was
admitedly... um... minimal. But the idea was that msg should *NOT* be a
character string on the Fortran side. After all, character is (in my
view, expressed here before) a poor choice for an arbitrary bit bucket.
I tend to use arrays of integers (generally 1-byte ones) for buffers
like this. I think you'll find that if msg is an array of integers, that
the first form shown above is appropriate.
(Yes, I know it is a pointer to char on the C side, but C is ... funny
that way in that char is numeric.)
Gib's test happened to use character data, but that's not a general
property of tcp data buffers.
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
|
|
0
|
|
|
|
Reply
|
nospam
|
9/2/2010 7:59:09 AM
|
|
Arjen Markus wrote:
> There is an interfacing problem here:
>
> In calls like:
>
> call tcp_write(sock,msg,msglen,error)
>
> the argument msglen is not needed. The C source does define it, but
> that
> is because most Fortran implementations pass the length of a string as
> a hidden argument. This argument is visible on the C side, but it is
> an integer _value_, not as with all other arguments a pointer.
>
> The above call should be:
>
> call tcp_write(sock,msg,error)
>
> (Unfortunately, the position of the hidden argument depends on the
> Fortran compiler you use, and possibly on flags you pass. That is
> one of the things I want to generalise in this code ;). F2003 uses
> a different solution: by regarding character strings as arrays of
> characters (len=1))
Hi Arjen,
Obviously I wasn't concentrating when I worked on this, because I am aware of
this string-passing issue (there was something nagging at the back of my mind
but I ignored it). Now I'm wondering why it seemed to pass the brief test I
gave it. A puzzle that I'll have to defer until tomorrow - it's getting late here.
Gib
|
|
0
|
|
|
|
Reply
|
Gib
|
9/2/2010 9:01:16 AM
|
|
On 2 sep, 09:59, nos...@see.signature (Richard Maine) wrote:
> Arjen Markus <arjen.markus...@gmail.com> wrote:
> > =A0 =A0 call tcp_write(sock,msg,msglen,error)
>
> > the argument msglen is not needed....
>
> =A0 [explains the usual issue with Fortran/C character passing]
>
> > The above call should be:
>
> > =A0 =A0call tcp_write(sock,msg,error)
>
> Except that I think you'll find that won't work because my tcp_write
> expects msglen to be passed by address instead of by value as will
> probably happen with most implementations (as you correctly describe).
> This is intentional. The documentation I provided in the post was
> admitedly... um... minimal. But the idea was that msg should *NOT* be a
> character string on the Fortran side. After all, character is (in my
> view, expressed here before) a poor choice for an arbitrary bit bucket.
> I tend to use arrays of integers (generally 1-byte ones) for buffers
> like this. I think you'll find that if msg is an array of integers, that
> the first form shown above is appropriate.
>
> (Yes, I know it is a pointer to char on the C side, but C is ... funny
> that way in that char is numeric.)
>
> Gib's test happened to use character data, but that's not a general
> property of tcp data buffers.
>
> --
> Richard Maine =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0| Good judgment come=
s from experience;
> email: last name at domain . net | experience comes from bad judgment.
> domain: summertriangle =A0 =A0 =A0 =A0 =A0 | =A0-- Mark Twain
Ah, right - oversight at my end. I had seen this for tcl_connect and
transposed the pattern to tcl_write without actually looking.
tcl_connect does expect a character string (and host_name_len is a
plain int)
Now I understand why :).
And yes, C is funny (for funny values of funny) wrt characters - in
many
implementations they are signed and there is a special connection
between
chars and ints that comes into play from time to time.
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
9/2/2010 11:51:20 AM
|
|
On 2 sep, 11:01, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> Arjen Markus wrote:
>
> =A0 > There is an interfacing problem here:
>
>
>
>
>
>
>
> > In calls like:
>
> > =A0 =A0 call tcp_write(sock,msg,msglen,error)
>
> > the argument msglen is not needed. The C source does define it, but
> > that
> > is because most Fortran implementations pass the length of a string as
> > a hidden argument. This argument is visible on the C side, but it is
> > an integer _value_, not as with all other arguments a pointer.
>
> > The above call should be:
>
> > =A0 =A0call tcp_write(sock,msg,error)
>
> > (Unfortunately, the position of the hidden argument depends on the
> > Fortran compiler you use, and possibly on flags you pass. That is
> > one of the things I want to generalise in this code ;). F2003 uses
> > a different solution: by regarding character strings as arrays of
> > characters (len=3D1))
>
> Hi Arjen,
>
> Obviously I wasn't concentrating when I worked on this, because I am awar=
e of
> this string-passing issue (there was something nagging at the back of my =
mind
> but I ignored it). =A0Now I'm wondering why it seemed to pass the brief t=
est I
> gave it. =A0A puzzle that I'll have to defer until tomorrow - it's gettin=
g late here.
>
> Gib- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -
It is only the tcp_connect() routine that is important here -
tcp_write()
works as you thought.
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
9/2/2010 11:52:11 AM
|
|
Arjen Markus wrote:
<snipped because my newsreader puts a limit on quoted characters>
>> Obviously I wasn't concentrating when I worked on this, because I am aware of
>> this string-passing issue (there was something nagging at the back of my mind
>> but I ignored it). Now I'm wondering why it seemed to pass the brief test I
>> gave it. A puzzle that I'll have to defer until tomorrow - it's getting late here.
>>
>> Gib- Tekst uit oorspronkelijk bericht niet weergeven -
>>
>> - Tekst uit oorspronkelijk bericht weergeven -
>
> It is only the tcp_connect() routine that is important here -
> tcp_write()
> works as you thought.
Interesting. In fact tcp_connect() was not working correctly as I had it,
although it appeared to. host_name_len passed as an int was giving a garbage
value (a big positive number), but it wasn't detected in my test because of
Richard's extra check in the for loop, limiting cstring to 128 chars. There is
no reason why tcp_connect() should be different from tcp_write(), as far as gcc
is concerned. I've made host_name_len a pointer in tcp_connect(), and now the
host_name is passed correctly.
This works with gcc and gfortran. I presume it wouldn't work with many other
compilers, which secretly place the string length as a hidden argument after the
string.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/2/2010 11:58:38 PM
|
|
On 8/31/2010 3:15 AM, Gib Bogle wrote:
> client.c
>
>
> /* Connect to a specified host and port.
> * Return a socket number in sock. */
>
> void tcp_connect(sock, host_name, host_name_len, port, error)
> int *sock, *port, *error;
> char *host_name;
> int host_name_len;
> {
> char cstring[129];
> int i;
> struct hostent *host_ent;
> struct sockaddr_in host;
> int keepalive;
> char *from, *to;
> WORD wver;
> WSADATA wsaData;
>
Gib, if you got this to link, then you found a way resolve
> /* Attempt connection */
> if (connect(*sock, (struct sockaddr *) &host, sizeof(host)) ==
> SOCKET_ERROR)
> {printerrormsg("Connect failed."); closesocket(*sock); return;}
this ^^^^, which I couldn't.
Possibilities:
Do you include a header which I did not?
#include <winsock.h>
#include <stdio.h>
Did you link to something on the command line to resolve it?
If your intent was to have it call this
subroutine write_error_sub(msg)
character*(*) :: msg
write(*,*) msg
end subroutine
from fortran, well, I couldn't find a way to do it:
C:\eq\source>gfortran client1.o tcpc.o libws2_32.a fortest.f90 -o out
client1.o:client1.c:(.text+0xc4): undefined reference to `write_error_sub'
Anyways, I don't know much about what I'm doing, and it's showing. Cheers,
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/3/2010 12:39:41 AM
|
|
On 9/2/2010 12:59 AM, Richard Maine wrote:
> Arjen Markus<arjen.markus895@gmail.com> wrote:
>
>> call tcp_write(sock,msg,msglen,error)
>>
>> the argument msglen is not needed....
> [explains the usual issue with Fortran/C character passing]
>
>> The above call should be:
>>
>> call tcp_write(sock,msg,error)
>
> Except that I think you'll find that won't work because my tcp_write
> expects msglen to be passed by address instead of by value as will
> probably happen with most implementations (as you correctly describe).
> This is intentional. The documentation I provided in the post was
> admitedly... um... minimal. But the idea was that msg should *NOT* be a
> character string on the Fortran side. After all, character is (in my
> view, expressed here before) a poor choice for an arbitrary bit bucket.
> I tend to use arrays of integers (generally 1-byte ones) for buffers
> like this. I think you'll find that if msg is an array of integers, that
> the first form shown above is appropriate.
>
> (Yes, I know it is a pointer to char on the C side, but C is ... funny
> that way in that char is numeric.)
>
> Gib's test happened to use character data, but that's not a general
> property of tcp data buffers.
>
Well, Richard, I've been slaughtering this material today. I don't do
much programming over here on windows, but I'm finding the reasons that
I had for doing so are becoming fewer. Who knows, one day I might
actually understand mingw.
When I couldn't find a line number in notepad, I bit the bullet and got
notepad++, which is wonderful compared to what ii replaces. So while
the code you posted lays strewn around in translation units with
unresolved references, darnit, things are getting squared.
I realized after I posted to Gib that I was leaving out part of the
story, and this detail could really help prognosticate. I had trouble
on the C side, but I failed to report that I had trouble on the fortran
side as well:
C:\DOCUME~1\dan\LOCALS~1\Temp\ccYPsO32.o:fortest.f90:(.text+0x126):
undefined re
ference to `tcp_connect_'
collect2: ld returned 1 exit status
Anyways, so I'm hoping you're giving partial credit for trying to state
the problem clearly.
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/3/2010 12:59:35 AM
|
|
Uno wrote:
> Gib, if you got this to link, then you found a way resolve
>> /* Attempt connection */
>> if (connect(*sock, (struct sockaddr *) &host, sizeof(host)) ==
>> SOCKET_ERROR)
>> {printerrormsg("Connect failed."); closesocket(*sock); return;}
>
> this ^^^^, which I couldn't.
My deepest apologies, Uno. I stupidly chopped some code off the top of the
client.c code when I posted it. I guess I was trying to reduce the size by
taking off Richard's comments. Bugger! I'll post it soon (have to go out now),
and I since I've been playing around a bit testing things I want to make sure
that what I post is right this time. Take a break.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/3/2010 1:15:18 AM
|
|
Arjen Markus wrote:
> I have installed the equation.com version of gfortran and it comes
> with the
> libws_32.a library, alright. But perhaps not the very latest
> version ...
Thanks for you reply, Arjen, I was motivated to look again and found it.
How do you have this arranged so that the linker finds it when it is
creating the executable?
I'm "new" on windows all over again. I find that the steps you take to
pay attention to the virtue of laziness inform what you can do to the
other installs.
(That's almost a sentence.:-))
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/3/2010 2:04:51 AM
|
|
On 3 sep, 01:58, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> Arjen Markus wrote:
>
> <snipped because my newsreader puts a limit on quoted characters>
>
> >> Obviously I wasn't concentrating when I worked on this, because I am a=
ware of
> >> this string-passing issue (there was something nagging at the back of =
my mind
> >> but I ignored it). =A0Now I'm wondering why it seemed to pass the brie=
f test I
> >> gave it. =A0A puzzle that I'll have to defer until tomorrow - it's get=
ting late here.
>
> >> Gib- Tekst uit oorspronkelijk bericht niet weergeven -
>
> >> - Tekst uit oorspronkelijk bericht weergeven -
>
> > It is only the tcp_connect() routine that is important here -
> > tcp_write()
> > works as you thought.
>
> Interesting. =A0In fact tcp_connect() was not working correctly as I had =
it,
> although it appeared to. =A0host_name_len passed as an int was giving a g=
arbage
> value (a big positive number), but it wasn't detected in my test because =
of
> Richard's extra check in the for loop, limiting cstring to 128 chars. =A0=
There is
> no reason why tcp_connect() should be different from tcp_write(), as far =
as gcc
> is concerned. =A0I've made host_name_len a pointer in tcp_connect(), and =
now the
> host_name is passed correctly.
>
> This works with gcc and gfortran. =A0I presume it wouldn't work with many=
other
> compilers, which secretly place the string length as a hidden argument af=
ter the
> string.
I know CVF places the hidden argument right after the string (by
default) and
Intel Fortran places it at the end (by default). It is something for
which I
have a heuristic test :) and I will build that into the generalised
version
(well, the test will determine the value of a C macro, so needs to run
as
part of the build process). Anyway, this kind of things is annoying
but solvable.
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
Arjen
|
9/3/2010 6:35:25 AM
|
|
With any luck, this is a version of clientc.c that works with gfortran/gcc
/* $Id: clientc.c,v 1.1.1.1 2003/03/06 19:38:16 maine Exp $ */
/* $Name: $ */
/* clientc.c
* open a tcp connection for a getData client.
*
* The read/write/close routines are used in both clients and servers
* and are in a separate file.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for MS Windows MS C and CVF.
* Differences from nag version are:
* Different system include files.
* Different procedure name mangling convention in the defines
* Specify __stdcall in procedure headers.
* Add extern decl for write_error_sub (to get the __stdcall).
* Different placement of implicit string length argument.
* Have to call Win-specific WSAStartup or nothing works.
* Cast *port (perhaps appropriate for nag version also).
* Used SOCKET_ERROR and INVALID_SOCKET to test for errors per MS docs,
* though the bsd-style test did appear to work.
*
* 11 Oct 91, Richard Maine: Version 1.0
* 17 Jul 01, Richard Maine: MS Windows port.
* 3 Sep 10, slightly modified by Gib Bogle
*/
#include <sys/types.h>
#include <winsock.h>
/* Defines to make routines fortran-callable */
/*
* Would probably be better to return an error message to the
* calling routine instead of calling write_error_msg from in here.
*/
static int started = 0;
#define write_error_msg write_error_sub_
#define tcp_connect tcp_connect_
extern void write_error_msg (char * msg, int n);
/* Wrapper to call fortran error message routine.
* We want to avoid standard c i/o. */
void printerrormsg(msg)
char *msg;
{
write_error_msg(msg,strlen(msg));
}
/* Connect to a specified host and port.
* Return a socket number in sock. */
void tcp_connect(sock, host_name, host_name_len, port, error)
int *sock, *port, *error;
char *host_name;
int *host_name_len; // note: this was not a pointer in RM's code
{
char cstring[129];
int i;
struct hostent *host_ent;
struct sockaddr_in host;
int keepalive;
char *from, *to;
WORD wver;
WSADATA wsaData;
*error = 1;
/* Start up Windows sockets */
if (started == 0) {
started = 1;
wver = MAKEWORD(2,0);
i = WSAStartup(wver, &wsaData);
}
/* Make host_name into a c string */
for (i=0; (i<*host_name_len) && (i<127) && (host_name[i] != ' '); i++)
cstring[i] = host_name[i];
cstring[i] = '\0';
/* Find the host IP address */
host.sin_family = AF_INET;
host_ent = gethostbyname(cstring);
if (host_ent==0) {printerrormsg("Can't find host address."); return;}
/* Avoid bcopy/memcpy dependence. */
from = (char *) host_ent->h_addr;
to = (char *) &host.sin_addr;
for (i=0; i<host_ent->h_length; i++) *to++ = *from++;
/* Set the port */
host.sin_port = htons((unsigned short int) *port);
/* Create a socket */
*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (*sock==INVALID_SOCKET) {printerrormsg("Can't create socket.");
return;
}
/* Attempt connection */
if (connect(*sock, (struct sockaddr *) &host, sizeof(host)) == SOCKET_ERROR)
{printerrormsg("Connect failed."); closesocket(*sock); return;}
/* Enable keepalive packets to check socket connection. */
keepalive = 1;
setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, 4);
*error = 0;
}
|
|
0
|
|
|
|
Reply
|
Gib
|
9/3/2010 7:07:58 AM
|
|
Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
> With any luck, this is a version of clientc.c that works with gfortran/gcc
>
> int *host_name_len; // note: this was not a pointer in RM's code
I'm slightly curious about the reason for that change. I had it as a
nonpointer because host_name did seem like something that, being a name,
"naturally" ought to be character, unlike the tcp data buffers, which I
viewed as just bit buckets. Thus I played the game needed to pass a
Fortran character, which (usually) involves an extra "hidden" value
argument for the length.
That does mean I had to deal with the issues of porting the code between
compilers with differences in details such as where the extra length
argument belonged.
I suppose you probably are passing the host name as a non-character
Fortran variable in your modified version, and then passing the length
explicitly. I can see that would avoid having to deal with the
variations in character passing conventions. One would pay for it in
having to get the character name data into a non-character variable,
possibly using transfer or equivalence or some such game. I could se tht
as a tradeoff some people might prefer. Is that what you are doing?
P.S. Of course, this is all much better with the f2003 C interop stuff,
but you might not be able to count on availability of that in the
compilers you need to use. (I wouldn't hold my breath waiting for
someone to bring CVF back under active support and add that feature to
it, for example. I would expect it to get in gfortran, if it isn't
already there.)
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
|
|
0
|
|
|
|
Reply
|
nospam
|
9/3/2010 7:27:29 AM
|
|
Richard Maine wrote:
> Should anyone care, here's the comparable stuff I had for my servers. I
> did not port this to Windows, and it hasn't even been used on a wide
> variety of Unix systems - just the few we were using for servers. It
> started out on SunOS 4, ported to Solaris 2, and I think I recall
> experimenting with this on Linux.
>
> I wrote this stuff about 20 years ago. It is currently still in wide use
> at NASA Dryden (and I don't see any sign of that changing soon).
I needed to add these includes:
#include <stdlib.h>
#include <unistd.h>
I guess I didn't need to, but for a function like exit, well, I don't
want to go looking for new code when the c standard headers are recommended.
$ gcc -c -D_GNU_SOURCE -std=c99 -Wall -Wextra server1.c
server1.c:45: warning: unused parameter �sig�
server1.c: In function �tcp_listen_�:
server1.c:103: warning: pointer targets in passing argument 3 of
�accept� differ in signedness
server1.c:131: warning: pointer targets in passing argument 3 of
�getsockname� differ in signedness
server1.c:69: warning: unused variable �pid�
$ cat server1.c
/* $Id: serverc.c,v 1.2 2005/12/28 22:49:13 maine Exp $ */
/* $Name: $ */
/* serverc.c
* listen for a connection attempt to a getData server.
*
* The read/write/close routines are used in both clients and servers
* and are in a separate file.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for SunOS 4.1.x with NAG f90 1.2.
* Note, specifically that Sun Fortran puts character length
* information at the end of the argument list instead of right
* after the corresponding arguments.
*
* 28 Jun 93, Richard Maine: Version 1.5.
* 19 Nov 96, Richard Maine: casts to avoid gcc warnings.
* 16 Apr 98, Richard Maine: fix sol2 sigchld handling.
* 2 Dec 98, Richard Maine: fix addr len in gethostbyaddr.
* 21 Dec 05, Richard Maine: avoid sun-specific in_addr structure.
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
/* Defines to make routines fortran-callable */
#define tcp_listen tcp_listen_
/* handler for SIGCHLD signal
* Needed so that child processes can be decently buried when they die.
* USG systems may need signal handler reestablished after use?
* but we are not using this for SOL2. */
void child_handler(sig)
int sig;
{
wait3(0, WNOHANG, 0);
return;
}
/* Listen for connection attempts on the specified port.
* Fork a child for each connection.
* Return connection information.
* Normal return value for child is 0.
* Positive return values are errors in parent.
* Negative return values are errors in child. */
void tcp_listen(port ,sock, client_ip, client_port, client_name,
server_ip, connect_num, error, client_name_len)
int *port, *sock, *client_port, *connect_num, *error;
int client_ip[3], server_ip[3];
char *client_name;
int client_name_len;
{
struct sockaddr_in server_addr, client_addr;
int lsock; /* Socket for listening */
int addr_size = sizeof(server_addr);
int addr_len;
int pid;
int keepalive;
struct hostent *host_ent;
int i;
unsigned char *ip_byte;
/* Divorce ourselves from controlling terminal. */
/* See Stevens, Chapter 13. */
if (fork()>0) exit(0); /* Parent returns; child continues. */
setsid(); /* Become session leader */
/* Create a socket, bind it to specified port and listen. */
*connect_num = 0;
lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lsock<0) {*error=1; return;}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(*port);
if (bind(lsock, (struct sockaddr *) &server_addr, addr_size) < 0)
{*error=2; return;}
if (listen(lsock,5) < 0) {*error=3; return;}
/* Accept connections and fork server instances. */
#ifdef SOL2
signal(SIGCHLD, SIG_IGN);
#else
signal(SIGCHLD, child_handler);
#endif
for (;;) {
addr_len = addr_size;
*sock = accept(lsock, (struct sockaddr *) &client_addr, &addr_len);
/* For reasons unclear to me, we get a lot of accept failures. */
/* perror says something about interrupted system call. */
/* We just ignore them for now. */
/* Can count them by moving the connect_num incrementation. */
if (*sock<0) continue; /* Connect failed */
*connect_num = *connect_num + 1;
/* Child process. Return connection data. */
if (fork()==0) {
close(lsock);
ip_byte = (unsigned char *) &client_addr.sin_addr;
client_ip[0] = *(ip_byte++);
client_ip[1] = *(ip_byte++);
client_ip[2] = *(ip_byte++);
client_ip[3] = *(ip_byte++);
*client_port = ntohs(client_addr.sin_port);
host_ent = gethostbyaddr((char *) &client_addr.sin_addr.s_addr,
sizeof(client_addr.sin_addr.s_addr), AF_INET);
for (i=0; i<client_name_len; i++) client_name[i] = ' ';
if (host_ent) {
for (i=0; (i<client_name_len) && (host_ent->h_name[i]!=0); i++)
client_name[i] = host_ent->h_name[i];
}
addr_len = addr_size;
getsockname(*sock, (struct sockaddr *) &server_addr, &addr_len);
ip_byte = (unsigned char *) &server_addr.sin_addr;
server_ip[0] = *(ip_byte++);
server_ip[1] = *(ip_byte++);
server_ip[2] = *(ip_byte++);
server_ip[3] = *(ip_byte++);
signal(SIGCHLD, SIG_DFL);
/* We want an error return from writes to closed sockets so
* we can shut down cleanly and log the termination.
* Without this, we quitely die.
*/
signal (SIGPIPE, SIG_IGN);
/* Enable keepalive packets to check socket connection. */
keepalive = 1;
setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, 4);
/* Allow SIGIO/SIGURG to signal this process. */
/* Not currently needed. Retain as comment. */
/* if (fcntl(*sock, F_SETOWN, getpid()) < 0) return(-1); */
/* if (fcntl(*sock, F_SETFL, FASYNC) < 0) return (-2); */
*error=0;
return;
}
/* Parent process. Loop back for further connections. */
close(*sock);
}
}
// gcc -c -D_GNU_SOURCE -std=c99 -Wall -Wextra server1.c
$
Client didn't do so well as to become object code.
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/3/2010 7:28:07 AM
|
|
Richard Maine wrote:
> Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
>
>> With any luck, this is a version of clientc.c that works with gfortran/gcc
>>
>> int *host_name_len; // note: this was not a pointer in RM's code
>
> I'm slightly curious about the reason for that change. I had it as a
> nonpointer because host_name did seem like something that, being a name,
> "naturally" ought to be character, unlike the tcp data buffers, which I
> viewed as just bit buckets. Thus I played the game needed to pass a
> Fortran character, which (usually) involves an extra "hidden" value
> argument for the length.
>
> That does mean I had to deal with the issues of porting the code between
> compilers with differences in details such as where the extra length
> argument belonged.
>
> I suppose you probably are passing the host name as a non-character
> Fortran variable in your modified version, and then passing the length
> explicitly. I can see that would avoid having to deal with the
> variations in character passing conventions. One would pay for it in
> having to get the character name data into a non-character variable,
> possibly using transfer or equivalence or some such game. I could se tht
> as a tradeoff some people might prefer. Is that what you are doing?
When I put a printf in clientc.c to look at the value of host_name_len that was
passed, I saw that it was 2686536 when host_name = 'localhost'. Since Fortran
passes all visible arguments by reference, of course that makes sense. As I
said in another post that you may have missed, my test program seemed to work
because you also truncate cstring to 128 chars. The hidden string-length
arguments that some Fortran compilers use are passed by value, true, but since
no problems arise with the gcc compilers, presumably the hidden arguments are at
the end of the argument list - I must check .... Yes, when the argument 'hidden'
is added to the end of the argument list for tcp_connect() in clientc.c, it
receives the value 128 in my test program. That is the size of the host_name
character variable.
>
> P.S. Of course, this is all much better with the f2003 C interop stuff,
> but you might not be able to count on availability of that in the
> compilers you need to use. (I wouldn't hold my breath waiting for
> someone to bring CVF back under active support and add that feature to
> it, for example. I would expect it to get in gfortran, if it isn't
> already there.)
>
This should really all be done using the C interop stuff, I agree. Currently
with my other Fortran compiler (ifort) I'm using some much more complicated
Fortran code (accessing the Windows winsock stuff directly) that someone
supplied me with. The C option is much cleaner, and it should be made to work
on any modern Fortran compiler.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/3/2010 9:14:47 AM
|
|
On 9/3/2010 12:07 AM, Gib Bogle wrote:
> With any luck, this is a version of clientc.c that works with gfortran/gcc
C:\eq\source>gcc -c -Wall -Wextra tcpc.c
C:\eq\source>gcc -c -Wall -Wextra client1.c
client1.c: In function 'tcp_connect':
client1.c:40:3: warning: implicit declaration of function 'write_error_sub'
client1.c:25:6: warning: 'started' is used uninitialized in this function
C:\eq\source>gfortran client1.o tcpc.o libws2_32.a fortest.f90 -o out
client1.o:client1.c:(.text+0xc4): undefined reference to `write_error_sub'
client1.o:client1.c:(.text+0x165): undefined reference to `write_error_sub'
client1.o:client1.c:(.text+0x19f): undefined reference to `write_error_sub'
C:\DOCUME~1\dan\LOCALS~1\Temp\ccIMr2tt.o:fortest.f90:(.text+0x126):
undefined re
ference to `tcp_connect_'
collect2: ld returned 1 exit status
Lines 42 and 43 of client.c:
#define write_error_msg write_error_sub_
#define tcp_connect tcp_connect_
So is the idea here that we want to figure out how the compilers are
going to mangle the function names?
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/4/2010 2:59:43 AM
|
|
Uno wrote:
> On 9/3/2010 12:07 AM, Gib Bogle wrote:
>> With any luck, this is a version of clientc.c that works with
>> gfortran/gcc
>
> C:\eq\source>gcc -c -Wall -Wextra tcpc.c
>
> C:\eq\source>gcc -c -Wall -Wextra client1.c
> client1.c: In function 'tcp_connect':
> client1.c:40:3: warning: implicit declaration of function 'write_error_sub'
> client1.c:25:6: warning: 'started' is used uninitialized in this function
>
> C:\eq\source>gfortran client1.o tcpc.o libws2_32.a fortest.f90 -o out
> client1.o:client1.c:(.text+0xc4): undefined reference to `write_error_sub'
> client1.o:client1.c:(.text+0x165): undefined reference to `write_error_sub'
> client1.o:client1.c:(.text+0x19f): undefined reference to `write_error_sub'
> C:\DOCUME~1\dan\LOCALS~1\Temp\ccIMr2tt.o:fortest.f90:(.text+0x126):
> undefined re
> ference to `tcp_connect_'
> collect2: ld returned 1 exit status
>
> Lines 42 and 43 of client.c:
> #define write_error_msg write_error_sub_
> #define tcp_connect tcp_connect_
>
> So is the idea here that we want to figure out how the compilers are
> going to mangle the function names?
Yes. gfortran adds '_' to subroutine/function names.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/4/2010 5:12:25 AM
|
|
On 9/3/2010 10:12 PM, Gib Bogle wrote:
> Yes. gfortran adds '_' to subroutine/function names.
I think you're as lost as I am. You wouldn't happen to be an
australian, would you, because that is who you are in my head.
If you are, then bob and judy need to get to the airport. If you
aren't, then you need not worry.
Cheers,
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/4/2010 6:39:33 AM
|
|
Uno wrote:
> On 9/3/2010 10:12 PM, Gib Bogle wrote:
>
>> Yes. gfortran adds '_' to subroutine/function names.
>
> I think you're as lost as I am. You wouldn't happen to be an
> australian, would you, because that is who you are in my head.
>
> If you are, then bob and judy need to get to the airport. If you
> aren't, then you need not worry.
>
> Cheers,
Frankly, I have difficulty following you. All this socket stuff is working for
me, with gfortran. To embark on multi-language programming it is necessary to
have a degree of familiarity with each language.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/5/2010 12:17:08 AM
|
|
Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
> Uno wrote:
[something]
>
> Frankly, I have difficulty following you.
Don't think you are alone. :-)
--
Richard Maine | Good judgment comes from experience;
email: last name at domain . net | experience comes from bad judgment.
domain: summertriangle | -- Mark Twain
|
|
0
|
|
|
|
Reply
|
nospam
|
9/5/2010 2:10:27 AM
|
|
Richard Maine wrote:
> Gib Bogle <g.bogle@auckland.no.spam.ac.nz> wrote:
>
>> Uno wrote:
> [something]
>> Frankly, I have difficulty following you.
>
> Don't think you are alone. :-)
>
It's a challenge.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/6/2010 6:15:24 AM
|
|
On 3 sep, 09:07, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
> With any luck, this is a version of clientc.c that works with gfortran/gc=
c
>
> /* $Id: clientc.c,v 1.1.1.1 2003/03/06 19:38:16 maine Exp $ */
> /* $Name: =A0$ */
>
> /* clientc.c
> =A0 * open a tcp connection for a getData client.
> =A0 *
> =A0 * The read/write/close routines are used in both clients and servers
> =A0 * and are in a separate file.
> =A0 *
> =A0 * This file has wrappers for the system routines that are
> =A0 * not directly or easily callable from Fortran.
> =A0 * Specific to system Fortran and C calling conventions.
> =A0 *
> =A0 * Version for MS Windows MS C and CVF.
> =A0 * Differences from nag version are:
> =A0 * =A0 Different system include files.
> =A0 * =A0 Different procedure name mangling convention in the defines
> =A0 * =A0 Specify __stdcall in procedure headers.
> =A0 * =A0 Add extern decl for write_error_sub (to get the __stdcall).
> =A0 * =A0 Different placement of implicit string length argument.
> =A0 * =A0 Have to call Win-specific WSAStartup or nothing works.
> =A0 * =A0 Cast *port (perhaps appropriate for nag version also).
> =A0 * =A0 Used SOCKET_ERROR and INVALID_SOCKET to test for errors per MS =
docs,
> =A0 * =A0 though the bsd-style test did appear to work.
> =A0 *
> =A0 * 11 Oct 91, Richard Maine: Version 1.0
> =A0 * 17 Jul 01, Richard Maine: MS Windows port.
> =A0 * 3 Sep 10, slightly modified by Gib Bogle
> =A0 */
>
> #include <sys/types.h>
> #include <winsock.h>
>
> /* Defines to make routines fortran-callable */
> /*
> =A0 * Would probably be better to return an error message to the
> =A0 * calling routine instead of calling write_error_msg from in here.
> =A0 */
>
> static int started =3D 0;
>
> #define write_error_msg write_error_sub_
> #define tcp_connect tcp_connect_
> extern void =A0write_error_msg (char * msg, int n);
>
> /* Wrapper to call fortran error message routine.
> =A0 * We want to avoid standard c i/o. */
>
> void printerrormsg(msg)
> =A0 =A0char *msg;
> {
> =A0 =A0write_error_msg(msg,strlen(msg));
>
> }
>
> /* Connect to a specified host and port.
> =A0 * Return a socket number in sock. */
>
> void tcp_connect(sock, host_name, host_name_len, port, error)
> =A0 =A0int *sock, *port, *error;
> =A0 =A0char *host_name;
> =A0 =A0int *host_name_len; =A0// note: this was not a pointer in RM's cod=
e
> {
> =A0 =A0char cstring[129];
> =A0 =A0int i;
> =A0 =A0struct hostent *host_ent;
> =A0 =A0struct sockaddr_in host;
> =A0 =A0int keepalive;
> =A0 =A0char *from, *to;
> =A0 =A0WORD wver;
> =A0 =A0WSADATA wsaData;
>
> =A0 =A0*error =3D 1;
>
> =A0 =A0/* Start up Windows sockets */
> =A0 =A0if (started =3D=3D 0) {
> =A0 =A0 =A0started =3D 1;
> =A0 =A0 =A0wver =3D MAKEWORD(2,0);
> =A0 =A0 =A0i =3D WSAStartup(wver, &wsaData);
> =A0 =A0}
>
> =A0 =A0/* Make host_name into a c string */
> =A0 =A0for (i=3D0; (i<*host_name_len) && (i<127) && (host_name[i] !=3D ' =
'); i++)
> =A0 =A0 =A0cstring[i] =3D host_name[i];
> =A0 =A0cstring[i] =3D '\0';
>
> =A0 =A0/* Find the host IP address */
> =A0 =A0host.sin_family =3D AF_INET;
> =A0 =A0host_ent =3D gethostbyname(cstring);
> =A0 =A0if (host_ent=3D=3D0) {printerrormsg("Can't find host address."); r=
eturn;}
> =A0 =A0/* Avoid bcopy/memcpy dependence. */
> =A0 =A0from =3D (char *) host_ent->h_addr;
> =A0 =A0to =3D (char *) &host.sin_addr;
> =A0 =A0for (i=3D0; i<host_ent->h_length; i++) *to++ =3D *from++;
>
> =A0 =A0/* Set the port */
> =A0 =A0host.sin_port =3D htons((unsigned short int) *port);
>
> =A0 =A0/* Create a socket */
> =A0 =A0*sock =3D socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> =A0 =A0if (*sock=3D=3DINVALID_SOCKET) {printerrormsg("Can't create socket=
..");
> =A0 =A0 =A0return;
> =A0 =A0}
>
> =A0 =A0/* Attempt connection */
> =A0 =A0if (connect(*sock, (struct sockaddr *) &host, sizeof(host)) =3D=3D=
SOCKET_ERROR)
> =A0 =A0 =A0{printerrormsg("Connect failed."); closesocket(*sock); return;=
}
>
> =A0 =A0/* Enable keepalive packets to check socket connection. */
> =A0 =A0keepalive =3D 1;
> =A0 =A0setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, 4=
);
>
> =A0 =A0*error =3D 0;
>
>
>
> }- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -
I have started making this code and the related code a bit more
general.
It is more or less working now with gfortran/gcc, but I ran into an
odd
problem with Intel Fortran: I can not find the correct library to use
for the sockets!
Anyone knows which library to use? (I suppose the IDE will help a bit,
have not tried that yet :))
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
9/8/2010 7:10:26 AM
|
|
On 8 sep, 09:10, Arjen Markus <arjen.markus...@gmail.com> wrote:
> On 3 sep, 09:07, Gib Bogle <g.bo...@auckland.no.spam.ac.nz> wrote:
>
>
>
>
>
> > With any luck, this is a version of clientc.c that works with gfortran/=
gcc
>
> > /* $Id: clientc.c,v 1.1.1.1 2003/03/06 19:38:16 maine Exp $ */
> > /* $Name: =A0$ */
>
> > /* clientc.c
> > =A0 * open a tcp connection for a getData client.
> > =A0 *
> > =A0 * The read/write/close routines are used in both clients and server=
s
> > =A0 * and are in a separate file.
> > =A0 *
> > =A0 * This file has wrappers for the system routines that are
> > =A0 * not directly or easily callable from Fortran.
> > =A0 * Specific to system Fortran and C calling conventions.
> > =A0 *
> > =A0 * Version for MS Windows MS C and CVF.
> > =A0 * Differences from nag version are:
> > =A0 * =A0 Different system include files.
> > =A0 * =A0 Different procedure name mangling convention in the defines
> > =A0 * =A0 Specify __stdcall in procedure headers.
> > =A0 * =A0 Add extern decl for write_error_sub (to get the __stdcall).
> > =A0 * =A0 Different placement of implicit string length argument.
> > =A0 * =A0 Have to call Win-specific WSAStartup or nothing works.
> > =A0 * =A0 Cast *port (perhaps appropriate for nag version also).
> > =A0 * =A0 Used SOCKET_ERROR and INVALID_SOCKET to test for errors per M=
S docs,
> > =A0 * =A0 though the bsd-style test did appear to work.
> > =A0 *
> > =A0 * 11 Oct 91, Richard Maine: Version 1.0
> > =A0 * 17 Jul 01, Richard Maine: MS Windows port.
> > =A0 * 3 Sep 10, slightly modified by Gib Bogle
> > =A0 */
>
> > #include <sys/types.h>
> > #include <winsock.h>
>
> > /* Defines to make routines fortran-callable */
> > /*
> > =A0 * Would probably be better to return an error message to the
> > =A0 * calling routine instead of calling write_error_msg from in here.
> > =A0 */
>
> > static int started =3D 0;
>
> > #define write_error_msg write_error_sub_
> > #define tcp_connect tcp_connect_
> > extern void =A0write_error_msg (char * msg, int n);
>
> > /* Wrapper to call fortran error message routine.
> > =A0 * We want to avoid standard c i/o. */
>
> > void printerrormsg(msg)
> > =A0 =A0char *msg;
> > {
> > =A0 =A0write_error_msg(msg,strlen(msg));
>
> > }
>
> > /* Connect to a specified host and port.
> > =A0 * Return a socket number in sock. */
>
> > void tcp_connect(sock, host_name, host_name_len, port, error)
> > =A0 =A0int *sock, *port, *error;
> > =A0 =A0char *host_name;
> > =A0 =A0int *host_name_len; =A0// note: this was not a pointer in RM's c=
ode
> > {
> > =A0 =A0char cstring[129];
> > =A0 =A0int i;
> > =A0 =A0struct hostent *host_ent;
> > =A0 =A0struct sockaddr_in host;
> > =A0 =A0int keepalive;
> > =A0 =A0char *from, *to;
> > =A0 =A0WORD wver;
> > =A0 =A0WSADATA wsaData;
>
> > =A0 =A0*error =3D 1;
>
> > =A0 =A0/* Start up Windows sockets */
> > =A0 =A0if (started =3D=3D 0) {
> > =A0 =A0 =A0started =3D 1;
> > =A0 =A0 =A0wver =3D MAKEWORD(2,0);
> > =A0 =A0 =A0i =3D WSAStartup(wver, &wsaData);
> > =A0 =A0}
>
> > =A0 =A0/* Make host_name into a c string */
> > =A0 =A0for (i=3D0; (i<*host_name_len) && (i<127) && (host_name[i] !=3D =
' '); i++)
> > =A0 =A0 =A0cstring[i] =3D host_name[i];
> > =A0 =A0cstring[i] =3D '\0';
>
> > =A0 =A0/* Find the host IP address */
> > =A0 =A0host.sin_family =3D AF_INET;
> > =A0 =A0host_ent =3D gethostbyname(cstring);
> > =A0 =A0if (host_ent=3D=3D0) {printerrormsg("Can't find host address.");=
return;}
> > =A0 =A0/* Avoid bcopy/memcpy dependence. */
> > =A0 =A0from =3D (char *) host_ent->h_addr;
> > =A0 =A0to =3D (char *) &host.sin_addr;
> > =A0 =A0for (i=3D0; i<host_ent->h_length; i++) *to++ =3D *from++;
>
> > =A0 =A0/* Set the port */
> > =A0 =A0host.sin_port =3D htons((unsigned short int) *port);
>
> > =A0 =A0/* Create a socket */
> > =A0 =A0*sock =3D socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> > =A0 =A0if (*sock=3D=3DINVALID_SOCKET) {printerrormsg("Can't create sock=
et.");
> > =A0 =A0 =A0return;
> > =A0 =A0}
>
> > =A0 =A0/* Attempt connection */
> > =A0 =A0if (connect(*sock, (struct sockaddr *) &host, sizeof(host)) =3D=
=3D SOCKET_ERROR)
> > =A0 =A0 =A0{printerrormsg("Connect failed."); closesocket(*sock); retur=
n;}
>
> > =A0 =A0/* Enable keepalive packets to check socket connection. */
> > =A0 =A0keepalive =3D 1;
> > =A0 =A0setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive,=
4);
>
> > =A0 =A0*error =3D 0;
>
> > }- Tekst uit oorspronkelijk bericht niet weergeven -
>
> > - Tekst uit oorspronkelijk bericht weergeven -
>
> I have started making this code and the related code a bit more
> general.
> It is more or less working now with gfortran/gcc, but I ran into an
> odd
> problem with Intel Fortran: I can not find the correct library to use
> for the sockets!
>
> Anyone knows which library to use? (I suppose the IDE will help a bit,
> have not tried that yet :))
>
> Regards,
>
> Arjen- Tekst uit oorspronkelijk bericht niet weergeven -
>
> - Tekst uit oorspronkelijk bericht weergeven -
With some small adjustments I have the client side almost working on
Linux
as well (both gfortran and Intel Fortran, by the way).
Regards,
Arjen
|
|
0
|
|
|
|
Reply
|
arjen.markus895 (633)
|
9/8/2010 11:11:04 AM
|
|
Arjen Markus wrote:
> With some small adjustments I have the client side almost working on
> Linux
> as well (both gfortran and Intel Fortran, by the way).
>
> Regards,
>
> Arjen
I'm also using it with gfortran on Windows (with MinGW) and Linux. I haven't
looked at ifort, since I have some Fortran code that uses the Win API that is
working. I'd like to replace this by a C-based version, though, to make
everything as uniform and portable as possible. I'm in the process of porting
my application to Mac OS X, on which I expect clientc.c to work pretty much the
same as in Linux.
|
|
0
|
|
|
|
Reply
|
Gib
|
9/8/2010 9:12:58 PM
|
|
Gib Bogle wrote:
> It's a challenge.
Of course it's a challenge to communicate a half-world away. It's
harder for me to understand this notion since I have little experience
with client/server communication. I have clients and servers, but
they're people like bob, judy, ginger, sandy or jimmy.
I think the target for Gib's original thesis was for windows. Richard
posted this for linux. My question is simple:
What are you supposed to do with this and why does fortran make this easier?
A lot of these warnings will disappear pretty quickly, but without the
author's intent being known, I don't know how to proceed.
$ gcc -c -Wall -Wextra client2.c
client2.c:43: warning: unused parameter �sig�
client2.c: In function �tcp_listen_�:
client2.c:76: warning: implicit declaration of function �fork�
client2.c:76: warning: implicit declaration of function �exit�
client2.c:76: warning: incompatible implicit declaration of built-in
function �exit�
client2.c:77: warning: implicit declaration of function �setsid�
client2.c:101: warning: pointer targets in passing argument 3 of
�accept� differ in signedness
client2.c:113: warning: implicit declaration of function �close�
client2.c:129: warning: pointer targets in passing argument 3 of
�getsockname� differ in signedness
client2.c:67: warning: unused variable �pid�
$ gcc -c -D_GNU_SOURCE -std=c99 -Wall -Wextra server1.c
server1.c:45: warning: unused parameter �sig�
server1.c: In function �tcp_listen_�:
server1.c:103: warning: pointer targets in passing argument 3 of
�accept� differ in signedness
server1.c:131: warning: pointer targets in passing argument 3 of
�getsockname� differ in signedness
server1.c:69: warning: unused variable �pid�
$ cat client2.c
/* $Id: serverc.c,v 1.2 2005/12/28 22:49:13 maine Exp $ */
/* $Name: $ */
/* serverc.c
* listen for a connection attempt to a getData server.
*
* The read/write/close routines are used in both clients and servers
* and are in a separate file.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for SunOS 4.1.x with NAG f90 1.2.
* Note, specifically that Sun Fortran puts character length
* information at the end of the argument list instead of right
* after the corresponding arguments.
*
* 28 Jun 93, Richard Maine: Version 1.5.
* 19 Nov 96, Richard Maine: casts to avoid gcc warnings.
* 16 Apr 98, Richard Maine: fix sol2 sigchld handling.
* 2 Dec 98, Richard Maine: fix addr len in gethostbyaddr.
* 21 Dec 05, Richard Maine: avoid sun-specific in_addr structure.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
/* Defines to make routines fortran-callable */
#define tcp_listen tcp_listen_
/* handler for SIGCHLD signal
* Needed so that child processes can be decently buried when they die.
* USG systems may need signal handler reestablished after use?
* but we are not using this for SOL2. */
void child_handler(sig)
int sig;
{
wait3(0, WNOHANG, 0);
return;
}
/* Listen for connection attempts on the specified port.
* Fork a child for each connection.
* Return connection information.
* Normal return value for child is 0.
* Positive return values are errors in parent.
* Negative return values are errors in child. */
void tcp_listen(port ,sock, client_ip, client_port, client_name,
server_ip, connect_num, error, client_name_len)
int *port, *sock, *client_port, *connect_num, *error;
int client_ip[3], server_ip[3];
char *client_name;
int client_name_len;
{
struct sockaddr_in server_addr, client_addr;
int lsock; /* Socket for listening */
int addr_size = sizeof(server_addr);
int addr_len;
int pid;
int keepalive;
struct hostent *host_ent;
int i;
unsigned char *ip_byte;
/* Divorce ourselves from controlling terminal. */
/* See Stevens, Chapter 13. */
if (fork()>0) exit(0); /* Parent returns; child continues. */
setsid(); /* Become session leader */
/* Create a socket, bind it to specified port and listen. */
*connect_num = 0;
lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lsock<0) {*error=1; return;}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(*port);
if (bind(lsock, (struct sockaddr *) &server_addr, addr_size) < 0)
{*error=2; return;}
if (listen(lsock,5) < 0) {*error=3; return;}
/* Accept connections and fork server instances. */
#ifdef SOL2
signal(SIGCHLD, SIG_IGN);
#else
signal(SIGCHLD, child_handler);
#endif
for (;;) {
addr_len = addr_size;
*sock = accept(lsock, (struct sockaddr *) &client_addr, &addr_len);
/* For reasons unclear to me, we get a lot of accept failures. */
/* perror says something about interrupted system call. */
/* We just ignore them for now. */
/* Can count them by moving the connect_num incrementation. */
if (*sock<0) continue; /* Connect failed */
*connect_num = *connect_num + 1;
/* Child process. Return connection data. */
if (fork()==0) {
close(lsock);
ip_byte = (unsigned char *) &client_addr.sin_addr;
client_ip[0] = *(ip_byte++);
client_ip[1] = *(ip_byte++);
client_ip[2] = *(ip_byte++);
client_ip[3] = *(ip_byte++);
*client_port = ntohs(client_addr.sin_port);
host_ent = gethostbyaddr((char *) &client_addr.sin_addr.s_addr,
sizeof(client_addr.sin_addr.s_addr), AF_INET);
for (i=0; i<client_name_len; i++) client_name[i] = ' ';
if (host_ent) {
for (i=0; (i<client_name_len) && (host_ent->h_name[i]!=0); i++)
client_name[i] = host_ent->h_name[i];
}
addr_len = addr_size;
getsockname(*sock, (struct sockaddr *) &server_addr, &addr_len);
ip_byte = (unsigned char *) &server_addr.sin_addr;
server_ip[0] = *(ip_byte++);
server_ip[1] = *(ip_byte++);
server_ip[2] = *(ip_byte++);
server_ip[3] = *(ip_byte++);
signal(SIGCHLD, SIG_DFL);
/* We want an error return from writes to closed sockets so
* we can shut down cleanly and log the termination.
* Without this, we quitely die.
*/
signal (SIGPIPE, SIG_IGN);
/* Enable keepalive packets to check socket connection. */
keepalive = 1;
setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive,
4);
/* Allow SIGIO/SIGURG to signal this process. */
/* Not currently needed. Retain as comment. */
/* if (fcntl(*sock, F_SETOWN, getpid()) < 0) return(-1); */
/* if (fcntl(*sock, F_SETFL, FASYNC) < 0) return (-2); */
*error=0;
return;
}
/* Parent process. Loop back for further connections. */
close(*sock);
}
}
// gcc -c -Wall -Wextra client2.c
$ cat server1.c
/* $Id: serverc.c,v 1.2 2005/12/28 22:49:13 maine Exp $ */
/* $Name: $ */
/* serverc.c
* listen for a connection attempt to a getData server.
*
* The read/write/close routines are used in both clients and servers
* and are in a separate file.
*
* This file has wrappers for the system routines that are
* not directly or easily callable from Fortran.
* Specific to system Fortran and C calling conventions.
*
* Version for SunOS 4.1.x with NAG f90 1.2.
* Note, specifically that Sun Fortran puts character length
* information at the end of the argument list instead of right
* after the corresponding arguments.
*
* 28 Jun 93, Richard Maine: Version 1.5.
* 19 Nov 96, Richard Maine: casts to avoid gcc warnings.
* 16 Apr 98, Richard Maine: fix sol2 sigchld handling.
* 2 Dec 98, Richard Maine: fix addr len in gethostbyaddr.
* 21 Dec 05, Richard Maine: avoid sun-specific in_addr structure.
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
/* Defines to make routines fortran-callable */
#define tcp_listen tcp_listen_
/* handler for SIGCHLD signal
* Needed so that child processes can be decently buried when they die.
* USG systems may need signal handler reestablished after use?
* but we are not using this for SOL2. */
void child_handler(sig)
int sig;
{
wait3(0, WNOHANG, 0);
return;
}
/* Listen for connection attempts on the specified port.
* Fork a child for each connection.
* Return connection information.
* Normal return value for child is 0.
* Positive return values are errors in parent.
* Negative return values are errors in child. */
void tcp_listen(port ,sock, client_ip, client_port, client_name,
server_ip, connect_num, error, client_name_len)
int *port, *sock, *client_port, *connect_num, *error;
int client_ip[3], server_ip[3];
char *client_name;
int client_name_len;
{
struct sockaddr_in server_addr, client_addr;
int lsock; /* Socket for listening */
int addr_size = sizeof(server_addr);
int addr_len;
int pid;
int keepalive;
struct hostent *host_ent;
int i;
unsigned char *ip_byte;
/* Divorce ourselves from controlling terminal. */
/* See Stevens, Chapter 13. */
if (fork()>0) exit(0); /* Parent returns; child continues. */
setsid(); /* Become session leader */
/* Create a socket, bind it to specified port and listen. */
*connect_num = 0;
lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lsock<0) {*error=1; return;}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(*port);
if (bind(lsock, (struct sockaddr *) &server_addr, addr_size) < 0)
{*error=2; return;}
if (listen(lsock,5) < 0) {*error=3; return;}
/* Accept connections and fork server instances. */
#ifdef SOL2
signal(SIGCHLD, SIG_IGN);
#else
signal(SIGCHLD, child_handler);
#endif
for (;;) {
addr_len = addr_size;
*sock = accept(lsock, (struct sockaddr *) &client_addr, &addr_len);
/* For reasons unclear to me, we get a lot of accept failures. */
/* perror says something about interrupted system call. */
/* We just ignore them for now. */
/* Can count them by moving the connect_num incrementation. */
if (*sock<0) continue; /* Connect failed */
*connect_num = *connect_num + 1;
/* Child process. Return connection data. */
if (fork()==0) {
close(lsock);
ip_byte = (unsigned char *) &client_addr.sin_addr;
client_ip[0] = *(ip_byte++);
client_ip[1] = *(ip_byte++);
client_ip[2] = *(ip_byte++);
client_ip[3] = *(ip_byte++);
*client_port = ntohs(client_addr.sin_port);
host_ent = gethostbyaddr((char *) &client_addr.sin_addr.s_addr,
sizeof(client_addr.sin_addr.s_addr), AF_INET);
for (i=0; i<client_name_len; i++) client_name[i] = ' ';
if (host_ent) {
for (i=0; (i<client_name_len) && (host_ent->h_name[i]!=0); i++)
client_name[i] = host_ent->h_name[i];
}
addr_len = addr_size;
getsockname(*sock, (struct sockaddr *) &server_addr, &addr_len);
ip_byte = (unsigned char *) &server_addr.sin_addr;
server_ip[0] = *(ip_byte++);
server_ip[1] = *(ip_byte++);
server_ip[2] = *(ip_byte++);
server_ip[3] = *(ip_byte++);
signal(SIGCHLD, SIG_DFL);
/* We want an error return from writes to closed sockets so
* we can shut down cleanly and log the termination.
* Without this, we quitely die.
*/
signal (SIGPIPE, SIG_IGN);
/* Enable keepalive packets to check socket connection. */
keepalive = 1;
setsockopt(*sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, 4);
/* Allow SIGIO/SIGURG to signal this process. */
/* Not currently needed. Retain as comment. */
/* if (fcntl(*sock, F_SETOWN, getpid()) < 0) return(-1); */
/* if (fcntl(*sock, F_SETFL, FASYNC) < 0) return (-2); */
*error=0;
return;
}
/* Parent process. Loop back for further connections. */
close(*sock);
}
}
// gcc -c -D_GNU_SOURCE -std=c99 -Wall -Wextra server1.c
$
--
Uno
|
|
0
|
|
|
|
Reply
|
Uno
|
9/10/2010 7:40:51 AM
|
|
Uno wrote:
> Gib Bogle wrote:
>
>> It's a challenge.
>
> Of course it's a challenge to communicate a half-world away. It's
> harder for me to understand this notion since I have little experience
> with client/server communication. I have clients and servers, but
> they're people like bob, judy, ginger, sandy or jimmy.
>
> I think the target for Gib's original thesis was for windows. Richard
> posted this for linux. My question is simple:
>
> What are you supposed to do with this and why does fortran make this
> easier?
Fortran doesn't make using sockets easier, just the reverse. I wanted a way to
communicate between my GUI written in C++ (using Qt, and multi-threaded) and my
number-crunching program, written in Fortran. There are other ways to achieve
this, but I decided to use sockets. It's very easy to get sockets working in C,
and calling the C socket functions from Fortran turns out to be a quick route to
having sockets in Fortran. If you are really interested in sockets and all
that, the classic book is Stevens's Unix Network Programming
http://www.kohala.com/start/unpv12e.html
|
|
0
|
|
|
|
Reply
|
Gib
|
9/12/2010 8:40:59 AM
|
|
|
47 Replies
1014 Views
(page loaded in 3.051 seconds)
|