Java中的网络编程

1 - 概述

1.1 - 网络编程的三要素

1、IP地址

2、端口

3、协议

1.2 - IP地址

IP地址就是设备(电脑、手机、pad、空调、冰箱...)在网络中的唯一标识(通过IP地址访问的设备)

  • IP地址两大类

    • IPV4:(主流的网络地址格式)

    • IPV6:(预计未来10年够呛能普及)

  • 常用的DOS命令

    • ipconfig -all (查看当前计算机IP信息)

    • ping xxx.xxx.xxx.xxx (测试网络是否畅通,会给对方地址发送一个网络数据测试包,查看是否有响应)

    • netstat -ano (查看本机端口占用情况)

  • 特殊IP地址

    • 127.0.0.1 - 本地回环地址

 1.3 - InetAddress

import java.net.InetAddress;
import java.net.UnknownHostException;

public class Demo03 {
    public static void main(String[] args) throws UnknownHostException {
        //主机名
        InetAddress address = InetAddress.getByName("DELL-3020");
        System.out.println(address);
        //外网地址
        InetAddress address1 = InetAddress.getByName("10.50.20.230");
        System.out.println(address1);
        //本地回环地址
        InetAddress address2 = InetAddress.getByName("127.0.0.1");
        System.out.println(address2);
    }
}

1.4 - 端口

端口是应用程序(进程/线程)在设备中的唯一标识

        端口号:用两个字节的整数来表示(unsigned short:0 ~ 65535),其中0~1024之间的端口尽量不要使用,因为很多知名网商都会在这个去区间来选择端口号(被占用的几率大),建议在10000以上来选择使用。如果被占用,再换一个。

1.5 - 协议

计算机网络中,连接和通信的规则。

  • UDP

    • User Datagram Protocol 用户数据包协议

    • UDP协议是一种无连接的通信协议,即在数据的发送的时候,数的发送端和接收端不会建立所谓的逻辑连接,简单来说就当一台计算机向另一台计算机发送数据时,发送端不会确认接收端是否存在,同样接收端也不会向发送端反馈数据是否接收到。

  • UDP协议消耗的资源相对小,通信效率高,但是不安全。一般情况下使用在在线音频、视频的用处较多。

  • TCP

    • Transmission Control Protocol 传输控制协议

    • TCP协议是一种 面向连接/安全的数据通信协议,在数据传输之前,会在发送端和接收端各自创建一个逻辑连接,然后再进行数据的传输。

    • 在数据传输之前会做“三次握手”

      • 第一次:客户端会向服务器端发送连接请求,等待服务器确认

      • 第二次:服务器会向客户端发送一个响应,通知客户端收到了请求并同意连接

      • 第三次:客户端会再次向服务器端发送确认信息,确认最终的连接。

    • TCP释放资源之前会做“四次挥手”

      • 第一次:客户端数据发送完成后,向服务器端发送连接释放请求

      • 第二次:服务器端收到客户端发来的释放请求后,会通知服务器端内部相关进程,客户端已经释放向服务器端的连接

      • 第三次:服务器端会向客户端发送同意连接释放执行

      • 客户端收到服务器端连接释放指令后,向服务器端发送确认指令 

2 - UDP通信

2.1 - 通信原理

UDP在通信的时候会在两端各自创建一个Socket(套接字)对象,但是这两个Socket只是用来发送和接收数据的对象,因此对于UDP通信的双方而言,没有所谓的客户端和服务器端的概念的。  

2.2 - UDP发送

  • 发送步骤

    1、创建发送端Socket对象(DatagramSocket)

    2、创建数据,并把数据打包(DatagramPacket)

    3、调用发送方法(send)

    4、关闭发送

    import java.io.IOException;
    import java.net.*;
    
    public class UDPSend {
        public static void main(String[] args) throws IOException {
            //创建发送端Socket对象
            DatagramSocket ds = new DatagramSocket();
    
            //创建数据
            byte[] bytes = "Hello UDP Send Message".getBytes();
            int length = bytes.length;
            InetAddress address = InetAddress.getByName("127.0.0.1");
            int port = 9958;
            //创建数据,并且将数据打包
            DatagramPacket dp = new DatagramPacket(bytes, length, address, port);
            //调用发送数据方法
            ds.send(dp);
            //关闭发送
            ds.close();
    
        }
    }

    2.3 - UDP接收端

        接收步骤

        1、创建接收端Socket对象(DatagramSocket)

        2、创建一个数据包,用于接收数据

        3、调用接收方法

        4、解析数据包

        5、关闭接收

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

public class UDPReceive {
    public static void main(String[] args) throws IOException {
        //1、创建发送端Socket对象(DatagramSocket)
        DatagramSocket ds = new DatagramSocket(9958);
        //创建数据包
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
        ds.receive(dp);
        //解析数据包
        //byte[] data = dp.getData();//通过数据包取出数据
        int length = dp.getLength();//实际接收到的数据的长度
        //通过数组取出数据
        System.out.println(new String(bytes, 0, length));
        //5、关闭接收
        ds.close();
    }
}

2.4 - 案例

需求:

  • UDP发送数据:数据来源于键盘输入,直接输入over的时候结束发送

  • UDP接收数据:因为不知道发送端发送多少条数据,所有采用死循环接收

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

/**
 * @Description #TODO  循环接收
 */
public class UDPReceive {
    public static void main(String[] args) throws IOException {
        //创建接收端对象
        DatagramSocket ds = new DatagramSocket(1314);
        while (true){
            byte[] bytes = new byte[1024];
            //创建接收数据的数据包
            DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
            ds.receive(dp);//调用接收方法接收数据
            //解析数据并打印
            System.out.println("接收到 -" + new String(bytes,0,dp.getLength()));
        }
    }
}
-------------------------------------------
package demo05;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

/**
 * @Description #TODO  发送键盘输入内容
 */
public class UDPSend {
    public static void main(String[] args) throws IOException {
        DatagramSocket ds = new DatagramSocket();//创建发送端对象

        String line;//来存储键盘输入的内容
        while (true){
            System.out.println("请输入要发送的数据:");
           line = new Scanner(System.in).nextLine();//获取键盘输入
            //判断是否是出口指令
            if ("over".equals(line)){
                break;//跳出循环
            }
            //如果不是over,则将数据发送出去
            byte[] bytes = line.getBytes();//将发送的数据转换成byte数组
            DatagramPacket dp = new DatagramPacket(bytes, bytes.length,
                    InetAddress.getByName("127.0.0.1"), 1314);
            ds.send(dp);//发送数据包
        }
        //关闭发送对象
        ds.close();
    }
}

3 - TCP通信

3.1 - TCP通信原理

TCP通信是一种可靠的网络编程协议,它会在通信的两端各自创建一个Socket对象,从而在通信的两端形成虚拟的网络连接,一旦建立了这种网络连接,两端的程序就可以通过虚拟的网络的IO流来进行数据的通信。

3.2 - TCP的发送数据

  • 发送步骤

    1、创建客户端的Socket对象

    2、获取输出流

    3、写数据

    4、释放资源

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/**
 * @Description #TODO
 */
public class TCPSend {
    public static void main(String[] args) throws IOException {
//        1、创建客户端的Socket对象
        Socket socket = new Socket("127.0.0.1", 9527);
//        2、获取输出流
        OutputStream os = socket.getOutputStream();
//        3、写数据
        os.write("Hello TCP Send Messages!~".getBytes());
//        4、释放资源
        os.close();
        socket.close();
    }
}

3.3 - 服务器接收数据

  • 接收数据步骤

    1、创建服务器端的Socket对象(ServerSocket)

    2、获取通信使用的Socket对象

    3、获取输入流

    4、读数据

    5、处理数据

    6、释放资源

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Description #TODO  服务器接收数据
 */
public class TCPReceive {
    public static void main(String[] args) throws IOException {
//        1、创建服务器端的Socket对象(ServerSocket)
        ServerSocket serverSocket = new ServerSocket(9527);
//        2、获取通信使用的Socket对象
        Socket socket = serverSocket.accept();
//        3、获取输入流
        InputStream is = socket.getInputStream();
//        4、读数据
        byte[] bytes = new byte[1024];//用来存储读取的数据的容器
        int length = is.read(bytes);//将数据读取到数组中,并返回数组中读取的实际数据的长度
//        5、处理数据
        String data = new String(bytes, 0, length);//将byte数组转换成字符串
        System.out.println("接收到客户端发来的消息:" + data);
//        6、释放资源
        serverSocket.close();
        socket.close();
        is.close();
    }
}

3.4 - 案例

  • 客户端发送数据,并接收服务器的反馈

  • 服务器端接收数据,并给出反馈

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * @Description #TODO  客户端发送数据,并接收服务器端的反馈信息
 */
public class TCPSend {
    public static void main(String[] args) throws IOException {
        //先发送数据到服务器端
        //创建socket对象
        Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9527);
        //向服务器端发送数据,通过io流
        OutputStream os = socket.getOutputStream();
        os.write("今天是周日,是第二阶段最后一天可!~".getBytes());


        //接收服务器端发送过来的反馈消息
        InputStream is = socket.getInputStream();//获取输入流
        byte[] bytes = new byte[1024];//创建容器,用来存储数据
        int length = is.read(bytes);//通过输入流读取数据,并返回读取的数据的实际长度
        String data = new String(bytes, 0, length);//将数据转换成字符串
        System.out.println("接收到服务器端发来的反馈----" + data);

        //释放资源
        socket.close();
        os.close();
        is.close();
    }
}
----------------------------------------------------------
package demo01;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Description #TODO  服务器接收数据,并给出反馈
 */
public class TCPReceive {
    public static void main(String[] args) throws IOException {
        //创建socket对象
        ServerSocket serverSocket = new ServerSocket(9527);
        Socket socket = serverSocket.accept();

        //接收客户端发来的消息
        InputStream is = socket.getInputStream();//获取输入流
        byte[] bytes = new byte[1024];//创建容器,用来存储数据
        int length = is.read(bytes);//将数据存到数组中,并返回数组中存储实际数据的长度
        System.out.println("接收到客户端发来的信息:" + new String(bytes,0,length));


        //给客户点发送反馈信息
        OutputStream os = socket.getOutputStream();//获取输出流
        os.write("服务器已经接收到数据".getBytes());

        //释放资源
        serverSocket.close();
        socket.close();
        is.close();
        os.close();
    }
}
  • 通过TCP发送键盘内容,直到over结束

    import java.io.*;
    import java.net.Socket;
    
    /**
     * @Description #TODO  客户端发送键盘输入的内容
     */
    public class ClientSendInput {
        public static void main(String[] args) throws IOException {
            Socket socket = new Socket("127.0.0.1", 9527);//创建通信的socket对象
    
            //自己封装一个标准键盘输入
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            //自己封装一个缓冲区字符输出流对象
    //        OutputStream os = socket.getOutputStream();//字节流
    //        OutputStreamWriter osw = new OutputStreamWriter(os);//字符流
    //        BufferedWriter bw = new BufferedWriter(osw);//缓冲区字符输出流
            //当前效果,与上面三行同等
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    
    
            //获取用户的键盘输入
            String line;
            while ((line = br.readLine()) != null){
                //判断一个出口
                if("over".equals(line)){
                    break;
                }
                //将数据写到输出流中
                bw.write(line);
                bw.newLine();
                bw.flush();
            }
    
            //释放资源
            socket.close();
            br.close();
            bw.close();
        }
    }
    ---------------------------------------------
    package demo02;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /**
     * @Description #TODO  接收客户端的键盘输入的内容
     */
    public class ServerReceiveInput {
        public static void main(String[] args) throws IOException {
            ServerSocket serverSocket = new ServerSocket(9527);
            Socket socket = serverSocket.accept();
    
            //自己封装一个缓冲区字符输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
            String line;
            while ((line = br.readLine()) != null){
                System.out.println("接收到客户端发来的:-" + line);
            }
    
            //释放资源
            serverSocket.close();
            socket.close();
        }
    }
  • 服务器端接收客户端发来的消息,并保存到文件中


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

/**
 * @Description #TODO   客户端发送键盘输入内容给服务器端
 */
public class ClientSend {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("127.0.0.1", 9527);//创建socket对象

        //封装键盘输入
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //自己封装输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        //发送数据
        String line;
        String[] arr = {"张三","李四"};
        int i = 1;
        while ((line = br.readLine()) != null){
            if("over".equals(line))//判断出口
                break;

            //如果不是over则把数据发送到服务器端
            bw.write(arr[i % 2] + "说:" + line);
            bw.newLine();
            bw.flush();
            i++;
        }
        //资源释放
        br.close();
        bw.close();
        socket.close();
    }
}
----------------------------------------------
package demo03;

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

/**
 * @Description #TODO  服务器端接收并保存数据到文件中
 */
public class ServerReceive {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9527);
        Socket socket = serverSocket.accept();

        //字节封装输入流(获取客户端数据)和输出流(将数据写入到文件中)
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream("./myTest.txt")));

        //获取客户端发来的消息并将消息写入到文件中
        String line;
        while ((line = br.readLine()) != null){
            bw.write(line);//写一行
            bw.newLine();//换一行
            bw.flush();//刷新一行
        }

        serverSocket.close();
        socket.close();
        br.close();
        bw.close();
    }
}

```
  • 模拟聊天室

import java.util.HashMap;
import java.util.Map;

/**
 * 初始化用户类
 */
public class Global {
    public static Map<Integer, String> portMap = new HashMap<>();

    static {
        portMap.put(9991, "佐伊");
        portMap.put(9992, "狐狸");
        portMap.put(9993, "拉克丝");
        portMap.put(9994, "蛮族之王");
        portMap.put(9995, "盲僧");
    }

}
-------------------------------------------------------------------->
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Map;

public class Service {

    /**
     * 服务端,负责接收客户端的数据,再转发客户端发来的数据给各个客户端
     * @param args
     */
    public static void main(String[] args) {
        try {
            DatagramSocket dsSend = new DatagramSocket();
            DatagramSocket dsReceive = new DatagramSocket(9999);
            InetAddress address = InetAddress.getByName("127.0.0.1");
            repeatData(dsSend, dsReceive, address,Global.portMap );

        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 转发数据
     * @param dsSend 发送端Socket
     * @param dsReceive 接收端Socket
     * @param address 接收端host
     * @param portMap 需要发送的端口号
     */
    private static void repeatData(DatagramSocket dsSend, DatagramSocket dsReceive,InetAddress address,Map<Integer, String> portMap) {
        try {
            byte[] bytes = new byte[1024];
            int i = 1;
            while (true) {

                DatagramPacket dpReceive = new DatagramPacket(bytes, bytes.length);
                dsReceive.receive(dpReceive);

                for (Integer port : portMap.keySet()) {
                    DatagramPacket dpSend = new DatagramPacket(bytes,  dpReceive.getLength(), address, port);
                    dsSend.send(dpSend);
                }
                System.out.println("服务端转发了" + i++ + "数据");
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
--------------------------------------------------------------------->
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

public class Client {
    /**
     * 客户端类
     *  启动多个客户端,互相发送消息
     * @param args
     */

    public static void main(String[] args) {
        int userPort = getUser() + 9990;
        //发送线程
        Thread sendThread = new Thread(Client::send, Global.portMap.get(userPort));
        sendThread.start();

        //接受线程
        Thread receiveThread = new Thread(() -> receive(userPort), Global.portMap.get(userPort));
        receiveThread.start();

    }

    /**
     * 发送端数据
     */
    public static  void send(){
        try {
            //创建输入流
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            //创建发送socket
            DatagramSocket ds = new DatagramSocket();

            //获取控制台数据,发送到service
            String line;
            while ((line = br.readLine()) != null){
                if(line.equals("exit")){
                    System.exit(0);
                }
                byte[] bytes = (Thread.currentThread().getName() + "&&" + line).getBytes();
                DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("127.0.0.1"), 9999);
                ds.send(dp);
            }

        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 接收数据
     * @param port
     */
    public synchronized static void receive(int port){
        try {
            DatagramSocket ds = new DatagramSocket(port);
            System.out.println("用户登录成功!~");

            while (true){
                byte[] bytes = new byte[1024];
                DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length);
                ds.receive(dp);
                String[] mesArr = new String(bytes, 0, dp.getLength()).split("&&");
                String message = mesArr.length > 1 ? mesArr[1] : "";
                if(!Thread.currentThread().getName().equals(mesArr[0]) && !message.equals("")){
                    System.out.println(mesArr[0] + "说----------->" + message);
                }
            }
        }catch (IOException e){
            e.printStackTrace();
        }

    }

    /**
     * 获取选择客户序号
     * @return
     */

    public synchronized static int getUser(){

        System.out.println("下面是客户列表");
        int index = 1;
        for (Integer port :Global.portMap.keySet()) {
            System.out.println(index++ + " - " + Global.portMap.get(port));
        }

        do{
            System.out.println("请选择几号客户端");
            index = new Scanner(System.in).nextInt();
        }while (index >5 || index<1 );

        return index;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值