官术网_书友最值得收藏!

  • 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.

Client

Let's create a client by executing the following steps:

  1. Create a Socket object.
  2. Connect to the server.
  3. 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.
  4. Receive the hello message we sent in the server.
  5. 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)]);
    }
}

Server

Let's create a server by executing the following steps:

  1. Create a Socket object.
  2. Bind it to the listening address.
  3. Call the listen() method to start listening.
  4. Create a list of connectedClients objects and a SocketSet object for multiplexing.
  5. Enter a main loop.
  6. Reset the SocketSets object.
  7. Add your listener and all connected clients to the readSet function.
  8. Call Socket.select() and pass your SocketSet objects to it.
  9. If any client is ready to read, receive its data with the socket’s receive method and reply to the client with the socket’s send method.
  10. If your listener was set in the readSet function, call accept 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
主站蜘蛛池模板: 临猗县| 登封市| 泌阳县| 绩溪县| 香格里拉县| 赞皇县| 泾阳县| 全椒县| 香格里拉县| 青州市| 通道| 五台县| 塔城市| 南丹县| 湟源县| 昆山市| 广昌县| 马龙县| 桃园市| 成安县| 舟山市| 格尔木市| 老河口市| 济宁市| 炉霍县| 太谷县| 正安县| 老河口市| 登封市| 马鞍山市| 洞头县| 巴东县| 易门县| 钟祥市| 兰州市| 伊宁县| 景洪市| 绥化市| 三明市| 荆州市| 通化市|