Fortran Coder

标题: fortran语言实现socket通信/winsock [打印本页]

作者: YHWHIT    时间: 2015-7-27 20:38
标题: fortran语言实现socket通信/winsock
fortran语言实现tcpip功能,用socket选项实现实现数据交换。比如在同一台电脑中同时使用fortran语言编写的程序和c++语言编写的程序,实现两个程序之间的数据交换。c语言中有socket选项,fortran语言如何实现呢?

作者: vvt    时间: 2015-7-27 22:02
首先你要知道,语言本身并没有,也不会有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协议请照猫画虎。

[Fortran] 纯文本查看 复制代码
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
vvt 发表于 2015-7-27 22:02
首先你要知道,语言本身并没有,也不会有socket通讯功能。
其实C语言本身也不规定如何使用 socket,它只规 ...

非常感谢您.
我现在刚开始接触这方面的知识,所以看到c++书中有socket这类的东西,而fortran语言书中却没有介绍,所以有这种疑惑。
我用的是win7系统,然后装了intel fortran,集成到vs上面。
这几天在查阅这方面的资料,网上这方面相关的资料很少。请问您有这方面相关的资料吗?或者说我去看一些什么样的资料?
不胜感谢!
作者: vvt    时间: 2015-7-28 08:19
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
vvt 发表于 2015-7-28 08:19
C++的书讲到 socket 也是正常的,但应该会告诉你,这是具体实现,而非 C++ 的语法。
我不清楚你看到的部分 ...

嗯嗯 好的,谢谢指教。我先去学习winsock。
作者: YHWHIT    时间: 2015-8-10 15:18
vvt 发表于 2015-7-28 08:19
C++的书讲到 socket 也是正常的,但应该会告诉你,这是具体实现,而非 C++ 的语法。
我不清楚你看到的部分 ...


[Fortran] 纯文本查看 复制代码

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

我的习惯是用 Equivalence 方便处理:
[Fortran] 纯文本查看 复制代码

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

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


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


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

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

发送方是matlab,代码如下:
fwrite(Tcpsever,num2str(dpredout'));
其中dpredout是一个1*4向量,转化为字符串发送。
按理来说后面的应该是空格才对,却不知为何乱码了。然后我想读取空格前面的那些数据,然后存到一个向量里面。这该怎么做?
作者: 楚香饭    时间: 2015-8-11 12:58
我觉得最好不要用字符串发送。都是数据,用二进制多好。
干嘛要转换成字符串,然后接收以后再转换回来呢?多此一举不是。

对于后面有二进制的数据,你可以直接用 fortran read 。
也可以控制 matlab,发送完字符串以后,接着发送一系列的 \0 或空格。都可以
作者: YHWHIT    时间: 2015-8-11 19:49
楚香饭 发表于 2015-8-11 12:58
我觉得最好不要用字符串发送。都是数据,用二进制多好。
干嘛要转换成字符串,然后接收以后再转换回来呢? ...

嗯哪  谢谢
对二进制不太了解,我先用字符串做做。
作者: YHWHIT    时间: 2015-8-13 17:07
楚香饭 发表于 2015-8-11 12:58
我觉得最好不要用字符串发送。都是数据,用二进制多好。
干嘛要转换成字符串,然后接收以后再转换回来呢? ...

代码如下:
        ! 接收数据
      ireceivelen=recv(isender,ClientServerMessage%buffer,500,0)
         if(ireceivelen<0)then
          write(*,*)"连接失败"
          stop
         else
             dpred=ClientServerMessage%buffer
             write(*,*)"sever say:"
             write(*,*) dpred
             write(*,*) ClientServerMessage%buffer
         end if
      
      
      ! 发送数据
      write(*,*)"please enter message:"
      ClientServerMessage%bufferout=rfoutput
      isendlen=send(isender,ClientServerMessage%bufferout,500,0)
      write(*,*) ClientServerMessage%bufferout
      if(isendlen<0)then
          write(*,*)"连接失败"
      end if为什么先发送数据,后接收不好使?先发送那面收不到
如果改为先接收(后面不变),再发送,再接收却又好使,那面也能收到。


作者: fcode    时间: 2015-8-13 17:18
winsock 的发送接收,有 TCP 和 UPD 两种。
其中 UDP 是没有反馈信息的,发送方只管发送,不知道接收方是否接收到。
而这又分为阻塞和非阻塞两种。
如果你使用的是非阻塞方式,那可能需要循环一段时间,来判断是否接收到。
即:
发送方发送后,循环等待回应。
接收方循环接收,直到收到,发送回应。

如果你需要一边发送,一边接收(而且两者还有关联)。那么 TCP 其实更适合你。

作者: YHWHIT    时间: 2015-8-14 09:15
fcode 发表于 2015-8-13 17:18
winsock 的发送接收,有 TCP 和 UPD 两种。
其中 UDP 是没有反馈信息的,发送方只管发送,不知道接收方是否 ...

嗯那,我用的就是tcp
只是同样的代码,如果我先发送,再接收,不成功
如果把发送代码复制在前,接收,然后再发送,却是成功了。




欢迎光临 Fortran Coder (http://bbs.fcode.cn/) Powered by Discuz! X3.2