Java网络编程


一、TCP和UDP协议

1、TCP协议

(1)使用TCP协议前,需先建立TCP连接,形成传输数据通道

(2)传输前,采用”三次握手”方式,是可靠的

(3)TCP协议进行通信的两个应用进程:客户端、服务端

(4)在连接中可进行大数据量的传输

(5)传输完毕,需释放已建立的连接,效率低

2、UDP协议

(1)将数据、源、目的封装成数据包,不需要建立连接

(2)每个数据报的大小限制在64KB内

(3)因无需建立连接,所以不可靠

(4)发送数据结束无需释放资源(因为不是面向连接的),速度快

(5)举例:发短信

二、ip地址和主机名的获取

//1、获取本机的InetAddress对象
InetAddress localHost = InetAddress.getLocalHost();

//获取InetAddress对象的ip地址
String hostAddress = localHost.getHostAddress();

//获取netAddress对象的主机名
String hostName = localHost.getHostName();

//2、根据指定的主机名、域名获取IP地址
InetAddress ipAdress = InetAddress.getByName("rewind.show");

三、TCP连接

0、注意

(1)写入数据必须有结束标记

(2)当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端连接,不过这个端口是由TCP/IP来分配的,是随机的

1、字节流通讯

image-20210419220919713

服务端

public class MyServer {
    public static void main(String[] args) throws Exception {
        //1、建立服务器端的Socket,配置监听端口
        ServerSocket serverSocket = new ServerSocket(9000);

        //2、当没有客户端连接9000端口时,程序会 阻塞,等待连接
        //如果有客户端连接,则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        //3、通过输入流读取客户端写入的数据
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int readLen = 0;
        while((readLen = inputStream.read(buf)) != -1){
            System.out.println(new String(buf,0,readLen));
        }

        //4、向客户端写入数据
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("来自服务端信息".getBytes());
        socket.shutdownOutput();

        //5、关闭流和socket
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

客户端

public class MyClient {
    public static void main(String[] args) throws IOException {
        //1、连接指定主机端口
        //连接成功返回socket对象,如果没有该服务端则报错
        Socket socket = new Socket("127.0.0.1",9000);

        //2、通过输出流写入数据
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello world!!!".getBytes());
        socket.shutdownOutput();    //写入结束标记

        //3、通过输入流读取服务端写入的数据
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int readLen = 0;
        while((readLen = inputStream.read(buf)) != -1){
            System.out.println(new String(buf,0,readLen));
        }

        //4、关闭流
        outputStream.close();
        socket.close();
    }
}

2、字符流通讯

image-20210419221101928

服务端

public class MyServerWriter {
    public static void main(String[] args) throws Exception {
        //1、建立服务器端的Socket,配置监听端口
        ServerSocket serverSocket = new ServerSocket(9000);

        //2、当没有客户端连接9000端口时,程序会 阻塞,等待连接
        //如果有客户端连接,则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        //3、向客户端写入数据,通过字符流
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello client!!!");

        //插入一个换行符,表示写入内容结束,与socket.shutdownOutput();效果一样
        // 注意:要求对方使用readLine()
        bufferedWriter.newLine();

        //如果使用字符流需要手动刷新,否则数据不会写入
        bufferedWriter.flush();

        //5、关闭流和socket
        socket.close();
        serverSocket.close();
        bufferedWriter.close(); //关闭外层流
    }
}

客户端

public class MyClientReader {
    public static void main(String[] args) throws IOException {
        //1、连接指定主机端口
        //连接成功返回socket对象,如果没有该服务端则报错
        Socket socket = new Socket("127.0.0.1",9000);

        //2、通过字符流流读取服务端写入的数据
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //4、关闭流
        socket.close();
        bufferedReader.close(); //关闭外层流
    }
}

3、图片文件传输

image-20210419231642256

服务端

public class MyFileDownloadServer {
    public static void main(String[] args) throws Exception {
        //1、建立服务器端的Socket,配置监听端口
        ServerSocket serverSocket = new ServerSocket(9000);

        //2、当没有客户端连接9000端口时,程序会 阻塞,等待连接
        //如果有客户端连接,则会返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        //3、读取图片
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToArray(bis);

        //4、存储到本地
        String imgPath = "H:\\1.png";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(imgPath));
        bos.write(bytes);
        bos.flush();

        bis.close();
        bos.close();
        socket.close();
        serverSocket.close();
    }
}

客户端

public class MyFileUploadClient {
    public static void main(String[] args) throws Exception {
        //1、连接指定主机端口
        //连接成功返回socket对象,如果没有该服务端则报错
        Socket socket = new Socket("127.0.0.1",9000);

        //2、创建读取磁盘文件的输入流
        String imgPath = "H:\\图\\头像\\1.png";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(imgPath));

        //3、将输入流中的文件转化为字节数组
        byte[] bytes = StreamUtils.streamToArray(bis);

        //4、通过socket将文件发送给服务端
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes);
        bos.flush();

        //5、结束标记
        socket.shutdownOutput();

        bos.close();
        socket.close();
    }
}

工具类

public class StreamUtils {

    /**
     * 将输入流转化为byte[],可把文件内容读入byte[]
     */
    public static byte[] streamToArray(InputStream is) throws Exception{
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        byte[] b = new byte[1024];
        int len;
        while((len = is.read(b)) != -1){
            bos.write(b,0,len); //把读取到的数据写入输出流中
        }
        byte[] array = bos.toByteArray(); //将输出流中的数据转化成字节数组
        bos.close();
        return array;
    }
}

四、UDP

image-20210420200259495

基本流程

(1)核心2个类/对象,DatagramSocketDatagramPacket(数据包)

(2)建立发送端、接收端(无服务端和客户端的概念)

(3)发送数据前建立数据包/报 DatagramPacket对象

(4)调用DatagramSocket的发送和接收方法

(5)关闭DatagramSocket

注意

  • 系统不保证UDP数据包一定安全到达目的地,也不保证什么时候到达。
  • DatagramPacket对象封装了UDP数据包,在数据包中包含发送端和接收端的IP和端口,故不需要建立连接,最大64KB
  • 创建没有IP和端口号的DatagramPacket对象,是用来接收的UDP数据包。
  • 创建含有IP和端口号的DatagramPacket对象,用来发送。

1、接收端

public class UDPReceiverA {
    public static void main(String[] args) throws Exception {
        //1、创建一个DatagramSocket对象,在指定端口接收/发送数据
        DatagramSocket socket = new DatagramSocket(9000);

        //2、创建一个DatagramPacket对象,用于接收数据,数据包最大64KB
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        //3、调用接收方法,将通过网络传输的 DatagramPacket 对象填充到packet对象中
        //如果没有数据包发送到本机的指定端口,将会阻塞等待
        socket.receive(packet);

        //4、对packet进行拆包
        int length = packet.getLength();    //实际接收到的数据的字节长度
        byte[] data = packet.getData();     //接收到的数据
        String s = new String(data, 0, length);
        System.out.println(s);

        socket.close();
    }
}

2、发送端

public class UDPSenderB {
    public static void main(String[] args) throws Exception {
        //1、在9001端口接收/发送数据
        DatagramSocket socket = new DatagramSocket(9001);

        //2、封装发送的数据到 DatagramPacket
        byte[] data = "hello world!!!".getBytes();
        DatagramPacket packet = new DatagramPacket(data,data.length, InetAddress.getByName("127.0.0.1"),9000);

        //3、发送数据
        socket.send(packet);

        socket.close();
    }
}

五、win网络相关命令

查看当前主机网络情况,包括端口的监听情况和网络情况

netstat -an

//分页显示,空格翻页
netstat -an | more                

//显示运行的程序,需要管理员权限
netstat -anb

//列出PID
netstat -ano

//根据端口查找PID
netstat -aon|findstr 端口号

//杀死指定PID的进程
taskkill /t /f /im PID

本地地址:本机的网络端口

外部地址:连接到指定的本地地址的的其他主机的端口

状态:LISTENING监听中,ESTABLISHED已建立连接


  目录