YHWHIT 发表于 2015-7-27 20:38:05

fortran语言实现socket通信/winsock

fortran语言实现tcpip功能,用socket选项实现实现数据交换。比如在同一台电脑中同时使用fortran语言编写的程序和c++语言编写的程序,实现两个程序之间的数据交换。c语言中有socket选项,fortran语言如何实现呢?

vvt 发表于 2015-7-27 22:02:34

首先你要知道,语言本身并没有,也不会有socket通讯功能。
其实C语言本身也不规定如何使用 socket,它只规定逻辑关系和数学计算的写法。
具体到各平台下的实现,是编译器和第三方库的事情。
换过来说,socket 是一个网络通讯规范,本身也不规定如何使用。像 winsock 一类的库才是它的实现,类似的还有 Java 的 Jsocket 接口等等。注意 winsock 不等于 socket。
理解了这两个问题,才能回到你的问题上。如果你是在 windows 上 intel fortran 通过 winsock 来实现的话,那么可以 Use ws2_32 来使用 windows 标准的 ws2_32 的 API 库实现。
如果你是在其他平台(Linux),使用其他编译器,通过其他 socket 的接口(如LSocket,ServerSocket等)实现,那么用法会不一样。这一点请你深刻理解!

以下是我曾经写过的一个 UDP 协议的封装。仅供参考,具体细节请翻阅 winsock 文档!

注意它有使用局限性:
1.仅限windows,使用 winsocket接口
2.仅限Intel fortran
3.仅限 UDP 协议,TCP/IP协议请照猫画虎。

Module SocketMod
Use ws2_32 , ID_Cancel => IDCancel
Use kernel32 , only : sleep
Implicit None
Type t_sockaddr_union
    Sequence
    union
      map
      Type (t_sockaddr_in) sockaddrin
      end map
      map
      Type (t_sockaddr) sockaddr
      end map
    end union
End Type t_sockaddr_union
Integer , parameter , private :: winsock_v2_2 = x'202' , success = 0 !版本
contains

Integer Function Socket_UDP_Conn( host , port )
    Character(len=15) host
    Integer(kind=2) port
    type (t_wsadata) WSAInfo !版本信息结构
    !integer receivelen , iSendLen   , ic
    type (t_sockaddr_union) ConnectionInfo !Client_add
    Integer hSocket , status , i
    Socket_UDP_Conn = 0
    status = WSAStartup(winsock_v2_2, WSAInfo)
    If (status/=success) return
! 创建套接字
    hSocket = Socket( af_inet , SOCK_DGRAM , ipproto_udp )
    If (hSocket==invalid_socket) goto 999
    ConnectionInfo.sockaddrin.sin_family = af_inet
    ConnectionInfo.sockaddrin.sin_port = htons( port )
    ConnectionInfo.sockaddrin.sin_addr%s_addr = inet_addr( host( 1 : len_trim(host) ) )
    status = Connect(hSocket, %ref(ConnectionInfo%sockaddr), sizeof(ConnectionInfo%sockaddr))
    If (status==socket_error) return      
    i = 1
    status = ioctlsocket( hSocket , FIONBIO , loc(1) )
    Socket_UDP_Conn = hSocket
    return
999 status = WSACleanup()
End Function Socket_UDP_Conn

Integer Function Socket_UDP_Disconn( hSocket )
    integer :: hSocket
    Socket_UDP_Disconn = CloseSocket( hSocket )
    Socket_UDP_Disconn = WSACleanup()
End Function Socket_UDP_Disconn

Integer Function Socket_UDP_Send( hSocket , c , len_c )
    Character(len=*) :: c
    Integer :: len_c   
    Integer hSocket , iSendLen
    Socket_UDP_Send = 0
    iSendLen = Send( hSocket , c , len_c , 0 )
    If ( iSendLen < 0 ) return
    Socket_UDP_Send = 1
End Function Socket_UDP_Send

Integer Function Socket_UDP_Recv( hSocket , c , len_c , timeout )
    Character(len=*) :: c
    Integer :: len_c , timeout   
    Integer :: hSocket , receivelen , i
    Socket_UDP_Recv = 0
    c = char(0)
    do i = 1 , timeout
      receivelen = Recv( hSocket , c , len_c , 0 )
      If ( receivelen == len_c ) goto 999
      call sleep(10)
    end do
    return
999 Socket_UDP_Recv = 1
End Function Socket_UDP_Recv

End Module SocketMod

YHWHIT 发表于 2015-7-27 22:41:31

vvt 发表于 2015-7-27 22:02
首先你要知道,语言本身并没有,也不会有socket通讯功能。
其实C语言本身也不规定如何使用 socket,它只规 ...

非常感谢您.
我现在刚开始接触这方面的知识,所以看到c++书中有socket这类的东西,而fortran语言书中却没有介绍,所以有这种疑惑。
我用的是win7系统,然后装了intel fortran,集成到vs上面。
这几天在查阅这方面的资料,网上这方面相关的资料很少。请问您有这方面相关的资料吗?或者说我去看一些什么样的资料?
不胜感谢!

vvt 发表于 2015-7-28 08:19:01

C++的书讲到 socket 也是正常的,但应该会告诉你,这是具体实现,而非 C++ 的语法。
我不清楚你看到的部分,是否是通过 winsock 接口的
(正如我前面所说,winsock不等于socket,前者是后者的一个实现方式,而后者是一种通讯规范。
就好像 USB 是通讯规范,而 U 盘是具体的实现。实际上,一些鼠标,风扇,充电器也使用 USB接口)

你可以学习底层的 socket 通讯协议,但是这似乎没什么卵用。因为你很难在底层上实现 socket。
大多数应用程序的程序员,都是使用 winsock 或 Jsocket 或其他实现来搞。

例如 winsock,是windows封装的socket协议接口,注意是接口。
那么,如果你打算用 winsock,你可以查看winsock相关的资料,例如 MSDN library,找 winsock 的资料时,请忘记 Fortran
等学会 winsock 以后,再来学习如何用 Fortran 来调用它。

其实 winsock 一共只有20多个函数,常用的只有七八个,如我的代码里提到的
WSAStartup
Socket
Connect
ioctlsocket
WSACleanup
Send
Recv
,还是比较简单的。(任何语言通过 winsock 接口操作,使用到的函数都是相同的,只是写法会有差异)

YHWHIT 发表于 2015-7-28 09:18:44

vvt 发表于 2015-7-28 08:19
C++的书讲到 socket 也是正常的,但应该会告诉你,这是具体实现,而非 C++ 的语法。
我不清楚你看到的部分 ...

嗯嗯 好的,谢谢指教。我先去学习winsock。

YHWHIT 发表于 2015-8-10 15:18:27

vvt 发表于 2015-7-28 08:19
C++的书讲到 socket 也是正常的,但应该会告诉你,这是具体实现,而非 C++ 的语法。
我不清楚你看到的部分 ...


subroutine exchangedata(iDof,iblock,dpred,rfoutput)
      use ws2_32
      use kernel32

      character*500 dpred
      character*500 d
      integer iDof,iblock
      character*500 rfoutput
      dimension fdpred(iblock)


      type t_sockaddr_union
          sequence
          union
            map
                  type(t_sockaddr_in)sockAddrIn
            end map
            map
                  type(t_sockaddr)sockAddr
            end map
          end union
      end type t_sockaddr_union

      parameter(iwinsock_v2_2=x'202',isuccess=0)
      type(t_wsadata) WSAInfo !版本信息结构
      type (t_sockaddr_union) ConnectionInfo !Client_add

      type T_client_server_message
          union
            map
                  character*(500) buffer
            end map
            map
                  character*(500) bufferout
            end map
          end union
      end type
      type(T_client_server_message) ClientServerMessage

      Character(len=15) host
      Integer(kind=2) iport


c**************************************************************************      

         ! Initialize Winsock v2
         istatus=WSAStartup(iwinsock_v2_2,WSAInfo)
      If (istatus.NE.isuccess)then
         write(*,*)'WSAStartup-',istatus
      stop
      end if

         ! 创建套接字
      isender = socket( af_inet , SOCK_STREAM , ipproto_tcp )
      if (isender.EQ.invalid_socket) then
       write(*,*)'socket-',invalid_socket
       istatus=WSACleanup()
      end if

      !设置连接地址
      host='127.0.0.1'
      iport=5001


      ConnectionInfo%sockaddrin%sin_family = af_inet
      ConnectionInfo%sockaddrin%sin_port = htons( iport )
      ConnectionInfo%sockaddrin%sin_addr%s_addr =
   *      inet_addr( host( 1 : len_trim(host) ) )
      istatus = connect(isender, %ref(ConnectionInfo%sockaddr),
   *         sizeof(ConnectionInfo%sockaddr))
      If (istatus.EQ.socket_error) then
      write(*,*)"连接失败"
      end if
c*****************************************************************************************
      ! 接收数据
      ireceivelen=recv(isender,ClientServerMessage%buffer,500,0)
         if(ireceivelen<0)then
          write(*,*)"连接失败"
          stop
         else
             dpred=ClientServerMessage%buffer
             write(*,*)"sever say:"
             write(*,*) dpred
             read(dpred,*) fdpred
             write(*,*) fdpred
         end if

c***********************************************************************************         

         ! 发送数据
      write(*,*)"please enter message:"
      write(*,*) rfoutput
      ClientServerMessage%bufferout=rfoutput
      isendlen=send(isender,ClientServerMessage%bufferout,500,0)
      write(*,*) ClientServerMessage%bufferout
      rfoutput=ClientServerMessage%bufferout
      write(*,*) rfoutput
      if(isendlen<0)then
          write(*,*)"连接失败"
      end if

             istatus=closesocket(isender)
             istatus=WSACleanup()

      end subroutine
您好,这是我看着网上编的一个子程序,但是在传输数据时出现了问题。传回来的字符串类型,但是字符串后面有很多空格。然后我怎样将字符串类型转化转化为一个向量。同时一个数据向量怎样发送出去呢?

楚香饭 发表于 2015-8-10 15:33:50

你定义的 character*(500) bufferout 就是字符串啊,所以当然就是字符串了。
fortran 直接操作字符串会在后面自动加空格。

我的习惯是用 Equivalence 方便处理:

Type T_DAQ
    SEQUENCE
    character(len=3) :: cFlagStart !// 一般会有帧头,但不是必须的
    !// 此处是你的各数据,用 type 会更方便多种不同数据放在一起
    character(len=3) :: cFlagEnd !// 一般会有帧尾,但不是必须的
End Type T_DAQ

type(T_DAQ) , private :: stDAQ !// 这是你真正的数据
character(len=80) , private :: C_DAQ !// 这是为了方便书写
Equivalence( C_DAQ , stDAQ ) !// 让 C_DAQ 和 stDAQ 公用一个内存地址!这样,当你传输时,使用 C_DAQ,而使用时,使用 stDAQ

YHWHIT 发表于 2015-8-10 16:11:27

楚香饭 发表于 2015-8-10 15:33
你定义的 character*(500) bufferout 就是字符串啊,所以当然就是字符串了。
fortran 直接操作字符串会在后 ...

恩恩,您的这个有点高级,看不太懂。
我的Log文件里面的文本是这样的:


sever say:
0.000123930.000123930.000123930.00012393
@    牫=    l薰   8?   
葕    膴    ?=   :=    `:=   薰   `?   >=    @==         
   ?       @       @?    €?    `?    @p3                              
    ?       ?6   ?    8&   ?                                   劺4
          ?6                  ?              `B+           €?    来?
      拇?   `?    却?   €B    ?   q〩          ?              ?
                     ?   


我只是需要前面的四个数,然后后面的从matlab输过来时时空格,不知为啥就乱码了。我只想提取前面的数据组成一个实型的向量,这个该怎么解决一下?

vvt 发表于 2015-8-11 08:24:59

我不知道你的发送方是什么,代码是怎么样的。
从表现来看,后面的不是文本内容,是二进制内容。

YHWHIT 发表于 2015-8-11 09:03:18

vvt 发表于 2015-8-11 08:24
我不知道你的发送方是什么,代码是怎么样的。
从表现来看,后面的不是文本内容,是二进制内容。 ...

发送方是matlab,代码如下:
fwrite(Tcpsever,num2str(dpredout'));
其中dpredout是一个1*4向量,转化为字符串发送。
按理来说后面的应该是空格才对,却不知为何乱码了。然后我想读取空格前面的那些数据,然后存到一个向量里面。这该怎么做?
页: [1] 2
查看完整版本: fortran语言实现socket通信/winsock