java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se))的总称。
本篇文章主要介绍了深入理解 Java Socket,Java 中的网络通信是通过 Socket 实现的,Socket 分为 ServerSocket 和 Socket 两大类,有兴趣的可以了解一下
简述
Java 中 Socket 分为普通 Socket 和 NioSocket 两种,这里介绍 Socket。
我们可以把 Socket 比作两个城市间的交通工具,有了它可以在两城之间来回穿梭,交通工具有很多种,每种交通工具也有相应的交通规则。Socket 也一样,也有多种。大多情况下使用的是 TCP/IP 的流套接字,它是一种稳定的通信协议。(TCP/IP 与 UDP 的对比)
Java 中的网络通信是通过 Socket 实现的,Socket 分为 ServerSocket 和 Socket 两大类,ServerSocket 用于服务端,通过 accept 方法监听请求,监听到请求后返回 Socket,Socket 用于具体完成数据传输,客户端直接使用 Socket 发起请求并传输数据。
ServerSocket 的使用可以分为三步:
1. 创建 ServerSocket。ServerSocket 的构造方法一共有 5 个,通常用的是 ServerSocket(int port),只需要端口号(port)即可。
2. 调用创建出来的 ServerSocket 的 accept 方法进行监听。accept 方法时阻塞方法,也就是说调用 accept 方法后程序会停下来等待连接请求,在接收到请求之前程序将不会往下走。当接收到请求后 accept 方法会返回一个 Socket。
3. 使用 accept 方法返回的 Socket 与客户端进行通信。
栗子
Client:
- package IO;
- import java.io. * ;
- import java.net.Socket;
- import java.util.Date;
- /**
- * Created by zhengbin06 on 2017/2/2.
- */
- public class Client {
- public static void main(String[] args) {
- String msg = "Client Data";
- try {
- Socket socket = new Socket("127.0.0.1", 9090);
- // 先写、再读
- PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
- // 发送数据
- printWriter.println(msg);
- printWriter.flush();
- // 获得服务端返回的数据
- BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
- String line = bufferedReader.readLine();
- System.out.println("received from server: " + line + "\ttime=" + new Date().getTime());
- // 关闭资源
- printWriter.close();
- bufferedReader.close();
- socket.close();
- } catch(IOException e) {
- e.printStackTrace();
- }
- }
- }
Server:
- package IO;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.Date;
- /**
- * Created by zhengbin06 on 2017/2/2.
- */
- public class Server {
- private static Socket socket = null;
- private static ServerSocket serverSocket = null;
- public static void main(String[] args) throws IOException {
- BufferedReader bufferedReader = null;
- PrintWriter printWriter = null;
- try {
- // 创建一个ServerSocket监听9090端口
- serverSocket = new ServerSocket(9090);
- while (true) {
- System.out.println("开始等待请求。。。。");
- // 等待请求
- // 监听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。
- socket = serverSocket.accept();
- System.out.println("接收到请求:" + socket.toString() + "\ttime=" + new Date().getTime());
- // 接收到请求后使用socket进行通信, 创建BufferedReader用于读取数据
- bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
- String line = bufferedReader.readLine();
- System.out.println("received from client: " + line + "\ttime=" + new Date().getTime());
- // 创建PrintWriter, 用于发送数据
- printWriter = new PrintWriter(socket.getOutputStream());
- printWriter.println("received data: " + line + "\ttime=" + new Date().getTime());
- printWriter.flush();
- }
- } finally {
- // 关闭所有资源
- bufferedReader.close();
- printWriter.close();
- socket.close();
- serverSocket.close();
- }
- }
- }
细节
监听请求:
当一个新的 Socket 请求来到时,将为这个连接创建一个新的套接字数据结构,该套接字数据的信息包含的地址和端口正式请求源地址和端口。这个新创建的数据结构将会关联到 ServerSocket 实例的一个未完成的连接数据结构列表中。注意,这时服务端的与之对应的 Socket 实例并没有完成创建,而要等到与客户端的 3 次握手完成后,这个服务端的 Socket 实例才会返回,并将这个 Socket 实例对应的数据结构从未完成列表中移动已完成列表中。
数据传输:
当连接已经建立成功时,服务端和客户端都会拥有一个 Socket 实例,每个 Socket 实例都有一个 InputStream 和 OutputStream,并通过这两个对象来交换数据。
要知道网络 I/O 都是以字节流传输的,当创建 Socket 对象时,操作系统将会为 InputStream 和 OutputStream 分别分配一定大小的缓存区,数据的写入和读取都是通过这个缓存区完成的。
写入端将数据写到 OutputStream 对应的 SendQ 队列中,当队列填满时,数据将被转移到另一端 InputStream 的 RecvQ 队列中,如果这时 RecvQ 已经满了,那么 OutputStream 的 write 方法将会阻塞,直到 RecvQ 队列有足够的空间容纳 SendQ 发送的数据。过程如下图所示:
来源: http://www.phperz.com/article/17/1218/358528.html