Ubuntu 上 Fortran 网络编程实操指南
一、环境准备与总体思路
二、方式一 原生套接字 TCP 最小示例(ISO_C_BINDING 调用 C API)
program server
use, intrinsic :: iso_c_binding
implicit none
integer(c_int), parameter :: AF_INET = 2, SOCK_STREAM = 1, INADDR_ANY = 0
integer(c_int), parameter :: PORT = 12345
integer(c_int) :: server_fd, client_fd, n
type(c_ptr) :: addr_ptr
character(len=1), target :: addr_buf(16 + 2*4) ! 简化:容纳 sockaddr_in
integer(c_socklen_t) :: addr_len
character(len=1024) :: buf
interface
function socket(domain, type, protocol) bind(c, name="socket")
import c_int
integer(c_int), value :: domain, type, protocol
integer(c_int) :: socket
end function socket
function bind(sockfd, addr, addrlen) bind(c, name="bind")
import c_int, c_ptr, c_socklen_t
integer(c_int), value :: sockfd
type(c_ptr), value :: addr
integer(c_socklen_t), value :: addrlen
integer(c_int) :: bind
end function bind
function listen(sockfd, backlog) bind(c, name="listen")
import c_int
integer(c_int), value :: sockfd, backlog
integer(c_int) :: listen
end function listen
function accept(sockfd, addr, addrlen) bind(c, name="accept")
import c_int, c_ptr, c_socklen_t
integer(c_int), value :: sockfd
type(c_ptr), value :: addr
integer(c_socklen_t), intent(inout) :: addrlen
integer(c_int) :: accept
end function accept
function recv(sockfd, buf, len, flags) bind(c, name="recv")
import c_int, c_void, c_socklen_t
integer(c_int), value :: sockfd
type(c_ptr), value :: buf
integer(c_socklen_t), value :: len
integer(c_int), value :: flags
integer(c_int) :: recv
end function recv
function close(fd) bind(c, name="close")
import c_int
integer(c_int), value :: fd
integer(c_int) :: close
end function close
end interface
! 创建套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0_c_int)
if (server_fd < 0) stop 'socket failed'
! 准备地址(简化:直接把 sockaddr_in 放在 addr_buf 中,端口用 htons)
addr_buf = 0
call set_sin_family(addr_buf, AF_INET)
call set_sin_port (addr_buf, htons(PORT))
call set_sin_addr (addr_buf, INADDR_ANY)
addr_ptr = c_loc(addr_buf)
addr_len = 16 + 2*4 ! sizeof(struct sockaddr_in)
! 绑定、监听、接收
if (bind(server_fd, addr_ptr, addr_len) < 0) stop 'bind failed'
if (listen(server_fd, 5_c_int) < 0) stop 'listen failed'
print '("Server listening on port ", I0)', PORT
client_fd = accept(server_fd, addr_ptr, addr_len)
if (client_fd < 0) stop 'accept failed'
print *, 'Client connected'
n = recv(client_fd, c_loc(buf), int(size(buf), c_socklen_t), 0_c_int)
if (n > 0) print '("Received: ", A)', trim(buf(:n))
call close(client_fd)
call close(server_fd)
contains
subroutine set_sin_family(buf, fam)
character(len=1), intent(inout) :: buf(*)
integer(c_int), intent(in) :: fam
buf(1:4) = transfer(fam, buf(1:4))
end subroutine
subroutine set_sin_port(buf, port)
character(len=1), intent(inout) :: buf(*)
integer(c_int), intent(in) :: port
buf(5:8) = transfer(port, buf(5:8))
end subroutine
subroutine set_sin_addr(buf, addr)
character(len=1), intent(inout) :: buf(*)
integer(c_int), intent(in) :: addr
buf(9:12) = transfer(addr, buf(9:12))
end subroutine
end program server
program client
use, intrinsic :: iso_c_binding
implicit none
integer(c_int), parameter :: AF_INET = 2, SOCK_STREAM = 1
integer(c_int), parameter :: PORT = 12345
integer(c_int) :: sock, n
type(c_ptr) :: addr_ptr
character(len=1), target :: addr_buf(16 + 2*4)
character(len=1024) :: msg = "Hello from Fortran client!" // c_null_char
interface
function socket(domain, type, protocol) bind(c, name="socket")
import c_int
integer(c_int), value :: domain, type, protocol
integer(c_int) :: socket
end function socket
function connect(sockfd, addr, addrlen) bind(c, name="connect")
import c_int, c_ptr, c_socklen_t
integer(c_int), value :: sockfd
type(c_ptr), value :: addr
integer(c_socklen_t), value :: addrlen
integer(c_int) :: connect
end function connect
function send(sockfd, buf, len, flags) bind(c, name="send")
import c_int, c_void, c_socklen_t
integer(c_int), value :: sockfd
type(c_ptr), value :: buf
integer(c_socklen_t), value :: len
integer(c_int), value :: flags
integer(c_int) :: send
end function send
function close(fd) bind(c, name="close")
import c_int
integer(c_int), value :: fd
integer(c_int) :: close
end function close
end interface
! 创建套接字
sock = socket(AF_INET, SOCK_STREAM, 0_c_int)
if (sock < 0) stop 'socket failed'
! 准备地址(127.0.0.1)
addr_buf = 0
call set_sin_family(addr_buf, AF_INET)
call set_sin_port (addr_buf, htons(PORT))
call set_sin_addr (addr_buf, inet_addr("127.0.0.1"))
addr_ptr = c_loc(addr_buf)
if (connect(sock, addr_ptr, 16 + 2*4_c_socklen_t) < 0) stop 'connect failed'
n = send(sock, c_loc(msg), int(len_trim(msg), c_socklen_t), 0_c_int)
if (n < 0) print *, 'send failed'
call close(sock)
contains
subroutine set_sin_family(buf, fam)
character(len=1), intent(inout) :: buf(*)
integer(c_int), value :: fam
buf(1:4) = transfer(fam, buf(1:4))
end subroutine
subroutine set_sin_port(buf, port)
character(len=1), intent(inout) :: buf(*)
integer(c_int), value :: port
buf(5:8) = transfer(port, buf(5:8))
end subroutine
subroutine set_sin_addr(buf, addr)
character(len=1), intent(inout) :: buf(*)
integer(c_int), value :: addr
buf(9:12) = transfer(addr, buf(9:12))
end subroutine
end program client
三、方式二 使用 libcurl 发起 HTTP 请求
program http_get
use, intrinsic :: iso_c_binding
implicit none
interface
function curl_easy_init() bind(c, name="curl_easy_init")
import c_ptr
type(c_ptr) :: curl_easy_init
end function curl_easy_init
function curl_easy_setopt(curl, opt, param) bind(c, name="curl_easy_setopt")
import c_ptr, c_int
type(c_ptr), value :: curl
integer(c_int), value :: opt
type(c_ptr), value :: param
integer(c_int) :: curl_easy_setopt
end function curl_easy_setopt
function curl_easy_perform(curl) bind(c, name="curl_easy_perform")
import c_ptr, c_int
type(c_ptr), value :: curl
integer(c_int) :: curl_easy_perform
end function curl_easy_perform
subroutine curl_easy_cleanup(curl) bind(c, name="curl_easy_cleanup")
import c_ptr
type(c_ptr), value :: curl
end subroutine curl_easy_cleanup
function curl_easy_strerror(code) bind(c, name="curl_easy_strerror")
import c_ptr, c_int
integer(c_int), value :: code
type(c_ptr) :: curl_easy_strerror
end interface
type(c_ptr) :: curl
integer(c_int) :: res
character(len=:), allocatable :: url
character(len=256) :: errbuf
curl = curl_easy_init()
if (.not. c_associated(curl)) then
print *, "curl_easy_init failed"
stop 1
end if
url = "http://example.com" // c_null_char
call curl_easy_setopt(curl, 10002_c_int, c_loc(url)) ! CURLOPT_URL
call curl_easy_setopt(curl, 52_c_int, 1_c_int) ! CURLOPT_FOLLOWLOCATION
call curl_easy_setopt(curl, 199_c_int, c_loc(errbuf)) ! CURLOPT_ERRORBUFFER
res = curl_easy_perform(curl)
if (res /= 0) then
print '("curl_easy_perform failed: ", A)', trim(errbuf)
end if
call curl_easy_cleanup(curl)
end program http_get
四、编译链接与运行要点
五、常见坑与优化建议