目录

开发经验java-socket编程详解

【开发经验】java socket编程详解

前言

​ 在大多数的java项目中,使用开发者直接使用socket的场景并不多。但是目前众多框架的底层中,都会有socket的身影。此示例一下java原始的socket编程,并通过telnet进行通讯。

1、功能实现

https://i-blog.csdnimg.cn/blog_migrate/e008ed940c6b254b954f93f81e589f21.png

如上图所示,主要实现服务器开启服务,每个客户端链接时都分配一个新的线程与其通讯。

2、服务端代码:


import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BIOServer {
    public static void main(String[] args) throws Exception {
        //创建线程池
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();

        //创建ServerSocket
        ServerSocket serverSocket = new ServerSocket(6666);

        System.out.println("服务器启动了");

        while (true) {

            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
            //监听,等待客户端连接
            System.out.println("等待连接....");
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");

            //就创建一个线程,与之通讯(单独写一个方法)
            newCachedThreadPool.execute(new Runnable() {
                public void run() { //我们重写
                    //可以和客户端通讯
                    handler(socket);
                }
            });

        }
    }
    //编写一个handler方法,和客户端通讯
    public static void handler(Socket socket) {

        try {
            System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
            byte[] bytes = new byte[1024];
            //通过socket 获取输入流
            InputStream inputStream = socket.getInputStream();
            //循环的读取客户端发送的数据
            while (true) {
                System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());
                System.out.println("read....");
               int read =  inputStream.read(bytes);
               if(read != -1) {
                   System.out.println(new String(bytes, 0, read
                   )); //输出客户端发送的数据
               } else {
                   break;
               }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("关闭和client的连接");
            try {
                socket.close();
            }catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}

3、client代码


import java.io.*;
import java.net.Socket;

 
public class SocketClient
{
	public static void main(String[] args) throws InterruptedException {
		try {
			// 和服务器创建连接
			Socket socket = new Socket("localhost",6666);
			// 要发送给服务器的信息
			OutputStream os = socket.getOutputStream();
			PrintWriter pw = new PrintWriter(os);
			pw.write("我是客户端:您好server!~");
			pw.flush();
			socket.shutdownOutput();
			os.close();
			pw.close();
			socket.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

先启动server,然后再启动client。server打印信息如下。

连接到一个客户端

线程信息 id =1 名字=main

等待连接…

线程信息 id =12 名字=pool-1-thread-1

线程信息 id =12 名字=pool-1-thread-1

read…

我是客户端:您好server!~

线程信息 id =12 名字=pool-1-thread-1

read…

关闭和client的连接

4、telnet连接

服务端启动之后,可以通过telnet进行连接。

https://i-blog.csdnimg.cn/blog_migrate/29fd2e80a82d49b5e0fc82164459ff70.png

https://i-blog.csdnimg.cn/blog_migrate/233d89e90ee47e4f06175866e75cdf48.png

连接成功之后,会跳入空白页面,通过 CTRL+] 进入发送信息的页面。

https://i-blog.csdnimg.cn/blog_migrate/860ae6a1f2f231ad907892c9e5bdac46.png

通过send命令发送信息。

https://i-blog.csdnimg.cn/blog_migrate/1f68cb895b0f42001877c6786d7ab8f7.png

服务端打印信息如下

连接到一个客户端

线程信息 id =1 名字=main

等待连接…

线程信息 id =13 名字=pool-1-thread-2

线程信息 id =13 名字=pool-1-thread-2

read…

hello server

线程信息 id =13 名字=pool-1-thread-2

read…

可多启动几个命令行窗口,可发现其线程id不同。

通过socket通讯即可发现,每个连接都会占用一个线程。另外,在这种聊天的场景中,每个线程大部分时间都在等待信息的传输,都在阻塞在 inputStream.read 这段代码。因为每次发送信息之后,最后都会 read... 。如此,发现通过这种bio的方式会造成线程的浪费。