前几天介绍了计算机网络的一些概念,并介绍了几个协议。下面就说说 Java 中的 Socket 编程,服务器和客户端是如何通信的呢?
首先要介绍一下 Socket ,我们知道在 TCP/IP 协议簇中,TCP、UDP 协议都是在传输层,应用层基于传输层进行通信。而 Socket 可以看成是对 TCP 、UDP 协议的实现。具体到编程的时候,要看业务选择是使用 TCP 还是 UDP 协议。今天主要讲的就是基于 TCP 通信的 Socket 实现。若你对 TCP 还不熟悉。可以看这篇文章。
Java 中为 TCP 协议提供了两个类:Socket 类和 ServerSocket 类。一个 Socket 实例代表了 TCP 连接的一个客户端,而一个 ServerSocket 实例代表了 TCP 连接的一个服务器端,一般在 TCP Socket 编程中,客户端有多个,而服务器端只有一个,客户端 TCP 向服务器端 TCP 发送连接请求,服务器端的 ServerSocket 实例则监听来自客户端的 TCP 连接请求,并为每个请求创建新的 Socket 实例,由于服务端在调用 accept()等待客户端的连接请求时会阻塞,直到收到客户端发送的连接请求才会继续往下执行代码,因此要为每个 Socket 连接开启一个线程(这里就是多线程的应用啊)。服务器端要同时处理 ServerSocket 实例和 Socket 实例,而客户端只需要使用 Socket 实例。
另外,每个 Socket 实例会关联一个 InputStream 和 OutputStream 对象,我们通过将字节写入 Socket 的 OutputStream 来发送数据,并通过从 InputStream 来接收数据。
好吧,上面的描述可能有点懵,下面就来看一个 demo。使用 Socket 实现一个简单的交互,在服务器端使用多线程来处理请求。
客户端实现如下:
public class Client { public static void main(String[] args) throws IOException { Socket socket = null; PrintWriter pw = null; BufferedReader br = null; try { // 创建Socket对象,指明需要连接的服务器地址和端口 socket = new Socket("localhost", 6688); // 连接建立后,通过 Socket 输出流向服务器端发送请求信息 pw = new PrintWriter(socket.getOutputStream()); pw.write("Hello , server . I'm Client !"); pw.flush(); socket.shutdownOutput(); // 通过输入流获取服务器端返回的响应信息; br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String info = null; while((info = br.readLine()) != null){ System.out.println("服务器返回信息: "+ info); } socket.shutdownInput(); ----后面的错误处理和关闭资源省略----- } }
服务器端实现如下:
public class Server { public static void main(String[] args) throws IOException { Socket socket = null; try { // 创建ServerSocket对象,绑定监听端口 ServerSocket serverSocket = new ServerSocket(6688); while(true){ // 通过accept()方法监听客户端请求 socket =serverSocket.accept(); ServerThread serverThread = new ServerThread(socket); serverThread.start(); } } }
线程具体实现如下:
public class ServerThread extends Thread { Socket socket = null; BufferedReader br = null; PrintWriter pw = null; public ServerThread(Socket socket){ this.socket = socket; } @Override public void run() { try { // 连接建立后,通过输入流读取客户端发送的请求信息 msg br = new BufferedReader(new InputStreamReader(socket.getInputStream())); StringBuffer msg = new StringBuffer(); String info = null; while((info = br.readLine()) != null){ msg.append(info); } System.out.println("服务器收到 [ "+ socket.getInetAddress()+" ] 的消息 [ " + msg+" ]"); socket.shutdownInput(); // 通过输出流向客户端发送相应信息 pw = new PrintWriter(socket.getOutputStream()); pw.write(" success !"); pw.flush(); socket.shutdownOutput(); } } }
总结一下 Socket TCP 中实战的步骤。
服务器端:
(1) 创建ServerSocket对象,绑定监听端口;
(2) 通过accept()方法监听客户端请求;
(3) 连接建立后,通过输入流读取客户端发送的请求信息;
(4) 通过输出流向客户端发送相应信息;
(5) 关闭响应资源。
客户端:
(1) 创建Socket对象,指明需要连接的服务器地址和端口;
(2) 连接建立后,通过输出流向服务器端发送请求信息;
(3) 通过输入流获取服务器端返回的响应信息;
(4) 关闭响应资源。
注意:
1 首先执行服务器端代码。
2 服务器端执行之后默认就一直在等待客户端的连接请求。
3 以上只是一个非常基础的案例,这只是 Socket 编程的冰山一角。
4 可以优化的地方还有很多,服务器端参数的优化,如,接受数据的缓冲区大小、等待客户端连接的最长时间、使用线程池处理请求等。