一个简单的服务器,可以向客户端发送信息。一旦启动了服务器程序,它便等待某个客户端连接到它的端口。这里选用8189,因为所有标准服务都不使用这个端口。
(1)ServerSocket类实现服务器套接字。 例:建立一个负责监控端口8189的服务器ServerSocket serverSocket = new ServerSocket(8189);
(2)侦听并接受到此套接字的连接。
Socket incoming = serverSocket.accept();
(3)当通过网络向该端口发送了正确的连接请求,该方法就会返回一个表示连接已经建立的Socket对象。可以使用儿歌对象来得到输入流和输出流
InputStream inStream = incoming.getInputStream(); OutputStream outStream = incoming.getOutputStream();
服务器发送给服务器输出流的所有信息都会成为客户端程序的输入,同时来自客户端程序的所有输出都会被包含在服务器输入流中。
(4)最终,关闭连接进来的套接字。incoming.close();
DEMO
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;public class EchoServer { public static void main(String[] args) { try{ ServerSocket serverSocket = new ServerSocket(8189); Socket incoming = serverSocket.accept(); try{ InputStream inStream = incoming.getInputStream(); OutputStream outStream = incoming.getOutputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(inStream, "gbk")); //也可以使用Scanner// Scanner in = new Scanner(is); //使用OutputStreamWriter 虽然通过 \n 可以换行, //但是第二行字符的起始位置与第一行字符的终止位置相同 OutputStreamWriter outWrite = new OutputStreamWriter(outStream, "gbk"); //使用PrintWriter的情况下第二行自动提到最前面 PrintWriter out = new PrintWriter(outWrite, true); out.print("Hello! Enter BYE to exit."); boolean done = false; String line = null; while((line=in.readLine())!=null){ System.out.println(line); out.println("Echo: " + line); out.println("Hello! Enter BYE to exit."); if("bye".equalsIgnoreCase(line.trim())){ out.write("bye"); break; } } //Scanner用法// while(!done && in.hasNextLine()){// String line = in.nextLine();// System.out.println(line);// out.println("Echo: " + line);// out.println("Hello! Enter BYE to exit.");// if("bye".equalsIgnoreCase(line.trim())){// done = true;// }// } }finally{ incoming.close(); } System.out.println(serverSocket.isClosed()); }catch(IOException e){ e.printStackTrace(); } }}
1.为多个客户端服务
通常希望有多个客户端同时连接到服务器上,通常,服务器总是不间断地运行在服务器计算机上,来自整个因特网的用户系统同时使用服务器。拒绝多客户端连接将使得某个用户可能回因长时间地连接服务而独占服务。其实可以通过线程来处理该问题。 每当程序建立一个新的套接字连接,也就是说当成功调用accept的时候,将创建一个新的线程来处理服务器和客户端之间的连接,而主程序将立即返回并等待下一个连接。 (1)为了实现这个机制,服务器应该具有类似以下代码的循环操作:while(true){ Socket incoming = serverSocket.accept(); Runnable run = new ThreadedEchoHandler(incoming); Thread t = new Thread(r); t.start; }
(2)ThreadedEchoHandler类实现了Runnable接口,而且它的run方法中包含了与客户端循环通信的代码。
DEMO
import java.io.IOException;import java.net.ServerSocket;import java.net.Socket;public class ThreadedEchoServer { public static void main(String[] args) { try{ int i = 1; ServerSocket server = new ServerSocket(8189); while(true){ Socket incoming = server.accept(); System.out.println("Spawning : " + i); Runnable runnable = new ThreadedEchoHandler(incoming); Thread t = new Thread(runnable); t.start(); i++; } }catch(IOException e){ e.printStackTrace(); } }}
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;public class ThreadedEchoHandler implements Runnable { private Socket incoming; public ThreadedEchoHandler(Socket incoming){ this.incoming = incoming; } @Override public void run() { try{ try{ InputStream inStream = incoming.getInputStream(); OutputStream outStream = incoming.getOutputStream(); BufferedReader in = new BufferedReader(new InputStreamReader(inStream, "gbk")); PrintWriter out = new PrintWriter(new OutputStreamWriter(outStream, "gbk"), true); String line = null; while((line=in.readLine())!=null){ System.out.println("in:" + line); out.println("Echo : " + line); if(line.trim().equals("bye")){ break; } } }finally{ incoming.close(); } }catch(IOException e){ e.printStackTrace(); } }}
2.半关闭
半关闭(half-close)提供了这样一种能力:套接字连接的一段可以终止其输出,同时仍旧可以接受来自另一端的数据。 这是一种很典型的情况,例如我们在向服务器传输数据,但是并不知道要传输多少数据。在写一个文件时,我们只需要在数据写入后关闭文件即可。但是,如果关闭一个套接字,那么与服务器的连接将立刻断开,因而也就无法读取响应。 使用半关闭的方法就可以解决上述问题。可以通过关闭一个套接字的输出流来表示发送给服务器的请求数据已经结束,但是必须保持输入流处理打开状态。 例:在客户端使用半关闭Socket socket = new Socket(host, port); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(new OutputStreamWriter(outStream, "gbk"), true); out.println(...); //禁用此套接字的输出流 socket.shutdownOutput(); String line = null; while((line=in.readLine())!=null){ ... } //此套接字的输入流置于“流的末尾”。 socket.shutdownInput(); socket.close();
服务器端将读取输入信息,知道到达输入流的结尾,然后再发送响应。 该协议只适用于一站式(ont-shop)的服务,例如HTTP服务,在这种服务中,客户端连接服务器,发送一个请求,捕获相应信息,然后断开连接。