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.