首页 > 编程语言 >Java - 网络编程 ,网络通信三要素,TCP通信,实现 TCP 网络通信程序,使用 TCP 网络通信实现文件上传

Java - 网络编程 ,网络通信三要素,TCP通信,实现 TCP 网络通信程序,使用 TCP 网络通信实现文件上传

时间:2022-08-16 14:12:01浏览次数:72  
标签:网络通信 Java 服务器端 对象 TCP java 字节 客户端 Socket

第十三章、网络编程

13.1、软件架构 CS/BS

C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。

image

B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。

image

两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。

13.2、网络通信协议

网络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信。这就好比在道路中行驶的汽车一定要遵守交通规则一样,协议中对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守,最终完成数据交换。

TCP/IP:传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。

image

image

上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能,接下来针对这四层进行详细地讲解。

链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。

网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。

传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。

应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。

13.3、协议分类

通信的协议还是比较复杂的, java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。

java.net 包中提供了两种常见的网络协议的支持:

TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。

三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

第一次握手,客户端向服务器端发出连接请求,等待服务器确认。

第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。

第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。

image

完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的全,所以应用十分广泛,例如下载文件、浏览网页(http)等。

图示:

image

UDP:用户数据报协议(User Datagram Protocol)。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方端服务是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。每个数据包的大小限制在64k以内。它是不可靠协议,因为无连接,所以传输速度快,但是容易丢失数据。日常应用中,例如视频会议、QQ聊天等。

每次发送的数据最大为64kb

总结:

TCP协议: 面向连接的,需要3次握手,消耗系统资源较大,传输速度相对较慢,没有数据大小的限制

面向连接的,保证数据的安全,不会丢失数据

TCP分为客户端和服务器端

UDP协议: 面向无连接的,不需要3次握手,消耗系统资源较小,传输速度相对较快,每个数据包的大小限制在64k以内

面向无连接的,不证数据的安全,可能会丢失数据

UDP不分客户端和服务器端

13.4、网络通信三要素

1)协议

协议:计算机网络通信必须遵守的规则,已经介绍过了,不再赘述。

2)IP 地址

IP地址:指互联网协议地址(Internet ProtocolAddress),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。

IP地址分类:

IPv4:是一个32位的二进制数,通常被分为4个字节,表示成a.b.c.d 的形式,例如 192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。

IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。

有资料显示,全球IPv4地址在2011年2月分配完毕。

为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789 ,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。

3)常用命令:

查看本机 IP 地址,在控制台输入:

ipconfig

检查网络是否连通,在控制台输入:

ping 空格 IP地址 -all(可选)

特殊的 IP 地址:

本机 IP 地址:
127.0.0.1
localhost

4)端口号:

网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分这些进程呢?

如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一

标识设备中的进程(应用程序)了。

端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。

利用 协议+ IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。

比如:http://192.168.0.105:3333
语法:协议://IP地址:端口号/xxx/xxx

图示:

image

5)总结:

IP地址:用来唯一标识网络中的一台计算机合法身份的数字编号

IPV4: 32位2进制数字,划分成4个字节,每个字节范围0-255 共可表示42亿个

比如:192.168.0.105

IPV6: 128位2进制数字,16位1组,分成8组,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题

比如:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789

查看本机IP地址:

检查网络是否连通:

本机IP地址:

端口号: 给计算机上的每个程序进行一个编号,就是一个int数字

取值范围: 0-65535

0-1023: 系统保留使用

我们自己用1024及以上的端口号

13.5、TCP通信

1)概述

TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。

两端通信时步骤:

  1. 服务端程序,需要事先启动,等待客户端的连接。

  2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。

在Java中,提供了两个类用于实现TCP通信程序:

  1. 客户端: java.net.Socket 类表示。创建 Socket 对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
  2. 服务端: java.net.ServerSocket 类表示。创建 ServerSocket 对象,相当于开启一个服务,并等待客户端的连接。

2)什么是套接字?

套接字(socket) :可以使用 IO 流的操作来实现客户端和服务端之间传输数据。

3)Socket 类

该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。

构造方法:

public Socket(String host, int port)
    	只要使用 new 创建 Socket 对象,就会进行3次握手,建立到服务器的连接
    参数:String host:要连接的服务器的 ip 地址
    	 int port:要连接的服务器的端口号

常用 API :

public OutputStream			getOutputStream():
							获取客户端 Socket 对象的字节输出流对象,向服务器发送请求信息的
                                
public InputStream			getInputStream():
							获取客户端 Socket 对象的字节输入流对象,读取服务器端发送回来的响应信息的

4)ServerSocket 类

这个类实现了服务器套接字,该对象等待通过网络的请求。

服务器程序工作在服务器的某个端口上,一旦启动服务器,它将在这个端口上监听,等待客户端发送来的请求.

如果客户端一直不连接服务器,那么服务器就一直处于阻塞状态,直到客户端连接服务器为止

构造方法:

ServerSocket(int port)
    		参数: int port :代表端口号,要和客户端请求的端口号保持一致
    		注意:服务器端不需要指定 ip 地址,使用本机 ip

常用 API :

public Socket				accept() :服务器端获取到连接服务器的客户端 Socket对象
    				返回值类型:java.new.Socket类 代表客户端
    
    //注意:这2个方法是 Socket 类的方法,不是ServerSocket 类的方法。
public OutputStream			getOutputStream() :
							获取服务器端的字节输出流对象,给客户端发送响应信息的
public InputStream			getInputStream() :
							获取服务器端的字节输出流对象,读取客户端发送过来的请求数据的

注意:套接字跟流一样,用完之后,必须要关闭。

13.6、实现 TCP 网络通信程序

通信图解:

image

1)客户端向服务端发送数据实现步骤:

  1. 创建客户端 Socket 对象,指定要连接的服务器的 ip 地址和端口号
  2. 客户端 Socket 对象调用 getOutputStream 方法,获取客户端的字节输出流对象
  3. 客户端的字节输出流对象调用 write 方法,给服务器发送请求信息
  4. 客户端 Socket 对象调用 getInputStream 方法,获取客户端的字节输入流对象
  5. 客户端的字节输入流对象调用 read 方法,读取服务器端发送回来的相应信息
  6. 关闭流,释放资源

2)服务端向客户端发送数据实现步骤:

  1. 创建服务器端的 ServerSocket 对象,指定端口号
  2. 服务器端的 ServerSocket 对象调用 accept 方法,获取连接到服务器的客户端 Socket 对象
  3. 服务器的客户端 Socket 对象调用 getInputStream 方法,获取服务器端的字节输入流对象
  4. 服务器端的字节输入流对象调用 read 方法,读取客户端发送的请求信息
  5. 服务器的客户端 Socket 对象调用 getoutputStream 方法,获取服务器端的字节输出流对象
  6. 服务器端的字节输出流对象调用 write 方法,给客户端发送响应信息
  7. 关闭流,释放资源

3)练习

案例:实现客户端和服务端互相发送一次消息

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

public class TcpServer {
    public static void main(String[] args) throws IOException {
        //创建服务服务套接字对象
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器启动,等待客户端的连接");
        //监听客户端的连接,返回套接字对象
        Socket accept = serverSocket.accept();
        System.out.println("客户端连接服务器成功");
        //服务器端的字节输入流对象调用read方法,读取客户端发送的请求信息
        InputStream is = accept.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        len = is.read(bytes);
        String str = new String(bytes, 0, len);
        System.out.println("客户端说:" + str);
        //服务器端的字节输出流对象调用write方法,给客户端发送响应信息
        OutputStream os = accept.getOutputStream();
        os.write("小黄不是帅哥".getBytes());
        is.close();
        os.close();
        accept.close();
        serverSocket.close();
    }
}


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

public class TcpClient {
    public static void main(String[] args) throws IOException {
        //创建客户端套接字对象
        Socket socket = new Socket("127.0.0.1", 9999); //IP 地址和端口号
        //客户端的字节输出流对象调用write方法,给服务器发送请求信息
        OutputStream os = socket.getOutputStream();
        os.write("小黄是帅哥".getBytes());
        //客户端的字节输入流对象调用read方法,读取服务器端发送回来的响应信息
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        len = is.read(bytes);
        String str = new String(bytes, 0, len);
        System.out.println("服务端说:" + str);
        //6.关闭流,释放资源
        is.close();
        os.close();
        socket.close();
    }
}

案例:实现客户端和服务端可以互相发送消息聊天

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpServer {

    public static void main(String[] args) throws IOException {
        Scanner input = new Scanner(System.in);
        //创建服务服务套接字对象
        //1.创建服务器端的ServerSocket对象,指定端口号
        ServerSocket ssk = new ServerSocket(9999);
        System.out.println("服务器启动,等待客户端的连接!");
        //监听客户端的连接,返回套接字对象
        //2.服务器端的ServerSocket对象调用accept方法,获取连接到服务器的客户端Socket对象
        Socket socket = ssk.accept();
        System.out.println("客户端连接服务端成功!!!");
        //3.服务器的客户端Socket对象调用getInputStream方法,获取服务器端的字节输入流对象
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        //4.服务器的客户端Socket对象调用getOutputStream方法,获取服务器端的字节输出流对象
        PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
        while (true){
            String clientStr = br.readLine();
            //判断 - 如果客户端发送来了bye,结束掉服务端
            //当客户端输入 bye 时,发送的为空,所以
            if (clientStr == null/* || clientStr.equalsIgnoreCase("bye")*/){
                System.out.println("客户端已退出!");
                break;
            }
            System.out.println(socket.getInetAddress().getHostAddress() + ">>>客户端说:" + clientStr);
            System.out.print("服务端输入:");
            String serverStr = input.nextLine();
            pw.println(serverStr);
        }
        //先关闭输入流,再关闭输出流
        br.close();
        pw.close();
        socket.close();
        ssk.close();
    }
}


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class TcpClient {

    public static void main(String[] args) throws IOException {
        Scanner input = new Scanner(System.in);
        //创建客户端套接字对象
        //1.创建客户端Socket对象,指定要连接的服务器的ip地址和端口号
        Socket socket = new Socket("127.0.0.1", 9999);
        //2.客户端Socket对象调用getOutputStream方法,获取客户端的字节输出流对象
        PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
        //3.客户端Socket对象调用getInputStream方法,获取客户端的字节输入流对象
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        while (true){
            System.out.print("客户端输入:");
            String clientStr = input.nextLine(); //控制台输入保留空格!!! nextLine()
            if (clientStr.equalsIgnoreCase("bye")){
                break;
            }
            pw.println(clientStr);
            String serverStr = br.readLine();
            System.out.println("服务端说:" + serverStr);
        }
        //6.关闭流,释放资源
        br.close();
        pw.close();
        socket.close();
    }
}

13.7、使用 TCP 网络通信实现文件上传

1)文件上传到客户端步骤:

  1. 创建客户端 Socket 对象,指定要连接的服务器的ip地址和端口号
  2. 客户端Socket对象调用getOutputStream方法,获取客户端的字节输出流对象
  3. 创建文件字节输入流FileInputStream类的对象,指定客户端本地文件
  4. 字节数组循环读(客户端本地文件)写(服务器端)
  5. 客户端Socket对象调用getInputStream方法,获取客户端的字节输入流对象
  6. 客户端的字节输入流对象调用read方法,读取服务器端发送回来的响应数据
  7. 关闭资源

2)文件上传到服务端步骤:

  1. 创建服务器端ServerSocket对象,指定端口号
  2. 服务器端ServerSocket对象调用accept方法,获取连接到服务器端的客户端Socket对象
  3. 服务器端的客户端Socket对象调用getInputStream方法,获取服务器端的字节输入流对象(读取客户端发送过来的数据)
  4. 创建文件字节输出流FileOutputStream类的对象,指定服务器端的目标文件
  5. 字节数组循环读(客户端发送过来的数据)写(服务器端的目标)文件
  6. 服务器端的客户端Socket对象调用getOutputStream方法,获取服务器端的字节输出流对象
  7. .服务器端的字节输出流对象调用write方法,给客户器端发送响应数据的
  8. 关闭资源

3)案例:

实现把本地的一个图片文件上传到服务器本地保存

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

public class UploadServer {
    public static void main(String[] args) throws IOException {
        ServerSocket ssk = new ServerSocket(9999);
        Socket socket = ssk.accept();

        FileOutputStream fos = new FileOutputStream("Image/TT.png");
        InputStream is = socket.getInputStream();
        byte[] bytes = new byte[1024*5];
        int len = 0;
        while ((len = is.read(bytes)) != -1){
            fos.write(bytes,0,len);
            fos.flush();
        }
        PrintWriter pw = new PrintWriter(socket.getOutputStream(),true);
        pw.println("文件上传成功!");
        is.close();
        pw.close();
        fos.close();
        socket.close();
        ssk.close();
    }
}


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

public class UploadClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("192.168.1.4", 9999); //192.168.3.68
        OutputStream os = socket.getOutputStream();
        FileInputStream fis = new FileInputStream("D:\\图片\\截图.png");
        byte[] bytes = new byte[1024*5];
        int len = 0;
        while ((len = fis.read(bytes)) != -1){
            os.write(bytes,0,len);
            os.flush();
        }
        //已经读完,关闭 socket 的读取
        socket.shutdownOutput();
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String result = br.readLine();
        System.out.println("服务端返回的消息:" + result);
        fis.close();
        br.close();
        os.close();
        socket.close();
    }
}

标签:网络通信,Java,服务器端,对象,TCP,java,字节,客户端,Socket
From: https://www.cnblogs.com/hwphwc/p/16591353.html

相关文章

  • 【JAVA反序列化_一】反射基础
    image-20220816132943277【JAVA反序列化_一】反射基础须知:​这个图相信不陌生,是shiro反序列化利用工具,其中这利用链说实话当我第一次见的时候满脸问号,于是就想探究......
  • Java基础知识
    Java基础知识一、注释单行注释格式://...... //HelloWorld!多行注释格式:/*......*/ /* 我是很多行 很多行 很多行 的注释 我叫多行注释 */文档注释:......
  • Java类型转换
    类型转换Java是强类型语言,所以要进行有些运算的时候的,需要用到类型转换。运算中,不同类型的数据先转化为同一类型,然后进行运算。转换从低级到高级(根据容量来看): 低---......
  • Java变量与常量、作用域
    变量与常量、作用域1、变量(variable)变量:可以变化的量!Java是一种强类型语言,每个变量都必须声明其类型。Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作......
  • Java运算符
    运算符(operator)Java支持如下运算符:算术运算符:+,-,*,/,%,++,--赋值运算符=关系运算符:>,<,>=,<=,==,!=instanceof*逻辑运算符:&&,||,!*位运算符:&,|,^,~,>>,<<,>>......
  • Java第一个程序HelloWorld
    HelloWorld建立一个后缀为.java的文件例:Hello.java编写代码 publicclassHello{ publicstaticvoidmain(String[]args){ System.out.print("......
  • Java基础预习
    一、整数拓展二进制数用0b开头十进制数(不多说)八进制数用0开头十六进制用0x开头 publicclassDemo03{   publicstaticvoidmain(String[]args){......
  • JavaScript中的Object.defineProperty方法
    首先看一下官方的定义:Object.defineProperty()方法会直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回这个对象。语法Object.defineProperty(obj,pro......
  • java基础
    1、Java的数据类型有哪些?Java的数据类型有:(1)基本数据类型:byte,short,int,long,float,double,char,boolean(2)空类型:void(3)引用数据类型:数组、类、接口、枚举、注解等2、......
  • Java创建多线程的3种方式【杭州多测师】【杭州多测师_王sir】
    /***创建线程的3种方式*1.继承Thread类*2.实现Runnable接口*3.实现Callable接口*4.一个进程可以有多个线程、一个线程对应一个进程*5.防止多线程数据共享和超......