首页 > 技术文章 > 网络通信

herebug 2021-08-03 21:35 原文

一、网络通信

概念:

1.两台设备之间通过网络实现数据传输
2.网络通信:将数据通过网络从一台设备传输到另一台设备
3. java.net包下提供了一系列的类或接口,供程序员使用,完成网络通信

 

 ip地址:

 

 

域名和端口概念:

访问机制如图:在网络开发中,不要使用0-1024的端口

 

 

 

 

Tcp/ip:运用流程

 

 网络通信协议:

 

TCP和UDP: 

 

 二、InetAddress

相关方法:

1.获取本机InetAddress对象getLocalHost
2.根据指定主机名/域名获取ip地址对象getByName

3.获取InetAddress对象的主机名getHostName

4.获取InetAddress对象的地址 getHostAddress

流程:

 

 实际代码操作:

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

public class API_ {
    public static void main(String[] args) throws UnknownHostException {
        //获取本机主机的名和ip
        InetAddress localHost = Inet4Address.getLocalHost();//DESKTOP-RHF15I8/192.168.19.1
        System.out.println(localHost);
        //通过本机的主机名获取本地信息
        InetAddress name = Inet4Address.getByName("DESKTOP-RHF15I8");
        System.out.println(name);//DESKTOP-RHF15I8/192.168.19.1
        //通过域名地址获取站点信息
        InetAddress byName = Inet4Address.getByName("www.baidu.com");
        System.out.println(byName);//www.baidu.com/14.215.177.39
        //通过InetAddress对象获取对应的地址
        String hostAddress = byName.getHostAddress();//182.61.200.6
        System.out.println(hostAddress);
        //通过InetAddress对象获取对应的主机名(HostName:主机名,HostAddress:ip)
        String address = byName.getHostName();//www.baidu.com
        System.out.println(address);

    }
}

三、TCP网络通信编程(socket)

基本介绍:

1.套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准。

2.通信的两端都要有Socket,是两台机器间通信的端点
3.网络通信其实就是Socket间的通信。
4.Socket允许程序把网络连接当成一个流,数据在两个Socket间通过I0传输。

5.一般主动发起通信的应用程序属客户端,等待通信请求的为服务端

原理图解:(TCP编程可靠,UDP编程不可靠)

 

 1、完成一个客户端和服务器的连接通信操作:

流程如下:

 

 使用字节流主要代码

//服务器端

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

public class Server {
    public static void main(String[] args) throws IOException {
        //创建一个ServerSocket,监听本机的9999端口
        //要确定该端口没有其他的服务在监听这个端口
        //ServerSocket可以通过accept返回多个Socket,达到多用户连接效果
        ServerSocket serverSocket = new ServerSocket(9999);
        //阻塞在此处等待客户机连接,客户机连接成功后会创建一个Socket套接字
        Socket socket = serverSocket.accept();
        System.out.println("有客户端连接到服务器!");
        //创建一个输入流(输出流和输入流都可以通过Socket创建)
        InputStream inputStream = socket.getInputStream();
        OutputStream outputStream = socket.getOutputStream();
        byte[] bytes = new byte[1024];
        int  line;
        //IO读取数据操作
        while ((line = inputStream.read(bytes)) != -1) {
            System.out.println(new String(bytes,0,line));
        }
        outputStream.write("hello,client!".getBytes());
        socket.shutdownOutput();
        //关闭一下接口服务
        inputStream.close();
        outputStream.close();
        socket.close();
        serverSocket.close();
    }
}
//客户端

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

public class Client {
    public static void main(String[] args) throws IOException {
        //创建一个Socket套接字,有客户主机和端口,才可以连接到对应的服务器
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        //定义一个输出流/输入流
        OutputStream outputStream = socket.getOutputStream();
        InputStream inputStream = socket.getInputStream();
        //输出到服务器端,注意输出流为字符流
        outputStream.write("hello,server".getBytes());
        socket.shutdownOutput();
        byte[] bytes = new byte[1024];
        int line;
        while ((line = inputStream.read(bytes)) != -1) {
            System.out.println(new String(bytes,0,line));
        }
        //需要关闭相应的流和套接字
        inputStream.close();
        outputStream.close();
        socket.close();
    }
}

 使用字符流主要代码

//Server服务器端  
@Test
    public  void Server01() throws IOException {
        //创建一个ServerSocket,监听本机的9999端口
        //要确定该端口没有其他的服务在监听这个端口
        //ServerSocket可以通过accept返回多个Socket,达到多用户连接效果
        ServerSocket serverSocket = new ServerSocket(9999);
        //阻塞在此处等待客户机连接,客户机连接成功后会创建一个Socket套接字
        Socket socket = serverSocket.accept();
        System.out.println("有客户端连接到服务器!");
        //创建一个输入流(输出流和输入流都可以通过Socket创建)
        InputStream inputStream = socket.getInputStream();
        OutputStream outputStream = socket.getOutputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        //IO读取数据操作
        System.out.println(reader.readLine());
        writer.write("hello,client");
        writer.newLine();
        writer.flush();

        //关闭一下接口服务
        writer.close();
        reader.close();
        socket.close();
        serverSocket.close();
    }
//Client客户端
@Test
    public void Client02() throws IOException {
        //创建一个Socket套接字,有客户主机和端口,才可以连接到对应的服务器
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        //定义一个输出流/输入流
        OutputStream outputStream = socket.getOutputStream();
        InputStream inputStream = socket.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
        //输出到服务器端,注意输出流为字节流
        writer.write("hello Server!!");
//        writer.newLine();//注意在使用这个时要放在 writer.flush();之前,不然不会起作用
        writer.flush();
        socket.shutdownOutput();//writer.newLine();也可以表示输出结束,但是对方要使用reader.readLine();才会知道输入已经结束
        System.out.println(reader.readLine());
        //需要关闭相应的流和套接字
        writer.close();
        reader.close();
        socket.close();
    }

2、网络文件传输

 

 代码实例:

//服务器端

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

public class TcpFileUpLoadServer {
    public static void main(String[] args) throws Exception {
        //监听接口
        ServerSocket serverSocket = new ServerSocket(9999);
        //等待连接
        Socket socket = serverSocket.accept();
        //创建一个输入的管道
        BufferedInputStream inputStream = new BufferedInputStream(socket.getInputStream());
        //将得到的图片资源转换成一个byte[]数组
        byte[] bytes = StreamUtils.streamToByteArray(inputStream);

        //将文件资源保存到本地
        BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("src\\test22.jpg"));
        outputStream.write(bytes);
        outputStream.flush();
        //给客户端返回自己已经收到的信息
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        writer.write("收到图片!");
        writer.flush();

        //关闭相关的流和管道
        outputStream.close();
        socket.close();
        writer.close();
        serverSocket.close();

    }
}

//客户端

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

public class TcpFileUpLoadClient {
    public static void main(String[] args) throws Exception {
        //图片地址
        String imgPath = "C:\\Users\\wenman\\Desktop\\imageTest\\test11.jpg";
        //连接服务器
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(imgPath));
        //将图片文件转换为一个字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);
        //创建一个发送数据到服务器的管道流
        BufferedOutputStream outputStream = new BufferedOutputStream(socket.getOutputStream());
        //将图片文件发送到管道
        outputStream.write(bytes);
        outputStream.flush();
        socket.shutdownOutput();

        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        System.out.println(reader.readLine());

        //关闭相关资源
        reader.close();
        outputStream.close();
        socket.close();
    }
}

//引用的工具类
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 此类用于演示关于流的读写方法
 *
 */
public class StreamUtils {
    /**
     * 功能:将输入流转换成byte[], 即可以把文件的内容读入到byte[]
     * @param is
     * @return
     * @throws Exception
     */
    public static byte[] streamToByteArray(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);//把读取到的数据,写入bos    
        }
        byte[] array = bos.toByteArray();//然后将bos 转成字节数组
        bos.close();
        return array;
    }
    /**
     * 功能:将InputStream转换成String
     * @param is
     * @return
     * @throws Exception
     */
    
    public static String streamToString(InputStream is) throws Exception{
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder builder= new StringBuilder();
        String line;
        while((line=reader.readLine())!=null){
            builder.append(line+"\r\n");
        }
        return builder.toString();
        
    }

}

3、netstat指令:

1). netstat -an可以查看当前主机网络情况,包括端口监听情况和网络连接情况

2). netstat -an | more可以分页显示
3).要求在dos控制台下执行

说明:
(1) Listening表示某个端口在监听
(2)如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息.

注意:当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP来分配的,是不确定的,是随机的.。

四、UDP网络通信编程

基本介绍:

1.类 DatagramSocket和 DatagramPacket实现了基于UDP 协议网络程序。

2.UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP
数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
3. DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和
端口号以及接收端的IP地址和端口号。
4.UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方
的连接

 

 

UDP说明:
1.没有明确的服务端和客户端,演变成数据的发送端和接收端

2.接收数据和发送数据是通过 DatagramSocket 对象完成

3.将数据封装到DatagramPacket 对象/装包
4.当接收到DatagramPacket 对象,需要进行拆包,取出数据

5. DatagramSocket可以指定在哪个端口接收数据

UDP传输编程实现:

//服务器端

import java.io.IOException;
import java.net.*;

public class UDPReceiver {
    public static void main(String[] args) throws IOException {
        //创建 DatagramSocket对象,准备在8888接收数据
        DatagramSocket socket = new DatagramSocket(8888);
        //将需要发送的数据报封装到DatagramPacket,封装需要数据的字节数组,数组的长度,IP地址,以及对方的端口号
        byte[] bytes = "hello,明天一起去潇洒!".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.19.1"), 9999);
        //发送数据报
        socket.send(packet);
        //接收数据报
        byte[] bytes1 = new byte[1024];
        DatagramPacket packet1 = new DatagramPacket(bytes1, bytes.length);
        socket.receive(packet1);
        byte[] data = packet1.getData();
        System.out.println(new String(data,0,data.length));

        socket.close();
    }
}

//客户端


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

public class UDPSender {
    public static void main(String[] args) throws IOException {
        //1. 创建一个DatagramSocket对象,在端口9999准备接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        //2.创建一个DatagramPacket对象,用来保存接收到的数据
        byte[] bytes = new byte[1024];
        DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
        //3.调用接收方法,将通过网络传输的DatagramPacket对象填充到packet对象
        //当有数据包发送到这个端口时,就会把数据放入这个package中,如果没有发送到
        //这个窗口就会一直堵塞
        socket.receive(packet);

        //拆包packet.getData();返回一个byte[]对象  获取数据报的长度:int length = packet.getLength();
        byte[] data = packet.getData();
        System.out.println(new String(data,0, data.length));
        byte[] bytes1 = "好的,明天见".getBytes();
        DatagramPacket packet1 = new DatagramPacket(bytes1, bytes1.length, InetAddress.getByName("192.168.19.1"), 8888);
        socket.send(packet1);

        socket.close();
    }
}

 

 

一个项目的开发流程:

 

推荐阅读