一个简单的TCP服务器
1.只接收一个连接然后自动关闭的服务端代码
1 -module(socket_server). 2 3 -export([start_nano_server/0]). 4 5 start_nano_server() -> 6 % 监听来自端口2345上的连接,消息打包规则{packet, 4} -> 每个应用程序都是从一个4字节长的头部开始 7 % gen_tcp:listen -> {ok, ListenSocket} | {error, Reason}, ListenSocket为监听套接字 8 {ok, ListenSocket} = gen_tcp:listen(2345, [binary, {packet, 4}, 9 {reuseaddr, true}, 10 {active, true}]), 11 % gen_tcp:accept(Listensocket)调用时,程序会暂停并等待一个连接 12 % 当一个新的连接建立起来时会返回变量Socket,该变量绑定到新建连接的套接字上 13 % 通过Socket套接字,服务器就可以和发起连接的客户机进行通信 14 {ok, Socket} = gen_tcp:accept(ListenSocket), 15 % 关闭监听套接字,此后服务器不会继续处于监听状态,也无法建立新的连接,但并不影响已经建立的连接套接字 16 % 只是阻止新连接的建立 17 gen_tcp:close(ListenSocket), 18 loop(Socket). 19 20 loop(Socket) -> 21 receive 22 {tcp, Socket, Bin} -> 23 io:format("Server received binary = ~p~n", [Bin]), 24 Str = binary_to_term(Bin), % 对接收数据解码(反整编) 25 io:format("Server(unpacked) ~p~n", [Str]), 26 Reply = lib_misc:string2value(Str), % 对回应数据进行编码(整编) 27 io:format("Server replying = ~p~n", [Reply]), 28 % send(Socket, Packet) -> ok | {error, Reason}, Packet -> iodata() 29 gen_tcp:send(Socket, term_to_binary(Reply)), 30 loop(Socket); 31 {tcp_closed, Socket} -> 32 io:format("Server socket closed~n") 33 end.
2.顺序型服务器
1 %% 顺序型服务器 2 -module(socket_server_order). 3 4 -export([start_nano_server/0]). 5 6 start_nano_server() -> 7 % 监听来自端口2345上的连接,消息打包规则{packet, 4} -> 每个应用程序都是从一个4字节长的头部开始 8 % gen_tcp:listen -> {ok, ListenSocket} | {error, Reason}, Listen为监听套接字 9 {ok, ListenSocket} = gen_tcp:listen(2345, [binary, {packet, 4}, 10 {reuseaddr, true}, 11 {active, true}]), 12 req_loop(ListenSocket). 13 14 req_loop(ListenSocket) -> 15 {ok, Socket} = gen_tcp:accept(ListenSocket), 16 loop(Socket), 17 req_loop(ListenSocket). 18 19 loop(Socket) -> 20 receive 21 {tcp, Socket, Bin} -> 22 io:format("Server received binary = ~p~n", [Bin]), 23 Str = binary_to_term(Bin), 24 io:format("Server(unpacked) ~p~n", [Str]), 25 Reply = lib_misc:string2value(Str), 26 io:format("Server replying = ~p~n", [Reply]), 27 gen_tcp:send(Socket, term_to_binary(Reply)), 28 loop(Socket); 29 {tcp_closed, Socket} -> 30 io:format("Server socket closed~n") 31 end.
3.并发型服务器
1 %% 并发型服务器 2 -module(socket_server_order). 3 4 -export([start_nano_server/0]). 5 6 start_nano_server() -> 7 % 监听来自端口2345上的连接,消息打包规则{packet, 4} -> 每个应用程序都是从一个4字节长的头部开始 8 % gen_tcp:listen -> {ok, ListenSocket} | {error, Reason}, Listen为监听套接字 9 {ok, ListenSocket} = gen_tcp:listen(2345, [binary, {packet, 4}, 10 {reuseaddr, true}, 11 {active, true}]), 12 spawn(fun() -> par_connect(ListenSocket) end). 13 14 par_connect(ListenSocket) -> 15 {ok, Socket} = gen_tcp:accept(ListenSocket), 16 spawn(fun() -> par_connect(ListenSocket) end), 17 loop(Socket). 18 19 loop(Socket) -> 20 receive 21 {tcp, Socket, Bin} -> 22 io:format("Server received binary = ~p~n", [Bin]), 23 Str = binary_to_term(Bin), 24 io:format("Server(unpacked) ~p~n", [Str]), 25 Reply = lib_misc:string2value(Str), 26 io:format("Server replying = ~p~n", [Reply]), 27 gen_tcp:send(Socket, term_to_binary(Reply)), 28 loop(Socket); 29 {tcp_closed, Socket} -> 30 io:format("Server socket closed~n") 31 end.
4.客户端代码
1 -module(socket_client). 2 3 -export([nano_client_eval/1]). 4 5 nano_client_eval(Str) -> 6 {ok, Socket} = gen_tcp:connect("localhost", 2345, 7 [binary, {packet, 4}]), 8 ok = gen_tcp:send(Socket, term_to_binary(Str)), 9 receive 10 {tcp, Socket, Bin} -> 11 io:format("Client received binary = ~p~n", [Bin]), 12 Val = binary_to_term(Bin), 13 io:format("Client result = ~p~n", [Val]), 14 gen_tcp:close(Socket) 15 end.
5.lib_misc模块
1 -module(lib_misc). 2 3 -export([string2value/1]). 4 5 string2value(L) -> string2value(L, []). 6 string2value([], N) -> list_to_tuple(lists:reverse(N)); 7 string2value([H|T], N) -> string2value(T, [H|N]).
6.测试
6.1只接收一个连接然后自动关闭的服务端代码测试
6.2顺序型服务器测试
6.3并发型测试
ps:顺序型服务器相对并发型服务器的区别在于:顺序型服务器忙于服务一个现存的连接时,如果又有新的客户机尝试连接服务器,那么这个连接就会在服务器排队,直到服务器完成对现有的服务为止,如果等待队列中的连接数目超过了监听套接字的能力,那么这个连接就会被拒绝,而并发型服务器则不会有该问题
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)