张小飞的Java之路——第四十七章——Socket

写在前面:

视频是什么东西,有看文档精彩吗?

视频是什么东西,有看文档速度快吗?

视频是什么东西,有看文档效率高吗?


诸小亮:讲解了一些基础知识后,我们正是学习Socket

张小飞:Sokcet 是啥?

诸小亮:Socket:Java中为网络通信提供的一种机制,专业称:套接字

两个电脑进行通信时,数据在两个Sokcet之间通过IO传输

image.png

张小飞:具体该如何使用呢?

诸小亮:Java中使用 Sokcet 有两种方式——UDP和TCP

UDP通信

诸小亮:我们先看UDP

  不需要建立连接,封装数据包,大小限制在64K以内
  
不可靠,安全性差,但速度快(就是发,对方是否接到,咱不管,比如:村里的喇叭)

张小飞:还是演示一下吧

入门

诸小亮:没问题,首先是——接收端,看下面的代码

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
* 接收端
*/
public class Receive {
    public static void main(String[] args) throws IOException {
        //1. 创建Socket,监听10000端口
        DatagramSocket socket = new DatagramSocket(10000);
        
        //2. 通过数据包接收数据
        byte[] arr = new byte[1024];

        //3. 创建 DatagramPacket 对象,用来接收数据
        DatagramPacket packet = new DatagramPacket(arr, arr.length);
        System.out.println("UDP接收端,准备接收数据。。。。。。");
        //3.1 调用receive方法,如果收不到数据,程序会阻塞(也就是程序会停在这里,直到有数据)
        socket.receive(packet);
        
        //4. 通过DatagramPacket对象,解析收到的数据
        String ip = packet.getAddress().getHostAddress();
        int port = packet.getPort();
        byte[] data = packet.getData();
        
        //5. 转换成String
        String msg = new String(data,0, packet.getLength());
        System.out.println("发送者IP:" + ip);
        System.out.println("发送者端口:" + port);
        System.out.println("消息内容:" + msg);

        //6. 关闭Socket
        socket.close();
    }
}

张小飞:有了接收端,想必还有发送端把

诸小亮:那是必须的,下面就是发送端的代码

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


public class Send {
    public static void main(String[] args) throws IOException {
        //1. 创建Socket,监听5000端口
        DatagramSocket sendSocket = new DatagramSocket(5000);
        //如果不指定端口,会随机开启一个端口进行数据发送
//        DatagramSocket sendSocket = new DatagramSocket();

        //2. 通过数据包封装数据
        String msg = "亚瑟提着大宝剑冲过来了";
        byte[] data = msg.getBytes();
        //需要ip和端口号
        DatagramPacket packet = new DatagramPacket(data, 0, data.length,
                            InetAddress.getLocalHost(),10000);

        //3. 发送数据
        System.out.println("UDP发送端,发送数据。。。。");
        sendSocket.send(packet);

        //4. 关闭Socket
        sendSocket.close();
    }

}

诸小亮:先启动接收端,再启动发送端,结果:

张小飞的Java之路——第四十七章——Socket_第1张图片

群聊

诸小亮:其实使用 UDP 可以实现群聊的功能

张小飞:哦?怎么实现?

诸小亮:UDP模式下,可以在局域网中发送广播消息,局域网中的每个电脑都能收到消息

IP地址中每个网段都有自己的广播地址,本机IP:192.168.31.173192.168.31 网段的广播地址是 192.168.31.255

张小飞:那,具体应该如何实现呢?

诸小亮:把上面的代码稍微改改就行了

接收端

因为 receive 方法会阻塞,所以可以把 socket.receive(packet); 放到 while 循环中,一直接收消息

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;


public class Receive {
    public static void main(String[] args) throws IOException {
        //1. 创建Socket,监听10000端口
        DatagramSocket socket = new DatagramSocket(10000);

        System.out.println("UDP接收端,准备接收数据。。。。。。");
        while (true){
            //2. 通过数据包接收数据
            byte[] arr = new byte[1024];
            DatagramPacket packet = new DatagramPacket(arr, arr.length);
            //receive方法,如果收不到数据会阻塞,也就是程序会停在这里直到有数据
            socket.receive(packet);
            
            //3. 通过DatagramPacket对象,解析收到的数据
            String ip = packet.getAddress().getHostAddress();
            byte[] data = packet.getData();
            //转换成String
            String msg = new String(data,0, packet.getLength());
            System.out.println(ip + ":" + msg);
        }

        //4. 一直运行接收消息,所以不再关闭Socket
//        socket.close();
    }
}

发送端

发送端,接收用户的输入,然后发送广播

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


public class Send {
    public static void main(String[] args) throws IOException {
        //1. 创建Socket,监听5000端口
        DatagramSocket sendSocket = new DatagramSocket(5000);

        //2. 接收用户输入
        System.out.println("开始接收用户输入。。。。。。");
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        String line = null;

        while((line=bufr.readLine())!=null){
            byte[] buf = line.getBytes();
            DatagramPacket dp =
                    new DatagramPacket(buf,buf.length,
                                       InetAddress.getByName("192.168.31.255"),10000);
            sendSocket.send(dp);
            
            if("886".equals(line))
                break;
        }

        //4. 关闭Socket
        sendSocket.close();
    }
}

结果

张小飞的Java之路——第四十七章——Socket_第2张图片

TCP通信

张小飞:您不是说还有 TPC 通信吗?这个怎么用?

诸小亮:TPC通信就比UDP要麻烦一些了

需要两台电脑建立连接,形成数据通道,在连接中进行大数据量传输
因为必须建立连接,所以效率低,但安全性高,比如:打电话

张小飞:这么说,可以做一对一通信?

诸小亮:是的

基本演示

诸小亮:TCP通信需要创建连接,分为客户端(Socket)、服务端(ServerSocket)

/**
* 客户端
*/
public static void main(String[] args) throws IOException {
    //1. 创建Socket对象,指定目的IP和端口,用于创建连接
    Socket socket = new Socket("127.0.0.1", 10000);

    //2. 发送数据
    System.out.println("客户端发送数据。。。。。。");
    OutputStream out = socket.getOutputStream();
    out.write("亚瑟提着大宝剑冲过来了".getBytes());

    //3. 关闭Socket
    socket.close();
}

张小飞:这也不麻烦啊,比 UDP 简单多了

诸小亮:客户端是很简单,麻烦的地方在服务端,比如:

/**
* 服务端
*/
public static void main(String[] args) throws IOException {
    //1. 创建ServerSocket,指定监听10000端口
    ServerSocket serverSocket = new ServerSocket(10000);

    System.out.println("等待客户端连接。。。。。。");
    
    //2. 获取Socket对象,如果能够获取到,说明连接成功
    //注意:accept 方法会阻塞,直到有连接过来
    Socket client = serverSocket.accept();

    String ip = client.getInetAddress().getHostAddress();
    int port = client.getPort();
    System.out.println(ip + "--" + port + ":连接成功。。。。。。");
    
    //3. 通过客户端的IO流,获取数据
    InputStream in = client.getInputStream();
    byte[] arr = new byte[1024];
    int len = in.read(arr);
    String msg = new String(arr,0,len);
    System.out.println(msg);

    //3.关闭资源
    client.close();
    serverSocket.close();
}

诸小亮:先运行服务端,再运行客户端,结果:

image.png

2. 客户端和服务端互相通信

张小飞:上面的代码服务端只能被动的接收数据啊,能不能让它们互相通信?

诸小亮:满足你

/**
* 客户端
*/
public static void main(String[] args) throws IOException {
    //1. 创建Socket对象,指定目的IP和端口
    Socket socket = new Socket("127.0.0.1", 10000);

    //2. 发送数据
    System.out.println("客户端发送数据。。。。。。");
    OutputStream out = socket.getOutputStream();
    out.write("亚瑟提着大宝剑冲过来了".getBytes());

    //3. 拿到InputStream,用于获取服务端返回的数据
    InputStream in = socket.getInputStream();
    byte[] arr = new byte[1024];
    int len = in.read(arr);
    String msg = new String(arr,0,len);
    System.out.println("服务端返回:" + msg);

    //4. 关闭Socket
    socket.close();
}
/**
* 服务端
*/
public static void main(String[] args) throws IOException {
    //1. 创建ServerSocket,指定监听10000端口
    ServerSocket serverSocket = new ServerSocket(10000);

    System.out.println("等待客户端连接。。。。。。");
    //2. 获取Socket对象,能够获取到,说明连接成功,accept会阻塞,直到新的连接过来
    Socket client = serverSocket.accept();
    String ip = client.getInetAddress().getHostAddress();
    int port = client.getPort();
    System.out.println(ip + "--" + port + ":连接成功。。。。。。");

    //通过客户端的IO流,获取数据
    InputStream in = client.getInputStream();
    byte[] arr = new byte[1024];
    int len = in.read(arr);
    String msg = new String(arr,0,len);
    System.out.println(msg);

    //3. 返回信息给客户端
    System.out.println("服务端端发送数据。。。。。。");
    OutputStream out = client.getOutputStream();
    out.write("吕布大招断路。。。。".getBytes());

    //4.关闭资源
    client.close();
    serverSocket.close();
}

先运行服务端,再运行客户端,结果:

image.png
image.png

张小飞:额。。。。,您这样确实是互相通信,不过,能不能做成QQ聊天那样子的?

诸小亮:也是可以的

练习:一对一聊天

客户端

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

public class Client {
    public static void main(String[] args) throws IOException {
        //1. 创建Socket对象,指定目的IP和端口
        Socket socket = new Socket("127.0.0.1", 20000);

        //开启一个守护线程,用于接收用户输入
        receiveUserInput(socket);

        //接收服务端发送的数据
        InputStream in = socket.getInputStream();
        while (true){
            byte[] arr = new byte[1024];
            int len = in.read(arr);
            if(len == -1){//-1:表示结束
                break;
            }
            String msg = new String(arr,0,len);
            System.out.println("server:" + msg);
            //如果Server 发送过来的是 886,跳出循环,结束通讯
            if("886".equals(msg)){
                break;
            }
        }
        //4. 关闭Socket
        socket.close();
    }

    private static void receiveUserInput(Socket socket) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    //2. 接收用户输入,发送给Server
                    System.out.println("开始接收用户输入。。。。。。");
                    BufferedReader reader = new BufferedReader(
                        new InputStreamReader(System.in));
                    OutputStream out = socket.getOutputStream();
                    String line = null;
                    while(true){
                        line = reader.readLine();
                        out.write(line.getBytes());

                        //如果输入886,跳出循环,结束通讯
                        if("886".equals(line))
                            break;
                    }
                }catch (Exception e){
                }
            }
        });
        //设置为守护线程,主线程结束后,守护线程也结束
        t.setDaemon(true);
        t.start();
    }
}

服务端


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

public class Server {
    public static void main(String[] args) throws IOException {
        //1. 创建ServerSocket,指定监听10000端口
        ServerSocket serverSocket = new ServerSocket(20000);

        //2. 获取客户端连接,能够获取到,说明连接成功,accept会阻塞,直到新的连接过来
        System.out.println("等待客户端连接。。。。。。");
        Socket client = serverSocket.accept();
        String ip = client.getInetAddress().getHostAddress();
        System.out.println(ip + " 连接成功。。。。。。");

        //开启一个守护线程,用于接收用户输入
        receiveUserInput(client);

        //通过客户端的IO流,获取数据
        InputStream in = client.getInputStream();
        while (true){
            byte[] arr = new byte[1024];
            int len = in.read(arr);
            if(len == -1){//-1表示结束
                break;
            }
            String msg = new String(arr,0,len);
            System.out.println("client:" + msg);
            //如果客户端发送的是886,跳出循环,结束通讯
            if("886".equals(msg)){
                break;
            }
        }

        //4.关闭资源
        client.close();
        serverSocket.close();
    }

    private static void receiveUserInput(Socket client) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    //2. 接收用户输入,发送给client
                    System.out.println("开始接收用户输入。。。。。。");
                    //接收用户输入
                    BufferedReader bufr = new BufferedReader(
                        new InputStreamReader(System.in));
                    String line = null;
                    //给客户端发送数据
                    OutputStream out = client.getOutputStream();
                    while(true){
                        line = bufr.readLine();
                        out.write(line.getBytes());
                        //如果给客户端发送的是886,跳出循环,结束通讯
                        if("886".equals(line))
                            break;
                    }
                }catch (Exception e){
                }
            }
        });
        //设置为守护线程,主线程结束后,守护线程也结束
        t.setDaemon(true);
        t.start();
    }
}

3. 结果

上面代码,客户端和服务端,分别在局域网中的不同机器上运行

先运行 Server,再运行 Client
张小飞的Java之路——第四十七章——Socket_第3张图片

你可能感兴趣的:(java,udp,开发语言)