- D Cookbook
- Adam D. Ruppe
- 1039字
- 2021-07-16 11:50:46
Creating a network client and server
Networking is a common requirement in modern applications. Phobos offers a module, std.socket
, which provides a foundation for networked applications. We'll write a client and server pair to demonstrate how to use it. The client will send lines from standard input to the server. The server will accept any number of connections, say hello to new clients, and then echo back whatever it received.
How to do it…
We'll be building two separate applications: the client and the server.
Let's create a client by executing the following steps:
- Create a
Socket
object. - Connect to the server.
- Declare a buffer to hold the received data. The buffer must be preallocated to the maximum amount of data you want to handle in a single call to receive the data. Here, we'll use a static array of length 1024; it will have plenty of room to hold our message.
- Receive the hello message we sent in the server.
- Then, send each line of the message to the server, wait for the response, and print it out.
The code is as follows:
void main() { import std.socket, std.stdio; auto socket = new Socket(AddressFamily.INET, SocketType.STREAM); char[1024] buffer; socket.connect(new InternetAddress("localhost", 2525)); auto received = socket.receive(buffer); // wait for the server to say hello writeln("Server said: ", buffer[0 .. received]); foreach(line; stdin.byLine) { socket.send(line); writeln("Server said: ", buffer[0 .. socket.receive(buffer)]); } }
Let's create a server by executing the following steps:
- Create a
Socket
object. - Bind it to the listening address.
- Call the
listen()
method to start listening. - Create a list of
connectedClients
objects and aSocketSet
object for multiplexing. - Enter a main loop.
- Reset the
SocketSets
object. - Add your listener and all connected clients to the
readSet
function. - Call
Socket.select()
and pass yourSocketSet
objects to it. - If any client is ready to read, receive its data with the socket’s
receive
method and reply to the client with the socket’ssend
method. - If your listener was set in the
readSet
function, callaccept
to accept a new connection and send our welcome message.
The code is as follows:
void main() { import std.socket; auto listener = new Socket(AddressFamily.INET, SocketType.STREAM); listener.bind(new InternetAddress("localhost", 2525)); listener.listen(10); auto readSet = new SocketSet(); Socket[] connectedClients; char[1024] buffer; bool isRunning = true; while(isRunning) { readSet.reset(); readSet.add(listener); foreach(client; connectedClients) readSet.add(client); if(Socket.select(readSet, null, null)) { foreach(client; connectedClients) if(readSet.isSet(client)) { // read from it and echo it back auto got = client.receive(buffer); client.send(buffer[0 .. got]); } if(readSet.isSet(listener)) { // the listener is ready to read, that means // a new client wants to connect. We accept it here. auto newSocket = listener.accept(); newSocket.send("Hello!\n"); // say hello connectedClients ~= newSocket; // add to our list } } } }
How it works…
Phobos' std.socket
module is a wrapper of the BSD socket API available on all major operating systems. The std.socket
module is a thin wrapper. If you've used sockets in other languages, you'll feel at home with Phobos.
Clients simply connect and then send and receive data. The send
and receive
functions both take a buffer as their argument instead of returning a newly allocated array. This provides maximum performance by allowing you to reuse memory. Note that they may not use the whole buffer. These methods will wait until they can do something (unless you set the blocking
property to false
), and then they will do as much as they can immediately before returning. If only one byte is available on the network connection, receive
will write that byte to the buffer and return the value; it won't wait to fill the buffer entirely. This lets you handle partial and variable size messages as soon as possible. Similarly, send
may not send your whole message at once. A robust socket code should check the return value to ensure that all the data actually made it out.
On the server side, instead of connecting, you bind the Socket
object to an address and then listen for connections. The argument to listen
is the maximum number of clients to queue. This is typically a relatively small number, for example, 10.
There are several techniques to handle multiple connections. You can use threads, processes, or various event-based checks. Each technique has pros and cons in simplicity and speed. In the example, we used std.socket
, an easy-to-use and portable function to multiplex several connections in an application. The select
function doesn't scale well to large numbers of connections because it must loop through each one to check isSet
, but for small numbers of connections, it performs adequately and is fairly simple to use.
The SocketSet
class in std.socket
is used to pass collections of sockets to select
. You can create it outside your loop and then add your connections to it. Don't forget to add the listening socket too when it is ready to read. This means that a new client is trying to connect.
When select
indicates that a socket is set in the SocketSet
object, it is ready to be used immediately. You should handle the data as quickly as possible to minimize the wait time for other clients. The server simply continues these same steps in a loop until it is terminated.
There's more…
Here, we created a socket that uses TCP, the Internet protocol upon which many common application protocols are built, including HTTP for the Web, SMTP for e-mail, and more. You can also create UDP and Unix sockets with std.socket
. To create a Unix socket (not available on Windows operating systems), pass AddressFamily.UNIX
to the Socket
constructor instead of AddressFamily.INET
. Then, when connecting or binding, use the new UnixAddress
instead of InternetAddress
. Other than that, the API is the same as the TCP socket. Unix sockets only work with clients on the same computer, so they provide more security and speed than a TCP socket. However, they are only useful for specialized purposes. To use UDP—a connectionless protocol that is faster but less reliable than TCP—you can create the socket with SocketType.DGRAM
and then use the sendTo
and receiveFrom
methods instead of send
, receive
, and connect
.
See also
- http://vibed.org/ is a server framework that is inspired by Node.js and written in D
- 自制編譯器
- Python入門很簡單
- 單片機C語言程序設計實訓100例:基于STC8051+Proteus仿真與實戰
- 從程序員到架構師:大數據量、緩存、高并發、微服務、多團隊協同等核心場景實戰
- 看透JavaScript:原理、方法與實踐
- ASP.NET 3.5程序設計與項目實踐
- 快速念咒:MySQL入門指南與進階實戰
- Oracle 18c 必須掌握的新特性:管理與實戰
- Teaching with Google Classroom
- Access 2010數據庫應用技術(第2版)
- SQL Server 入門很輕松(微課超值版)
- Learning Bootstrap 4(Second Edition)
- Arduino機器人系統設計及開發
- Java高手是怎樣煉成的:原理、方法與實踐
- 大規模語言模型開發基礎與實踐