网络——实现服务器

【0】README

0.1) 本文描述部分转自 core java volume 2 , 旨在理解 网络——实现服务器 的基础知识 ;
0.2) for source code, please visit https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/chapter3/EchoServer.java + https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/chapter3/ThreadedEchoServer.java

【1】实现服务器

1)看个荔枝:我们的服务器可以向client 发送信息;
2)服务器描述:

  • 2.1)一旦启动服务器程序, 它便等待某个客户端连接到他的端口;
  • 2.2)ServerSocket 类用于建立服务器套结字, 如:
ServerSocket s = new ServerSocket(8189)
  • 2.3)用于建立一个监控8189的服务器,如:
Socket incoming = s.accept();
  • 2.4)上述代码用于告诉程序不停地等待, 直到有client就连接到这个端口;
  • 2.5)如果有client 连接到 server 的 8189端口, 可以使用这个对象来得到输入流和输出流, 如:
 InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();
  • 2.6)服务器发送给服务器输出流的所有信息都会成为 client 程序的输入, 同时来自 client 程序的所有输出都会被包含在 服务器输入流中;
  • 2.7)因为本章中的程序,我们都要通过套接字来发送文本, 所以我们将流转换为扫描器和写入器:
InputStream inStream = incoming.getInputStream();
OutputStream outStream = incoming.getOutputStream();
try (Scanner in = new Scanner(inStream)) 
PrintWriter out = new PrintWriter(outStream, true /* autoFlush */);
  • 2.8)以下代码将给client 发送一条信息:
out.println("hello, ");
  • 2.9)代码最后,我们关闭了连接进来的套接字:
incoming.close();
  • Conclusion) 这就是大致情况。 每一个server 程序, 比如一个 HTTP web 服务器, 都不间断地执行这个循环:

    • C1)通过输入数据流从客户端接收一个命令;
    • C2)解码这个客户端命令;
    • C3) 收集客户端所请求的信息;
    • C4) 通过输出数据流发送消息给客户端;

网络——实现服务器_第1张图片

【2】为多个客户端服务(有多个client 同时连接到我们的server上)

1)假设我们希望有多个客户端同时连接到我们的server上, 每当程序建立一个新的套接字连接, 即当调用accept 时, 将启动一个新线程来处理服务器和该客户端间的连接, 而主程序将立即返回并等待下一个连接;
2)为了实现这个机制, 服务器应该具有以下代码的循环操作:

 while (true)
         {  
            Socket incoming = s.accept();
            System.out.println("Spawning " + i);
            Runnable r = new ThreadedEchoHandler(incoming);
            Thread t = new Thread(r);
            t.start();
            i++;
         }
  • 2.1)ThreadEchoHandler 类实现了 Runnable接口,而在它的run方法中包含了与client 循环通信的代码:
public void run()
   {  
      try
      {  
         try
         {
            InputStream inStream = incoming.getInputStream();
            OutputStream outStream = incoming.getOutputStream();              
            Scanner in = new Scanner(inStream);        
            PrintWriter out = new PrintWriter(outStream, true /* autoFlush */);              
            out.println( "Hello! Enter BYE to exit." );              
            // echo client input
            boolean done = false;
            while (!done && in.hasNextLine())
            {  
               String line = in.nextLine();            
               out.println("Echo: " + line);            
               if (line.trim().equals("BYE"))
                  done = true;
            }
         }
         finally
         {
            incoming.close();
         }
      }
      catch (IOException e)
      {  
         e.printStackTrace();
      }
   }
  • 2.2)由于每一个连接都会启动一个新的线程,因而多个client 就可以同时连接到 server了;

3)看个荔枝(做个测试):

  • step1)编译和运行服务器程序;
  • step2)打开 数个 telnet 窗口;
  • step3)切换窗口,键入命令(telnet localhost(or your visitorial host) port )。注意你可以同时通过这些窗口进行通信;
  • step4) 完成之后,切换到你启动服务器的窗口, 使用ctrl +c 强制关闭它;

网络——实现服务器_第2张图片
网络——实现服务器_第3张图片

【3】半关闭

1)引入半关闭的原因: 半关闭提供了这样一种能力: 套接字连接的一端可以终止其输出,同时仍旧可以接收来自另一端的数据;
2)problem+solution

  • 2.1)problem: 这是一种很典型的case。 例如,我们在向服务器传输数据, 但是并不知道要传输多少数据。在向文件写数据时,我们只需在数据写入后关闭文件即可。 但是,如果关闭一个套接字,那么与服务器的连接将立即断开, 因而也就无法读取服务器的响应了;
  • 2.2)solution:使用半关闭就可以解决。 可以通过关闭一个套接字的输出流来表示发送给服务器的请求数据已经结束, 但是必须保持输入流处于打开状态;

3)以下代码演示了 如何在客户端使用半关闭方法:

Socket socket = new Socket(host, port);
Scanner in = new Scanner(socket.getInputStream() );
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.print(...);
writer.flush();
socket.shutdownOutput();  // client 关闭 outputStream,但是保持inputStream 处于开启状态;
while(in.hasNextLine() != null)
{
    String line = in.nextLine();
    ......
}
socket.close();
  • 3.1)服务器端将读取输入信息,直到到达输入流的结尾, 然后它再发送响应; 然后上述代码中的while 循环读取 server 的response info;

你可能感兴趣的:(java,网络)