Socket编程之TCP实现

这是我参与11月更文挑战的第15天,活动详情查看:2021最后一次更文挑战

前几天介绍了计算机网络的一些概念,并介绍了几个协议。下面就说说 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 可以优化的地方还有很多,服务器端参数的优化,如,接受数据的缓冲区大小、等待客户端连接的最长时间、使用线程池处理请求等。