相关概念

网络编程:实现连接到网络的计算机之间的网络通信,比如聊天、传文件。
 

ip:唯一标识网络中的一台计算机。查看本机ip:

# windows
ipconfig

# linux
ifconfig

 
端口号:唯一标识一台机器上的某个进程。0-65535,1024以下的基本都被操作系统占用了,尽量使用1024以上的端口。

ip确定是哪台机器,port确定是这台机器上的哪个进程。
 

网络通信协议

  • UDP  无连接,不区分客户端、服务端。不可靠传输,直接发送数据,不管对方是否存在、是否已启动监听,速度比TCP快。

  • TCP  面向连接,3次握手,要区分客户端、服务端。提供可靠传输,需要对方存在、且已启动才能进行通信,因为要保证可靠性,速度比UDP慢。
     

套接字(socket):ip:port的形式,由ip、port唯一标识。通信的两端都有socket,网络通信其实是socket之间的通信,数据在2个socket之间通过IO流传输。

 

UDP

工具类


public class UDP {
    private static final int port = 9000;  //要使用的端口号

    /**
     * 发送消息
     * @Param ip 对方的ip,String
     * @Param msg 要发送的消息,String类型
     */
    public static void send(String ip,String msg) throws IOException {
        //对方的ip,不能直接用String,需要转换一下
        InetAddress ipAddr = InetAddress.getByName(ip);

        //socket,相当于码头
        DatagramSocket socket = new DatagramSocket();

        /* 
        packet,数据包,相当于集装箱
        参数:byte[]、数据长度、对方ip(不能直接写String)、要使用的端口号
        什么类型都可以传输,比如传文件,不局限于String,因为都要转换为字节数组
         */
        DatagramPacket packet = new DatagramPacket(msg.getBytes(), msg.length(),ipAddr,port);

        //通过socket发送packet
        socket.send(packet);
        System.out.println("send:"+msg);

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


    /**
     *监听端口,接收消息
     */
    public static void receive() throws IOException {
        //socket,指定要监听的端口。发送、接收使用的端口要相同
        DatagramSocket socket = new DatagramSocket(port);

        /*
        packet,用byte[]来存储数据
        第一个数值是字节数组的长度,第二个数值是要读取的字节数,把读取到的数据放到byte[]中
        读取的字节数要小于等于byte[]的长度,不然放不下
        */
        DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);

        //一直监听
        while (true){
            socket.receive(packet); //通过socket接收packet,用packet来封装接收到的数据,没接收到数据时会一直阻塞
            byte[] data = packet.getData(); //获取packet中的数据(整个byte[])
            int length = packet.getLength(); //获取数据的实际长度。packet中的byte[]不一定是装满的,需要获取实际的字节数

            String msg = new String(data, 0, length); //byte[]、offset、length
            // msg = new String(data); //其实不获取实际长度也行
            System.out.println("received:"+msg);

            // 获取ip常用的1方法
            // InetAddress localAddress = socket.getLocalAddress(); //本机
            // InetAddress address = packet.getAddress();  //发送方
            // String ip = address.getHostAddress(); //String类型的ip

			// 退出聊天时关闭socket,比如退出桌面程序或web项目点击“结束聊天”、超过2分钟未互动
			// 如果要一直监听,则不关闭socket
            // socket.close(); 
        }
    }

}

 

测试类


public class Test {
    public static void main(String[] args) throws IOException, InterruptedException {
        //开启一条线程,监听端口、接收消息
        Thread receiveThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    UDP.receive();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        receiveThread.start();

        //怕接收线程暂时没分配到时间片,在发送消息之后才执行,没能接收到掐前面的消息,可以让发送消息的线程沉睡会儿
        Thread.sleep(100);

        //也可以单独开启一条线程来发送消息
        UDP.send("127.0.0.1","hello");
    }
}

UDP是不可靠的,不管对方ip存不存在、对方有没有启动监听,直接发出去,不管对方能不能接收到。

 

TCP

工具类


public class TCP {
    private static final int port = 9000;  //要使用的端口号

    /**
     * 发送消息
     * @Param ip 对方的ip,String
     * @Param msg 要发送的消息,String类型
     */
    public static void send(String ip,String msg) throws IOException {
        //客户端的socket。直接使用String类型的对方的ip
        Socket socket = new Socket(ip, port);

        //向服务端发送数据
        OutputStream os = socket.getOutputStream(); //输出流
        os.write(msg.getBytes()); //byte[]
        System.out.println("send:"+msg);
    }


    /**
     *监听端口,接收消息
     */
    public static void receive() throws IOException {
        //服务端的socket,指定要监听的端口
        ServerSocket serverSocket = new ServerSocket(port);

        //一直监听
        while (true) {
            //接收客户端的请求,并创建一个与之对应的Socket来与该客户端通信
            Socket socket = serverSocket.accept();

            //接收客户端发送的数据
            InputStream is = socket.getInputStream();
            byte[] buff = new byte[1024];
            int length = is.read(buff); //将数据读取到byte[]中,返回读取到的字节数
            java.lang.String msg = new java.lang.String(buff, 0, length); //有很多个String类,不要导错了
            System.out.println("received:" + msg);

            // 获取ip常用的方法
            // InetAddress localAddress = socket.getLocalAddress();  //本机
            // InetAddress inetAddress = socket.getInetAddress();  //发送方
            // String ipAddress = localAddress.getHostAddress(); //获取String类型的IP

            // socket.close();    
        }
    }

}

TCP要区分客户端、服务端,客户端用Socket,服务端用ServerSocket,通过ServerSocket获取与客户端对应的socket,来与客户端通信。

客户端、服务端是相对的,发送数据的一方叫做客户端,接收消息的一方叫做服务端,通常要同时作为客户端、服务端。

接收到对方消息后,可以调用send()传入对方ip与之通信,也可以写个重载的send(),传入与客户端对应的socket。

 

测试类


public class Test {
    
    public static void main(String[] args) throws IOException, InterruptedException {
        //开启一条线程,监听端口、接收消息
        Thread receiveThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TCP.receive();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        receiveThread.start();

        //怕接收线程暂时没分配到时间片,在发送消息之后才执行,没能接收到掐前面的消息,可以让发送消息的线程沉睡会儿
        Thread.sleep(100);

        //也可以单独开启一条线程来发送消息
        TCP.send("127.0.0.1","hello");
    }
    
}

TCP提供可靠传输,发送消息之前要三次握手确定双方可以正常通信。