一个简单的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:顺序型服务器相对并发型服务器的区别在于:顺序型服务器忙于服务一个现存的连接时,如果又有新的客户机尝试连接服务器,那么这个连接就会在服务器排队,直到服务器完成对现有的服务为止,如果等待队列中的连接数目超过了监听套接字的能力,那么这个连接就会被拒绝,而并发型服务器则不会有该问题

posted @   Shay_黄  阅读(5331)  评论(0编辑  收藏  举报
编辑推荐:
· 基于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)
点击右上角即可分享
微信分享提示