阅读量:3
Ubuntu 上 Fortran 网络编程实操指南
一、环境准备与总体思路
- 安装编译器与常用库:
- 编译器:sudo apt update && sudo apt install gfortran
- HTTP 等协议:sudo apt install libcurl4-openssl-dev
- 实现路径:
- 原生套接字:通过 ISO_C_BINDING 调用 C 的 socket API(socket/bind/listen/accept/send/recv)实现 TCP/UDP。
- 高级库:使用 libcurl 发起 HTTP/HTTPS/FTP 等请求,开发效率高、可移植性好。
二、方式一 原生套接字 TCP 最小示例(ISO_C_BINDING 调用 C API)
- 思路要点
- 用 c_int、c_char、c_void、c_socklen_t 等 C 互操作类型;用 c_loc 传结构体指针;用 sizeof 取大小;字符串需以 C_NULL_CHAR 结尾;所有系统调用需检查返回值。
- 服务器端 server.f90(IPv4/TCP,监听 12345)
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
- 客户端 client.f90(连接 127.0.0.1:12345 并发送一行)
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
- 编译与运行
- 编译:gfortran -o server server.f90 与 gfortran -o client client.f90
- 运行:终端1执行 ./server;终端2执行 ./client
- 说明
- 上述示例为教学简化版,直接把 sockaddr_in 放在连续字节缓冲中并通过 c_loc 传递;生产代码建议封装 struct sockaddr_in 的 C 互操作派生类型,更直观且不易出错。
三、方式二 使用 libcurl 发起 HTTP 请求
- 安装库:sudo apt install libcurl4-openssl-dev
- 示例 http_get.f90(HTTP GET 并打印错误码)
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
- 编译与运行
- 编译:gfortran -o http_get http_get.f90 -lcurl
- 运行:./http_get
- 提示
- 若需要 HTTPS,安装 libcurl4-openssl-dev 即可;如需更详细错误信息,可使用 curl_easy_strerror 获取字符串描述。
四、编译链接与运行要点
- 套接字示例通常只需 gfortran 源文件 即可编译;若使用 libcurl,务必加上 -lcurl 链接选项。
- 运行前确认防火墙放行端口(示例为 12345/tcp):sudo ufw allow 12345/tcp。
- 所有系统调用与库函数调用都应检查返回值并做错误处理(如端口占用、连接失败、解析失败等)。
五、常见坑与优化建议
- 字符与缓冲区
- C 字符串需以 C_NULL_CHAR 结尾;Fortran 固定长度字符缓冲区接收数据后,用 len_trim 或记录实际 recv 字节数处理。
- 字节序与地址
- 端口需使用 htons 转为网络字节序;IPv4 地址可用 inet_addr(“127.0.0.1”) 得到网络序整数。
- 结构体与指针
- 生产代码建议为 struct sockaddr_in 定义明确的 C 互操作类型,避免手工填充字节缓冲带来的可维护性问题。
- 并发与扩展
- 高并发可结合 多进程/多线程 或并行库(如 OpenCoarrays)与 socket 协同设计;跨协议与跨平台需求优先考虑 libcurl 等成熟库。
以上就是关于“Ubuntu上Fortran网络编程怎么操作”的相关介绍,筋斗云是国内较早的云主机应用的服务商,拥有10余年行业经验,提供丰富的云服务器、租用服务器等相关产品服务。云服务器资源弹性伸缩,主机vCPU、内存性能强悍、超高I/O速度、故障秒级恢复;电子化备案,提交快速,专业团队7×24小时服务支持!
简单好用、高性价比云服务器租用链接:https://www.jindouyun.cn/product/cvm