2

I am working on creating a way to have multiple Fortran processes communicate with each other. This will be for a simulation where one machine is running the simulation processes and another machine (or possibly cluster of machines) will be generating synthetic data for the simulation processes.

ie: The simulation process(sp) makes a request to the generation process (gp) for a piece of information. The gp either finds the requested info or creates it and returns it to the sp.

Both of these processes are pretty intensive which is why they will be split between multiple machines.

Anyway, after doing a bit of research the best method I could come up with for this IPC was to use TCP along with IP. After doing a bit more research I was able to come up with the following code (taken from here):

Module

MODULE MSockets
   ! Interface to libmsock
   ! A library for TCP/IP client-server applications on Unix by Muhammad A Muquit
   ! http://www.muquit.com/muquit/software/libmsock/libmsock.html
   USE ISO_C_BINDING
   IMPLICIT NONE
   PUBLIC

   INTEGER, PARAMETER :: CC_SIZE_T=C_INT

   INTERFACE
      ! Interfaces for the main functions

      ! int close(int fildes)
      ! This one is not in libmsock technically but is needed to close sockets:
      FUNCTION CloseSocket(fildes) BIND(C,NAME="close")  RESULT(error)
         IMPORT
         INTEGER(C_INT), VALUE :: fildes
         INTEGER(C_INT) :: error
      END FUNCTION          
      ! int ServerSocket(u_short port,int max_servers);
      FUNCTION ServerSocket(port,max_servers) BIND(C,NAME="ServerSocket")  RESULT(sockfd)
         IMPORT
         INTEGER(C_SHORT), VALUE :: port
         INTEGER(C_INT), VALUE :: max_servers
         INTEGER(C_INT) :: sockfd
      END FUNCTION
      ! int ClientSocket(char *netaddress,u_short port);
      FUNCTION ClientSocket(netaddress,port) BIND(C,NAME="ClientSocket")  RESULT(sockfd)
         IMPORT
         CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: netaddress
         INTEGER(C_SHORT), VALUE :: port
         INTEGER(C_INT) :: sockfd
      END FUNCTION
      ! int sockGets(int sockfd,char *str,size_t count);
      FUNCTION sockGets(sockfd,str,count) BIND(C,NAME="sockGets")  RESULT(length)
         IMPORT
         INTEGER(C_INT), VALUE :: sockfd
         CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: str
         INTEGER(CC_SIZE_T), VALUE :: count
         INTEGER(C_INT) :: length ! Bytes read
      END FUNCTION
      ! int sockRead(int sockfd,char *str,size_t count);
      FUNCTION sockRead(sockfd,str,count) BIND(C,NAME="sockRead")  RESULT(error)
         IMPORT
         INTEGER(C_INT), VALUE :: sockfd
         INTEGER(CC_SIZE_T), VALUE :: count
         CHARACTER(C_CHAR), DIMENSION(count), INTENT(OUT) :: str
         INTEGER(C_INT) :: error
      END FUNCTION
      ! int sockPuts(int sockfd,char *str);
      FUNCTION sockPuts(sockfd,str) BIND(C,NAME="sockPuts")  RESULT(error)
         IMPORT
         INTEGER(C_INT), VALUE :: sockfd
         CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: str ! NULL terminated
         INTEGER(C_INT) :: error
      END FUNCTION
      ! int sockWrite(int sockfd,char *str,size_t count);
      FUNCTION sockWrite(sockfd,str,count) BIND(C,NAME="sockWrite")  RESULT(error)
         IMPORT
         INTEGER(C_INT), VALUE :: sockfd
         INTEGER(CC_SIZE_T), VALUE :: count
         CHARACTER(C_CHAR), DIMENSION(count), INTENT(IN) :: str
         INTEGER(C_INT) :: error
      END FUNCTION
   END INTERFACE

END MODULE

Server Program

PROGRAM MSockets_Server
   USE ISO_C_BINDING
   USE MSockets
   IMPLICIT NONE

   INTEGER(C_SHORT) :: port
   INTEGER(C_INT) :: sockfd
   CHARACTER(KIND=C_CHAR, LEN=1024) :: buffer
   INTEGER(CC_SIZE_T) :: count
   INTEGER(C_INT) :: length, error

   port=HUGE(port) ! Most likely unused port
   sockfd=ServerSocket(port,2_c_int) ! It will return only once a connection is made
   IF(sockfd<0) STOP "Failed to open server socket"

   WRITE(*,*) "Client connected!"
   DO
      length=sockGets(sockfd,buffer,INT(LEN(buffer),CC_SIZE_T))
      IF(length<0) EXIT
      IF(length>0) WRITE(*,*) "Client said: ", buffer(1:length)
   END DO
   WRITE(*,*) "Client disconnected"

   error=CloseSocket(sockfd)
   IF(error<0) STOP "Failed to close server socket"

END PROGRAM

Client Program

PROGRAM MSockets_Client
   USE ISO_C_BINDING
   USE MSockets
   IMPLICIT NONE

   INTEGER(C_SHORT) :: port
   INTEGER(C_INT) :: sockfd
   CHARACTER(KIND=C_CHAR, LEN=1024) :: buffer
   INTEGER(CC_SIZE_T) :: count
   INTEGER(C_INT) :: length, error

   port=HUGE(port) ! Most likely unused port
   sockfd=ClientSocket("localhost",port)
   IF(sockfd<0) STOP "Failed to open client socket"

   WRITE(*,*) "Enter what you want to tell the server:"
   DO
      READ(*,"(A)") buffer
      IF(buffer=="quit") EXIT ! Last input
      error=sockPuts(sockfd,TRIM(buffer)//C_NEW_LINE)
      IF(error<0) STOP "Server died!"
   END DO

   error=CloseSocket(sockfd)
   IF(sockfd<0) STOP "Failed to close client socket"   

END PROGRAM

These programs rely on the c library which was taken from here in order to do the TCP/IP communications. Everything compiles fine and I can successfully start the server; however, when I try to start the client I get the following error:

ClientSocket(): Invalid network address
STOP Failed to open client socket

I know the issue is coming from the "localhost" in:

sockfd=ClientSocket("localhost",port)

but I cannot for the life of me figure out why. I have also tried using the internal ip address of the computer and using the public ip address and trying to use multiple machines (run server on one and client on another while operating over a peer-to-peer network) and always get the same error. Does anyone have any idea whats going on (or if you think there is a better solution for my fortran IPC give me that as well)?

Thanks! Andrew

Also, for what it is worth I am running this on Mac OSX 10.9 using gfortran. The commands I use to compile are:

gfortran -c sockets.f90
gfortran client.f90 -o client.x -L"../../../libmsock/libmsock/" -lmsock -g
gfortran server.f90 -o server.x -L"../../../libmsock/libmsock/" -lmsock -g

where the names should pretty clearly indicate which code goes with which.

Andrew
  • 693
  • 6
  • 19
  • 1
    I would probably try to solve this problem with MPI rather than homebrewing a TCP client/server architecture. – casey Jul 29 '14 at 18:52
  • Thanks for the suggestion, I am taking a look at that now. – Andrew Jul 29 '14 at 19:23
  • @Stefan That was it! Thanks very much. If you want to write your comment as an answer I will accept it. – Andrew Jul 29 '14 at 19:54

1 Answers1

2

The problem is the different handling of strings in C and Fortran.

You pass "localhost" as a Fortran String, whose end is determined by a fixed (in this case implied) length. In C, strings have to end with a NUL. This can be achieved by writing

sockfd=ClientSocket("localhost"//C_NULL_CHAR, port)

where C_NULL_CHAR is defined in ISO_C_BINDING module.

Stefan
  • 2,460
  • 1
  • 17
  • 33