网络编程

什么是服务器?

服务器就是一台电脑,就是一台计算机,只不过配置非常高。

以前的代码:

都是在自己的电脑上运行的。一些结果也是打印在控制台或者是保存到文件中的。没有一个交互性的效果。

现在学习了网络编程,可以实现计算机跟计算机之间可以传输数据(交互)。

简单理解:你可以给你的同桌发消息了。你的同桌也可以接收到你的消息了。

什么是网络编程?

计算机跟计算机之间通过网络进行数据传输。

常见的互联网架构?

BS、CS

BS和CS的优缺点

BS:

  • 优点:不需要下载客户端,使用起来非常方便。
  • 缺点:用户体验比较差,画面不够精美。

CS:

  • 缺点:需要下载客户端,而且每一次要更新内容的时候,都要更新客户端,甚至要重新下载,非常麻烦。
  • 优点:画面非常精美,用户体验比较好。

在以后实际开发中,根据企业的项目需求来,如果是一些电商,新闻的项目对页面要求不高,可以用BS。

这也是为什么,在电脑上,京东,淘宝,新浪新闻都是BS的原因。

在以后实际开发中,如果对画面有强大的要求,一定是用CS,比如游戏。

网络编程三要素

IP、端口、协议

IP:

  • 设备在互联网中的地址,唯一的标识。

IP分类:

公网IP,局域网IP

公网IP:对外,在上外网的时候需要使用的。

局域网IP:192.168开头都是局域网地址。作用:节约IP的使用。

常用命令

ipconfig:查看本机IP地址

ping + IP:检查你的电脑跟指定IP的电脑之间网络是否畅通

####扩展:

ping + 域名 — ping www.baidu.com

####特殊IP:

127.0.0.1,也可以是localhost:是回送地址也称本地回环地址,也称本机IP,永远只会寻找当前所在本机。

####问题:(了解扩展)

假设,我的点IP是192.168.1.255,那么这个IP跟127.0.0.1有什么区别?

共同点:都是表示自己的电脑

区别:192.168.1.255是路由器给你分配的。127.0.0.1(localhost)就表示本机不是路由器分配的。

IP的两种表示形式

IPV4、IPV6

IPV4:32bit位,全球最多只能有42亿多,不够了。渐渐会被IPV6替代。

IPV6:128bit位,足够使用。

InetAddress类

常用方法:

getByName :确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址

getHostName:获取主机名

getHostAddress :获取IP

代码示例:

//1.获取一个IP地址(在网络中计算机的对象)
InetAddress address = InetAddress.getByName("192.168.89.92");

//2.获取主机名获取ip地址
//细节:如果能获取到主机名返回的就是主机名
//但是如果有的情况下,获取不到,返回的就是ip地址
String hostName = address.getHostName();
System.out.println(hostName);


//3.获取IP
String ip = address.getHostAddress();
System.out.println(ip);

端口:

一般也会成为端口号。是应用程序在设备中唯一的标识。

一个端口只能被一个应用程序绑定。

一个应用程序可以绑定多个端口。

举例:

10000端口,如果被QQ绑定了,那么10000这个端口就不能再被其他软件绑定了。

但是QQ可以绑定10000,还可以绑定其他端口。

端口号的注意点:

端口号的范围:0~65535

0~1023之间的端口号已经被系统或者一些知名的网络应用给占用了。

比如浏览器80端口 tomcat:8080,Mysql:3306

我们自己在使用的时候,需要用1024以上的端口。

扩展(了解)

我怎么知道系统当中有哪些端口被被人占用了呢?

1,我们下午会写代码去绑定端口,你可以随便绑定一个,如果该端口被占用,那么代码就会报错。

如果该端口没有被占用,那么就不会报错。

2,我们可以使用一些第三方软件进行查询。(鲁大师,360,等一下杀毒软件)

以360为例:点功能大全 —> 流量防火墙 —>网络连接可以看到

协议:

数据在网络中传输的规则。常见:UDP,TCP。

UDP:

面向无连接的协议。(发送数据的时候不保证连接已经建立)

速度快,大小限制最多64K,数据不安全,数据容易丢失。

TCP:

面向连接的协议。(发送数据的时候需要保证连接已经建立)

速度慢,没有大小限制,数据安全。

应用场景(了解,扩展)

UDP:一般来讲对速度比较有要求,但是数据丢失一点没有什么太大的关系。

看电影、视频网络会议。

TCP:一般是对数据安全有要求的情况下。

下载安装包,聊天。

UDP协议发送和接收数据

发送端:

public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建码头
        //如果没有绑定端口,那么系统会让程序绑定一个随机的可用端口
        DatagramSocket ds = new DatagramSocket();

        //2.确定要发送的数据
        String s = "吃俺老孙一棒棒";
        byte[] bytes = s.getBytes();

        //3.确定要发送的计算机
        InetAddress address = InetAddress.getByName("127.0.0.1");

        //4.确定要发送的端口
        int port = 10000;

        //5.把上面的数据,计算机,端口打包在一起
        DatagramPacket dp = new DatagramPacket
            (bytes,bytes.length,address,port);

        //6.发送数据
        ds.send(dp);

        //7.释放资源
        ds.close();
    }
}

接收端:

public class Server {
    public static void main(String[] args) throws IOException {
        //1.创建码头的对象
        //接收端,在创建对象的时候需要绑定端口
        //表示哥们要从10000端口接收数据
        DatagramSocket ds = new DatagramSocket(10000);

        //因为我们只要接收即可
        //所以,在箱子中,只要准备装数据的数组即可
        //IP和端口号,在发送数据的时候才需要。
        //2.创建一个新的包裹,用于接收数据
        DatagramPacket dp = new DatagramPacket(new byte[1024],1024);

        //3.接收数据
        ds.receive(dp);

        //4.从包裹中获取数据
        byte[] bytes = dp.getData();
        int len = dp.getLength();
        String ip = dp.getAddress().getHostAddress();

        System.out.println(ip + "发送过来了:" + new String(bytes,0,len));

        //5.释放资源
        ds.close();
    }
}

练习:UDP的聊天室

需求:

按照下面的要求实现程序

UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束

UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收

操作细节:

需要把发送端运行多次。

idea默认只能运行一次,所以需要进行配置。

需要保证第一个红色箭头是当前要运行多次的类,再点击下面的Edit Configurations,再按照下面的图解进行设置即可。

cs架构开发 springboot java开发cs架构好弄吗_网络

代码示例:

客户端(发送端)

public class Client {
    public static void main(String[] args) throws IOException {
        //UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
        //1. 创建码头对象
        DatagramSocket ds = new DatagramSocket();
        Scanner sc = new Scanner(System.in);
        while (true) {
            //2.数据是键盘录入
            System.out.println("请输出要聊天的内容");
            String line = sc.nextLine();
            if("886".equals(line)){
                break;
            }
            byte[] bytes = line.getBytes();
            //3.确定接收端
            InetAddress address = InetAddress.getByName("127.0.0.1");
            //4.确定端口
            int port = 10000;
            DatagramPacket dp = new DatagramPacket
                (bytes,bytes.length,address,port);
            //5.发送数据
            ds.send(dp);
        }
        //6.释放资源
        ds.close();
    }
}

服务端(接收端):

public class Server {//服务器

    public static void main(String[] args) throws IOException {
        //1.创建码头对象
        //需要绑定端口,表示在绑定的端口上接收数据
        //要跟发送端发送的端口保持一致
        DatagramSocket ds = new DatagramSocket(10000);
        //2.创建数据包
        DatagramPacket dp = new DatagramPacket(new byte[1024], 1024);
        //3.循环接收数据
        while (true) {
            ds.receive(dp);
            //4.获取数据并打印
            byte[] bytes = dp.getData();
            int len = dp.getLength();
            String ip = dp.getAddress().getHostAddress();
            System.out.println(ip + "发送过来了:" + new String(bytes, 0, len));
        }
        //在公司一般来讲,服务器不关机
        //所以服务器不需要释放资源
    }
}

TCP协议发送和接收数据

客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        //1,创建客户端Socket对象
        //去跟127.0.0.1的10000端口进行连接,如果连接不上,直接报错
        Socket socket = new Socket("127.0.0.1",10000);
        //2.从连接中,获取输出流,往服务器写数据
        OutputStream os = socket.getOutputStream();
        //3.写出数据
        String s = "abc";
        os.write(s.getBytes());
        //4.释放资源
        socket.close();
    }
}

服务端:

public class Server {
    public static void main(String[] args) throws IOException {
        //1.创建服务端对象,并绑定10000端口
        ServerSocket ss = new ServerSocket(10000);

        //2.等待客户端链接
        //此时服务端在accept方法这里,死等客户端来链接
        //如果有客户端来链接,会返回客户端的连接对象
        Socket socket = ss.accept();

        //3.从连接通道中获取流读数据
        InputStream is = socket.getInputStream();

        //4.因为不知道发了多少数据,所以循环读取
        int b;
        while ((b = is.read()) != -1){
            System.out.println((char)b);
        }
        //5.释放资源
        //断开和客户端之间的连接
        socket.close();
        //关闭服务器
        ss.close();

    }
}

3.7 文件上传

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

//给服务端上传文件
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建对象并连接服务器
        Socket socket = new Socket("127.0.0.1",10000);

        //2.读取本地文件并写到服务器
        //2.1创建本地的字节输入流对象,准备读取本地文件
        FileInputStream fis = new FileInputStream("meinv.jpg");
        //2.2获取客户端的输出流,准备往服务器写出数据
        OutputStream os = socket.getOutputStream();
        //2.3循环读取数据并写到服务器
        int b;//表示当前从文件中读取到的数据
        while((b = fis.read()) != -1){
            System.out.println(1);
            os.write(b);//把这个数据写到服务器
        }
        //给服务端一个结束标记
        socket.shutdownOutput();

        System.out.println("客户端上传文件完毕");

        //接收服务端返回的数据
        BufferedReader br = new BufferedReader
                (new InputStreamReader(socket.getInputStream()));
        String serverMessage = br.readLine();
        System.out.println(serverMessage);

        //3.释放资源
        socket.close();


    }
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class Server {
    public static void main(String[] args) throws IOException {
        //1.开启服务
        ServerSocket ss = new ServerSocket(10000);

        //2.等待客户端链接
        Socket socket = ss.accept();

        //3.获取输入流,准备接收客户端发送过来的数据
        InputStream is = socket.getInputStream();
        //生成一个随机的文件名
        String fileName = UUID.randomUUID().toString().replace("-", "");


        //创建本地的字节输出流对象,准备把读取到的数据保存到本地
        FileOutputStream fos = new FileOutputStream("day12-code\\serverfile\\" + fileName + ".jpg");
        //循环读取数据,并写到本地
        int b;
        //此时读到了shutdownOutput的结束标记
        while ((b = is.read()) != -1) {
            fos.write(b);
        }
        System.out.println("看看我执行了吗?");

        //给客户端返回上传成功的信息
        PrintStream ps = new PrintStream(socket.getOutputStream());
        ps.println("上传成功");


        //释放资源
        socket.close();
        ss.close();
    }
}

文件上传多线程版

客户端

//给服务端上传文件
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建对象并连接服务器
        Socket socket = new Socket("127.0.0.1",10000);
        //2.读取本地文件并写到服务器
        //2.1创建本地的字节输入流对象,准备读取本地文件
        FileInputStream fis = new FileInputStream("meinv.jpg");
        //2.2获取客户端的输出流,准备往服务器写出数据
        OutputStream os = socket.getOutputStream();
        //2.3循环读取数据并写到服务器
        int b;//表示当前从文件中读取到的数据
        while((b = fis.read()) != -1){
            System.out.println(1);
            os.write(b);//把这个数据写到服务器
        }
        //给服务端一个结束标记
        socket.shutdownOutput();
        System.out.println("客户端上传文件完毕");
        //接收服务端返回的数据
        BufferedReader br = new BufferedReader
            (new InputStreamReader(socket.getInputStream()));
        String serverMessage = br.readLine();
        System.out.println(serverMessage);
        //3.释放资源
        socket.close();
    }
}

服务端:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class Server {
    public static void main(String[] args) throws IOException {
        //1.开启服务
        ServerSocket ss = new ServerSocket(10000);
        while(true){
            //2.等待客户端链接
            Socket socket = ss.accept();
            //一旦有客户端来链接。就开启线程去进行处理
            new Thread(
                //线程要处理的任务
                ()->{
                    try {
                        //3.获取输入流,准备接收客户端发送过来的数据
                        InputStream is = socket.getInputStream();
                        //生成一个随机的文件名
                        String fileName = UUID.randomUUID().toString().replace("-", "");
                        //创建本地的字节输出流对象,准备把读取到的数据保存到本地
                        FileOutputStream fos = new FileOutputStream("day12-code\\serverfile\\" + fileName + ".jpg");
                        //循环读取数据,并写到本地
                        int b;
                        //此时读到了shutdownOutput的结束标记
                        while ((b = is.read()) != -1) {
                            fos.write(b);
                        }
                        System.out.println("看看我执行了吗?");

                        //给客户端返回上传成功的信息
                        PrintStream ps = new PrintStream(socket.getOutputStream());
                        ps.println("上传成功");

                        //断开客户端的连接
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        //释放资源的代码一般都是写在finally里面
                    }
                }
            ).start();
        }
        //服务器不关机
        //ss.close();
    }
}

3.9 文件上传线程池版

客户端:

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

//给服务端上传文件
public class Client {
    public static void main(String[] args) throws IOException {
        //1.创建对象并连接服务器
        Socket socket = new Socket("127.0.0.1",10000);

        //2.读取本地文件并写到服务器
        //2.1创建本地的字节输入流对象,准备读取本地文件
        FileInputStream fis = new FileInputStream("meinv.jpg");
        //2.2获取客户端的输出流,准备往服务器写出数据
        OutputStream os = socket.getOutputStream();
        //2.3循环读取数据并写到服务器
        int b;//表示当前从文件中读取到的数据
        while((b = fis.read()) != -1){
            System.out.println(1);
            os.write(b);//把这个数据写到服务器
        }
        //给服务端一个结束标记
        socket.shutdownOutput();

        System.out.println("客户端上传文件完毕");

        //接收服务端返回的数据
        BufferedReader br = new BufferedReader
            (new InputStreamReader(socket.getInputStream()));
        String serverMessage = br.readLine();
        System.out.println(serverMessage);

        //3.释放资源
        socket.close();


    }
}

服务端:

public class Server {
    public static void main(String[] args) throws IOException {
        //创建线程池
        ExecutorService pool = Executors.newFixedThreadPool(10);


        //1.开启服务
        ServerSocket ss = new ServerSocket(10000);

        while(true){
            //2.等待客户端链接
            Socket socket = ss.accept();
            //一旦有客户端来链接。就提交给线程池
            pool.submit(new ServerThread(socket));

        }
        //服务器不关机
        //ss.close();
    }
}

服务端线程类:

public class ServerThread implements Runnable {
    Socket socket;

    public ServerThread(Socket socket){
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            //3.获取输入流,准备接收客户端发送过来的数据
            InputStream is = socket.getInputStream();
            //生成一个随机的文件名
            String fileName = UUID.randomUUID().toString().replace("-", "");


            //创建本地的字节输出流对象,准备把读取到的数据保存到本地
            FileOutputStream fos = new FileOutputStream("day12-code\\serverfile\\" + fileName + ".jpg");
            //循环读取数据,并写到本地
            int b;
            //此时读到了shutdownOutput的结束标记
            while ((b = is.read()) != -1) {
                fos.write(b);
            }
            System.out.println("看看我执行了吗?");

            //给客户端返回上传成功的信息
            PrintStream ps = new PrintStream(socket.getOutputStream());
            ps.println("上传成功");

            //断开客户端的连接
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //释放资源的代码一般都是写在finally里面
        }
    }
}