JavaEESocket简单体验, TCP和UDP

Posted 书客创作

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaEESocket简单体验, TCP和UDP相关的知识,希望对你有一定的参考价值。

没有不进步的人生,只有不进取的人!

对于程序员来说,在实际生产过程中,使用最多的莫过于Http和Socket通信两中。Socket相对于Http来说速度快,效率高,所以广受青睐。

说到Socket很让人联想到”套接字”、”长连接”、”TCP/UDP”、”通信”。没错,Socket是套接字的意思,Socket能够实现长连接,所以常常人们所使用的一些即时通信的软件大多数是通过Socket来实现。TCP和UDP是两种形式,TCP是面向连接的,较安全。UDP是无连接的,速度较快。

基本概念理解

1、InetAddress用于标识网络上硬件资源。

// 获取本机的InetAddress实例
InetAddress inetAddress = InetAddress.getLocalHost();
System.out.println("计算机名:" + inetAddress.getHostName());
System.out.println("IP地址:" + inetAddress.getHostAddress());
// 获取字节数组形式的IP地址
byte[] bytes = inetAddress.getAddress();
System.out.println("字节数组形式的IP地址:" + Arrays.toString(bytes));
// 直接输出InetAddress对象
System.out.println(inetAddress);

// 根据主机名获取InetAddress实例
// InetAddress address = InetAddress.getByName("SC-201702092057");
// 根据IP地址获取InetAddress实例
InetAddress address = InetAddress.getByName("192.168.155.1");
System.out.println("计算机名:" + address.getHostName());
System.out.println("IP地址:" + address.getHostAddress());

2、URL统一资源定位符,通过资源可以读取和写入网络上的数据。

// 创建URL实例
URL baidu = new URL("http://www.baidu.com");
// 根据已存在URL可以创建一个新的URL
URL url = new URL(baidu, "/index.html?username=tom#test");// ?后面表示参数,#后面表示锚点
System.out.println("协议:" + url.getProtocol());
System.out.println("主机:" + url.getHost());
// 如果未指定端口号,则使用默认端口号,此时url.getPort()获取的返回值为-1
System.out.println("端口:" + url.getPort());
System.out.println("文件路径:" + url.getPath());
System.out.println("文件名:" + url.getFile());
System.out.println("相对路径:" + url.getRef());
System.out.println("查询字符串:" + url.getQuery());

/**
 * 使用URL获取网络内容
 */
// 通过URL的openStream方法获取URL对象所表示的资源的字节输入流
InputStream is = baidu.openStream();
// 将字节输入流转换成字符输入流(字节流与字符流之间的桥梁)
InputStreamReader isr = new InputStreamReader(is, "utf8");
// 为字符输入流添加缓冲(从字符输入流中读取文本)
BufferedReader br = new BufferedReader(isr);
String data = null;
while ((data = br.readLine()) != null) {
    System.out.println("当前行数据:" + data);
}

Socket实现基于TCP通信

Socket通信分为服务端和客户端,在Java语言中,要实现基于TCP的通信需要通过ServerSocket(服务端)和Socket(客户端)。因为TCP在通信过程中采用字节流传输,所以数据的取出和传递都要通过字节流来实现。

1、服务端搭建

服务端的搭建基本上可以分为5步。

(1)、创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口。

ServerSocket serverSocket = new ServerSocket(2500);

对于端口号,系统默认取值范围是0~65535,一般认为0~1023为系统端口号,所以这里采用>1023的端口号。

(2)、用accept()方法开始监听,等待客户端的连接。

Socket socket = serverSocket.accept();

注意accept方法会使程序处于阻塞状态,所以一般是放在子线程中完成。而接受到的socket就是用于服务端和客户端进行通信的socket。

(3)、获取输入流,并读取客户端信息

InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);// 转换成字符输入流
BufferedReader br = new BufferedReader(isr);// 字符输入流缓冲
String info=null;
while((info=br.readLine())!=null){// 循环读取客户端的信息
    System.out.println("*******客户端信息:" + info);
}
socket.shutdownInput();// 关闭输入流

获取客户端数据需要通过获取socket的输入流,这里使用到了字节流->字符流的过程,最后打印出字符。

(4)、获取输出流,响应客户端

OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);// 将输出流包装为打印流
pw.write("我是服务端,你好");
pw.flush();// 刷新缓存,将缓存输出
socket.shutdownOutput();// 关闭输出流

响应客户端需要通过获取socket输出流,这里是字节流->打印流。当然也可以直接进行输出。

(5)、关闭资源,最后将以上资源全部关闭即可。

pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();

到这里一个简单的服务端就构建完成了,上面的过程仅仅是用来说来,使用Socket如何构建一个服务端,实际上ServerSocket在接收链接的过程需要在子线程中完成。

优化代码:

// 1、创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
// (0~65535,使用1023之后的端口,0~1023一般为系统默认端口)
ServerSocket serverSocket = new ServerSocket(2500);
Socket socket = null;
// 记录客户端的数量
int count = 0;
System.out.println("*****服务端已开启******");
// 循环监听等待客户端的连接
while (true) {
    // 调用accept()方法开始监听,等待客户端的连接
    socket = serverSocket.accept();
    // 创建一个新的线程
    TcpServerThread tcpServerThread = new TcpServerThread(socket);
    tcpServerThread.setPriority(4);// 设置线程优先级,[0,10],默认5
    // 启动线程
    tcpServerThread.start();
    count++;// 统计客户端的数量
    System.out.println("客户端的数量:" + count);
    InetAddress address = socket.getInetAddress();
    System.out.println("当前客户端的IP:" + address.getHostAddress());
}

而对于socket的处理,也要放在一个子线程中完成。

public class TcpServerThread extends Thread {
    // 和本线程相关的socket
    private Socket socket = null;

    public TcpServerThread(Socket socket) {
        this.socket = socket;
    }

    // 线程执行的操作,响应客户端的请求
    @Override
    public void run() {
        super.run();
        if (socket != null) {
            InputStream is = null;
            InputStreamReader isr = null;
            BufferedReader br = null;
            OutputStream os = null;
            PrintWriter pw = null;
            try {
                // 获取输入流,并读取客户端信息
                is = socket.getInputStream();
                isr = new InputStreamReader(is);// 转换成字符输入流
                br = new BufferedReader(isr);// 字符输入流缓冲
                String info = null;
                while ((info = br.readLine()) != null) {// 循环读取客户端的信息
                    System.out.println("*******客户端信息:" + info);
                }
                socket.shutdownInput();// 关闭输入流

                // 获取输出流,相应客户端
                os = socket.getOutputStream();
                pw = new PrintWriter(os);// 将输出流包装为打印流
                pw.write("我是服务端,你好");
                pw.flush();// 刷新缓存,将缓存输出
                socket.shutdownOutput();// 关闭输出流
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                // 关闭资源
                try {
                    if (pw != null)
                        pw.close();
                    if (os != null)
                        os.close();
                    if (br != null)
                        br.close();
                    if (isr != null)
                        isr.close();
                    if (is != null)
                        is.close();
                    if (socket != null)
                        socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2、客户端搭建

客户端的搭建只需要4步

Socket socket = new Socket("localhost", 2500);

(2)、创建输出流,向服务端发送消息

OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);// 将输出流包装为打印流
pw.write("我是客户端,你好啊");
pw.flush();// 刷新缓存
//pw.close();// 不能关闭输出流,会导致socket也关闭
socket.shutdownOutput();// 关闭输出流

注意一点,输出数据之后不要将流关闭,否则会导致socket也关闭。

(3)、获取输入流,并读取服务端信息

InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);// 转换成字符输入流
BufferedReader br = new BufferedReader(isr);// 字符输入流缓冲
String info=null;
while((info=br.readLine()) != null){// 循环读取客户端的信息
    System.out.println("*******服务端信息:" + info);
}
socket.shutdownInput();// 关闭输入流

获取服务端数据需要通过获取socket的输入流,这里使用到了字节流->字符流的过程,最后打印出字符。

(4)、关闭资源,最后将以上资源全部关闭即可。

br.close();
isr.close();
is.close();
pw.close();
os.close();
socket.close();

Socket实现基于UDP通信

UDP通信时无连接的,传输过程中是通过数据包来完成,所以要想实现UDP通信,需要使用到Java中的DatagramSocket和DatagramPacket。DatagramSocket是用来建立连接,而DatagramPacket是用来传递数据。

1、服务端搭建

服务端搭建分为接收和响应客户端两个过程。

首先实现接受客户端发送的数据。

(1)、创建服务端DatagramSocket,指定端口

DatagramSocket datagramSocket = new DatagramSocket(2501);

同样要设置端口号,端口号取>1023并且<65535的整数。

(2)、创建数据报用来接收客户端传递过来的数据

byte[] data = new byte[1024];// 创建字节数组,指定接受数据包大小
DatagramPacket datagramPacket = new DatagramPacket(data, data.length);

这里接受数据为数据报,所以要采用DatagramPacket类来实现数据的接收。

(3)、接受客户端发送的数据,receive方法用于接收链接,最后的方式也是放于子线程完成这一过程

datagramSocket.receive(datagramPacket);// 此方法在接收到数据报之前会处于阻塞

(4)、读取数据

String info = new String(data, 0, datagramPacket.getLength());
System.out.println("****客户端数据*****" + info);

其次是响应客户端

InetAddress inetAddress = datagramPacket.getAddress();
int port = datagramPacket.getPort();
byte[] data2 = "我是服务端,你好".getBytes();

这里就采用了InetAddress来获取客户端的相关信息,例如客户端ip和相关端口号。

(2)、创建数据报,包含发送信息

DatagramPacket datagramPacket2 = new DatagramPacket(data2, data2.length, inetAddress, port);

此处的DatagramPacket构造方式与上面的构造方法不同,此处还要知道客户端的ip才能准确的发送数据。

(3)、向客户端发送数据报,通过send方法发送数据。

datagramSocket.send(datagramPacket2);

最后关闭资源

datagramSocket.close();

2、客户端搭建

其实客户端的构建与服务端的构建是一个相似的过程,一个逆向的过程。

向服务端发送数据。

// 1、定义服务端地址,端口号,数据
InetAddress inetAddress = InetAddress.getByName("localhost");
int port = 2501;
byte[] data = "我是客户端,你好".getBytes();
// 2、创建数据报,包含发送信息
DatagramPacket datagramPacket = new DatagramPacket(data, data.length, inetAddress, port);
// 3、创建DataGramSocket对象
DatagramSocket datagramSocket = new DatagramSocket();
// 4、向服务端发送数据报
datagramSocket.send(datagramPacket);

接受服务端响应的数据

// 1、创建数据报用来接收服务端传递过来的数据
byte[] data2 = new byte[1024];// 创建字节数组,指定接受数据包大小
DatagramPacket datagramPacket2 = new DatagramPacket(data2, data2.length);
// 2、接受服务端发送的数据
datagramSocket.receive(datagramPacket2);// 此方法在接收到数据报之前会处于阻塞
// 2、读取数据
String info = new String(data2, 0, datagramPacket2.getLength());
System.out.println("****服务端数据*****" + info);

关闭资源

datagramSocket.close();

以上是关于JavaEESocket简单体验, TCP和UDP的主要内容,如果未能解决你的问题,请参考以下文章

NodeJS编写简单TCP/UDP端口代理转发服务

面向体验的传输

TCP和UDP的区别

TCP和UDP的区别

TCP和UDP协议的区别

82.基于tcp和udp协议的简单通信套接字编程