Java教程

java进阶——day10-1 网络编程

本文主要是介绍java进阶——day10-1 网络编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

网络编程入门

1、软件结构

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

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

 

 

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

2、网络通信协议

  网络通信协议:通信协议是计算机必须遵守的规则,只有遵守这些规则,计算机才能进行通信。协议中对数据的传输速率、传输格式、传输步骤等做了统一的对待,通信双方必须同时遵守,最终完成协议交换。

  TCP/IP协议:传输控制协议/因特网互联协议,是因特网最基本、最广泛的协议,它定义了计算机如何连接因特网,以及数据如何在他们之间传输的标准,并采用了4层的分层模型。

3、协议分类

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

  TCP:

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

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

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

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

    第三次握手,客户端再次向服务器发送确认信息,确认连接。

 

 

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

  UDP:

  用户数据报协议。UDP协议是一个面向无连接的协议。传输数据时,不需要建立连接,不管对方服务端是否启动,直接将数据、数据源和目的地都封装在数据包中,直接发送。

  每个数据包大小限制在64kb内。他是不可靠的协议,因为无连接,传输速度快,但是容易丢包。日常常用,视频会议、QQ聊天等。

4、网络编程三要素

1、协议

  计算机网络通信必须遵守的规则

2、IP

  互联网协议地址:俗称IP。

  常用命令:ipconfig、ping

  特殊IP:127.0.01、localhost

3、端口号

  用两个字节表示的整数,取值范围(0~65535),其中0~1023之间的端口号用于一些知名的网络服务和应用,所以我们不能修改

  普通的应用程序,需要使用1024以上的端口号,如果端口被占用,会导致当前程序启动失败。

  常用的端口号:

  1.80端口  网络端口

  2.数据库: mysql 3306端口 , oracle 1521端口

  3.Tomcat服务器:8080

TCP通信程序

1、概述

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

 

 

   服务器端必须明确两件事:

  1、多个客户端和服务器进行交互,服务器必须明确是和哪个客户端进行的交互

    在服务器端,有一个方法accept,获取到请求的客户端对象

  2、多个客户端和服务器进行交互,就需要使用多个IO流对象

  服务器没有IO流。服务器可以获取请求的客户端对象Socket,使每个客户端Socket中提供的IO流和客户端进行交互

    服务器使用客户端的字节输入流读取客户端发送的数据

    服务器使用客户端的字节输出流给客户端回写数据

  简单记:服务器使用客户端的流和客户端交互

2、Socket类

  ServerSocket 类:这个类实现了服务器套接字(包含了ip地址和端口号的网络单位),该对象等待通过网络的请求。

  构造方法

  public Socket(String host,int port):创建套接字对象并将其连接到指定主机端口号,如果指定的host为null,则相当于指定地址为回送地址(127.0.0.1)

 

  Socket client = new Socket("127.0.0.1",6666);

  成员方法

  public InputStream getInputStream():返回此套接字输入流

  public OutputStram getOutputStream():返回此套接字输出流

  public void close():关闭此套接字

  public void shutdownOutput():进制此套接字的输入流

  例如:

package day13;

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 {
        //1、创建服务器ServerSocket对象和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        System.out.println("服务器已启动");
        //2、使用ServerSocket对象中的方法accpet,获取请求客户端对象Socket
        Socket socket = server.accept();

        //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象
        InputStream is = socket.getInputStream();

        //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));

        //6、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据
        OutputStream os = socket.getOutputStream();
        os.write("收到,谢谢".getBytes());

        //7、释放资源(Socket ServerSocket)
        socket.close();
        server.close();
    }
}
Socket

 

  注意:

  1、客户端和服务器进行交互,必须使用Socket中提供的网络流,不能使用自己创建的对象

  2、当我们创建客户端对象Socket的时候,就会去请求服务器并进行三次握手

  3、如果服务器没启动,那么会抛出异常ConnectException

3、ServerSocket

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

  构造方法

  public ServerSocket(int port):使用该构造方法在创建ServerScoket对象时,将其绑定到一个指定端口,参数port就是端口

  ServerScoket server = new ServerSocket(6666);

  成员方法

  public Socket accpect():侦听并接收连接,返回一个新的Socket对象,用于和客户端实现通信。该方法回一致阻塞直到建立连接。

  例如:

package day13;

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 {
        //1、创建服务器ServerSocket对象和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        System.out.println("服务器已启动");
        //2、使用ServerSocket对象中的方法accpet,获取请求客户端对象Socket
        Socket socket = server.accept();

        //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象
        InputStream is = socket.getInputStream();

        //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));

        //6、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据
        OutputStream os = socket.getOutputStream();
        os.write("收到,谢谢".getBytes());

        //7、释放资源(Socket ServerSocket)
        socket.close();
        server.close();
    }
}
ServerSocket

 

4、简单的TCP网络程序

综合案例

1、客户端向服务器发送数据

package day13;

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 {
        //1、创建服务器ServerSocket对象和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        System.out.println("服务器已启动");
        //2、使用ServerSocket对象中的方法accpet,获取请求客户端对象Socket
        Socket socket = server.accept();

        //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象
        InputStream is = socket.getInputStream();

        //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));

        //6、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据
        OutputStream os = socket.getOutputStream();
        os.write("收到,谢谢".getBytes());

        //7、释放资源(Socket ServerSocket)
        socket.close();
        server.close();
    }
}
Server

 

package day13;

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 {
        //1、创建一个客户端对象Socket,构造方法绑定服务器IP地址和端口号
        Socket Client = new Socket("127.0.0.1",8888);

        //2、使用Socket对象中的方法getOutputStream,获取网络字节输出流OutputStream对象
        OutputStream os = Client.getOutputStream();

        //3、使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据
        os.write("你好服务器".getBytes());

        //4、使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象
        InputStream is = Client.getInputStream();

        //5、使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        //6、释放资源Socket
        Client.close();
    }
}
Client

 

2、客户端向服务器上传文件

package day13;

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

public class demo01UpFileClient {
    public static void main(String[] args) throws IOException {
        //1、创建
        FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg");

        //2、创建客户端Socket对象,构造方法绑定ip和端口
       Socket socket = new Socket("127.0.0.1",8888);

       //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象
        OutputStream os = socket.getOutputStream();

        //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len=fis.read(bytes))!=-1){
            
            //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器
            os.write(bytes,0,len);
        }

        //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态
        socket.shutdownOutput();

        //6、使用Socket中的方法getInputStream获取网络字节输入流
        InputStream is = socket.getInputStream();
        while ((len=is.read(bytes))!=-1){
            //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据
            System.out.println(new String(bytes,0,len));
        }

        //8、关闭资源
        fis.close();
        socket.close();
    }
}
Client

 

package day13;

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

public class demo01UpFileServer {
    public static void main(String[] args) throws IOException {
        //1、创建服务器对象,向系统要指定端口号
        ServerSocket server = new ServerSocket(8888);

        //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象
        Socket client = server.accept();

        //3、使用Socket对象的 getInputStram方法 获取网络字节输入流
        InputStream is = client.getInputStream();
        //4、创建保存的路径
        File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\");
        //判断路径是否存在 不存在则创建指定文件
        if(!file.exists()){
            file.mkdirs();
        }
        //5、创建本地字节输出流,构造方法绑定输出的目的地
        FileOutputStream fos = new FileOutputStream(file+"\\upload.jpg");

        //6、使用网络字节输入流中的read方法,读取客户端上传的文件
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len=is.read(bytes))!=-1){
            //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址
            fos.write(bytes,0,len);
        }
        //8、使用网络输出流对象,给客户端回写“上传成功”
        client.getOutputStream().write("上传成功".getBytes());
        //9、释放资源
        fos.close();
        client.close();
        server.close();
    }
}
Server

 

  注意:上述客户端不加ShutdownOutput(),服务器和客户端会一直运行,不加会造成IS中的read()阻塞

3、文件上传优化分析

  1、文件名写死问题

  服务端保存文件时,名称写死,那么服务器始终只会保存最新存储的文件,所以建议使用系统时间优化,保证文件名的唯一性

package day13;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Calendar;
import java.util.Date;

public class demo01UpFileServer {
    public static void main(String[] args) throws IOException {
        //1、创建服务器对象,向系统要指定端口号
        ServerSocket server = new ServerSocket(8888);

        //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象
        Socket client = server.accept();

        //3、使用Socket对象的 getInputStram方法 获取网络字节输入流
        InputStream is = client.getInputStream();
        //4、创建保存的路径
        File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\");
        //判断路径是否存在 不存在则创建指定文件
        if(!file.exists()){
            file.mkdirs();
        }
        //5、创建本地字节输出流,构造方法绑定输出的目的地
        //时间优化文件唯一性
        FileOutputStream fos = new FileOutputStream(file+"\\"+System.currentTimeMillis()+"upload.jpg");

        //6、使用网络字节输入流中的read方法,读取客户端上传的文件
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len=is.read(bytes))!=-1){
            //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址
            fos.write(bytes,0,len);
        }
        //8、使用网络输出流对象,给客户端回写“上传成功”
        client.getOutputStream().write("上传成功".getBytes());
        //9、释放资源
        fos.close();
        client.close();
        server.close();
    }
}
Server(优化:文件名唯一性)
package day13;

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

public class demo01UpFileClient {
    public static void main(String[] args) throws IOException {
        //1、创建
        FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg");

        //2、创建客户端Socket对象,构造方法绑定ip和端口
       Socket socket = new Socket("127.0.0.1",8888);

       //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象
        OutputStream os = socket.getOutputStream();

        //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len=fis.read(bytes))!=-1){

            //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器
            os.write(bytes,0,len);
        }

        //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态
        socket.shutdownOutput();

        //6、使用Socket中的方法getInputStream获取网络字节输入流
        InputStream is = socket.getInputStream();
        while ((len=is.read(bytes))!=-1){
            //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据
            System.out.println(new String(bytes,0,len));
        }

        //8、关闭资源
        fis.close();
        socket.close();
    }
}
Client

  2、循环接收问题

  服务端,只保存一个文件就关闭了,之后用户无法上传数据(不符合实际),使用循环改进,可以让服务器不断接收不同用户的文件

package day13;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Calendar;
import java.util.Date;

public class demo01UpFileServer {
    public static void main(String[] args) throws IOException {
        //1、创建服务器对象,向系统要指定端口号
        ServerSocket server = new ServerSocket(8888);
        System.out.println("服务器已启动...");
        //优化服务器只能上传一次的问题
        while (true) {
            //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象
            Socket client = server.accept();

            //3、使用Socket对象的 getInputStram方法 获取网络字节输入流
            InputStream is = client.getInputStream();
            //4、创建保存的路径
            File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\");
            //判断路径是否存在 不存在则创建指定文件
            if (!file.exists()) {
                file.mkdirs();
            }
            //5、创建本地字节输出流,构造方法绑定输出的目的地
            //时间优化文件唯一性
            long time = System.currentTimeMillis();
            FileOutputStream fos = new FileOutputStream(file + "\\" + System.currentTimeMillis() + ".jpg");

            //6、使用网络字节输入流中的read方法,读取客户端上传的文件
            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = is.read(bytes)) != -1) {
                //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址
                fos.write(bytes, 0, len);
            }
            //8、使用网络输出流对象,给客户端回写“上传成功”
            client.getOutputStream().write("上传成功".getBytes());
            System.out.println(time+".jpg文件已成功保存");
            //9、释放资源

            fos.close();
            client.close();

        }

    }
}
Server(优化:服务器只能上传一次问题)

 

package day13;

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

public class demo01UpFileClient {
    public static void main(String[] args) throws IOException {
        //1、创建
        FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg");

        //2、创建客户端Socket对象,构造方法绑定ip和端口
       Socket socket = new Socket("127.0.0.1",8888);

       //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象
        OutputStream os = socket.getOutputStream();

        //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len=fis.read(bytes))!=-1){

            //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器
            os.write(bytes,0,len);
        }

        //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态
        socket.shutdownOutput();

        //6、使用Socket中的方法getInputStream获取网络字节输入流
        InputStream is = socket.getInputStream();
        while ((len=is.read(bytes))!=-1){
            //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据
            System.out.println(new String(bytes,0,len));
        }

        //8、关闭资源
        fis.close();
        socket.close();
    }
}
Client

  3、效率问题

  服务端在接收大文件时,可能耗费几秒或几分钟的时间,此时不能接收其他用户上传的文件,所以可以使用多线程优化代码

package day13;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Calendar;
import java.util.Date;

public class demo01UpFileServer {
    public static void main(String[] args) throws IOException {
        //1、创建服务器对象,向系统要指定端口号
        ServerSocket server = new ServerSocket(8888);
        System.out.println("服务器已启动...");
//优化服务器只能上传一次的问题
        while (true) {
            //2、通过ServerSocket对象中的方法 accept,获取请求的客户端Socket对象
            Socket client = server.accept();
//优化效率 多线程
            new Thread(()->{
                try {
                    //3、使用Socket对象的 getInputStram方法 获取网络字节输入流
                    InputStream is = client.getInputStream();
                    //4、创建保存的路径
                    File file = new File("C:\\Users\\YIAN\\Desktop\\File\\upload\\upload\\");
                    //判断路径是否存在 不存在则创建指定文件
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    //5、创建本地字节输出流,构造方法绑定输出的目的地
                    //时间优化文件唯一性
                    long time = System.currentTimeMillis();
                    FileOutputStream fos = new FileOutputStream(file + "\\" + System.currentTimeMillis() + ".jpg");

                    //6、使用网络字节输入流中的read方法,读取客户端上传的文件
                    int len = 0;
                    byte[] bytes = new byte[1024];
                    while ((len = is.read(bytes)) != -1) {
                        //7、使用本地字节输入流中的write方法,将读取的文件 存储到指定地址
                        fos.write(bytes, 0, len);
                    }
                    //8、使用网络输出流对象,给客户端回写“上传成功”
                    client.getOutputStream().write("上传成功".getBytes());
                    System.out.println(time+".jpg文件已成功保存");
                    fos.close();
                    client.close();
                }catch (IOException E){
                    System.out.println(E);
                }
            }).start();
        }

    }
}
Server(优化效率问题)
package day13;

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

public class demo01UpFileClient {
    public static void main(String[] args) throws IOException {
        //1、创建
        FileInputStream fis = new FileInputStream("C:\\Users\\YIAN\\Desktop\\2.jpg");

        //2、创建客户端Socket对象,构造方法绑定ip和端口
       Socket socket = new Socket("127.0.0.1",8888);

       //3、使用Socket中的方法getOutputStram,获取网络字节输出流OutputStram对象
        OutputStream os = socket.getOutputStream();

        //4、使用本地字节输入流FileInputStram对象中的read方法 读取数据
        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len=fis.read(bytes))!=-1){

            //5、使用网络字节输出流OutputStram对象中的方法,把读取的文件上传服务器
            os.write(bytes,0,len);
        }

        //重点:禁用此套接字输出流,不禁用 输入流会进入阻塞状态
        socket.shutdownOutput();

        //6、使用Socket中的方法getInputStream获取网络字节输入流
        InputStream is = socket.getInputStream();
        while ((len=is.read(bytes))!=-1){
            //7、使用网络网络字节输入流对象中的read方法读取服务器回写数据
            System.out.println(new String(bytes,0,len));
        }

        //8、关闭资源
        fis.close();
        socket.close();
    }
}
Client

 

  4、信息回写实现优化

package day13.demo;

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 server = new ServerSocket(8806);
        System.out.println("服务器 启动....");
        //循环接收
        while(true){

                //建立连接
                Socket client = server.accept();
                new Thread(()->{
                    try {
                        //使用Socket对象中的方法getInputStram(),获取网络字节输入流InputStram对象
                        InputStream is = client.getInputStream();
                        //使用网络字节输入流InputStram对象中的方法read,读取服务器回写的数据
                        int len = 0;
                        byte[] bytes = new byte[1024];
                        while ((len=is.read(bytes))!=-1){
                            System.out.println(new String (bytes,0,len));
                        }
                        //使用网络字节输出流OutputStram对象中的方法 write,给服务器发送数据
                        OutputStream os = client.getOutputStream();
                        os.write("您已上线".getBytes());
                        System.out.println("有新用户上线");
                        is.close();
                        os.close();
                    }catch (IOException e){
                        System.out.println(e);
                    }
                }).start();

        }
    }
}
TCPserver
package day13.demo;

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",8806);
        OutputStream os = socket.getOutputStream();
        int len = 0;
        byte[] bytes = new byte[1024];
        os.write("请求上线".getBytes());
        socket.shutdownOutput();
        InputStream is = socket.getInputStream();
        while ((len=is.read(bytes))!=-1){
            System.out.println(new String (bytes,0,len));
        }
        os.close();
        is.close();
        socket.close();
    }
}
Client

 

这篇关于java进阶——day10-1 网络编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!