Java语言的学习与实践12 (网络编程,实现聊天,图片的上传和下载)

1.收获

说实话,我今天有点跟不上了,是因为我遇到一些问题后就会去想它,想把它弄明白,可是时间不允许,就在我想的时候,课已经上了很大一部分了,就感觉有点急,不像前面那样,有的东西是已经学过的,有的东西一想就会有思路,可能今天学的是从前没有接触过的东西,理解上就会有点慢,花费的时间就会多一点,还好的是慢慢可以理解这些知识。有时候在想自己为什么自己在理解上赶不上别人,也许自己有点差吧,这也许就是差距,但是这个差距并不是鸿沟,还以可以跨过去的。加油!

2.技术

(1)网络编程
(2)实现聊天
(3)实现向服务器上传图片和在服务器上下载图片

3.技术的实际应用和实践

(1)网络编程
什么是网络编程:
实现两台不同的计算机之间传递数据。
在生活中,我们用QQ聊天,浏览器上网等这些都是需要数据的传递的。

数据是怎样传递:
客服端:手机APP 浏览器 在桌面QQ,当客服端需要数据时服务器就向客服端传递,当然客户端也可向服务器端传递数据。

服务器端:存储/处理数据
数据传输:TCP/IP协议
TCP:面向连接的 安全的 打电话
UDP:面向无连接的 不安全 但快 飞鸽传书

网络中的数据传输:Socket实现->套接字
-----Socket类:客服端
------ServerSocket:服务器端

读取数据:服务器 客服端读取数据
------ BufferedReader->InputStringReader->socket.getInputStream
--- 读取终端
-------BufferedReader->InputStringReader->System.in
输出数据:客服端 服务器端
-----BufferedWriter->OutputStreamWriter->socket.getOutputStream()
-----PrintStream->socket.getOutputStream()

在生活中,在浏览器中我们经常看到一些网址
网址的组成:
http://www.baidu.com/sesrch?code=android&type=1

  • http/https:传输协议 Tcp/ IP
  • www.baidu.com:域名 表示一台网络中的电脑
  • ipv4 ipv6IP地址:唯一标识一台网络中的计算机
    ------ 32位 四个部分每个部分是八个字节
    ------ 10.129.25.253<->www.baidu.com
  • DNS服务器:域名解析 将www.baidu.com类型的域名解析为对应的ip地址
  • sesrch:对应的后台的程序文件 php java jsp
  • ? 表示运行这个程序传递的参数
  • code=android code 是服务器端规定的字段
  • & 如果有多个参数使用&符号链接

IP地址:唯一标识某一台电脑
端口号:唯一标识电脑上的某一个进程
通过IP地址来访问这台电脑
通过端口号(某一个进程)来访问应用程序

(2)实现聊天
在这个实现的过程中并不是那么一帆风顺,首先我们实现的单向的发送消息,后来是实现单向的从终端输入传输,只能实现由客服端到服务器端或者由服务器端到客户端,再后来就是实现双向的发送消息。最后实现了群聊。

单向的发固定的内容
首先创建两个类来分别模拟客服端和服务器端

//客服端
lass Client{
    public static void main(String[] args)throws IOException {}
}
//服务器端
class Server{
    public static void main(String[] args)throws IOException {}
}

在客服端
1.创建用于通信的socket
// 指明和谁通信:IP地址 端口号

        Socket socket=new Socket("10.129.25.253",7171);

2.接收服务器端的数据

        BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));

3.读取服务器端发送的数据

        String line=null;
        while((line=br.readLine())!=null){
            System.out.println(line);
        }

4.向服务器端发送数据

        PrintStream ps=new PrintStream(socket.getOutputStream());
        ps.println("发送成功");
        socket.shutdownOutput();

在服务器端
1.创建服务器端的serversocket

        ServerSocket ss=new ServerSocket(7171);

2.获取连接的客服端的socket

        Socket clientSocket=ss.accept();

3.向客服端发送数据

                PrintStream ps=new PrintStream(clientSocket.getOutputStream());
        ps.println("登陆成功!!!");
        //输出完毕
        clientSocket.shutdownOutput();

4.接收客服端发来的数据


        BufferedReader br=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        String line=null;
        while((line=br.readLine())!=null){
            System.out.println(line);
        }

这样只能时发固定的消息,在上面的基础上做出一点修改,从终端输入信息
模拟客服端

class MyClient{
    public static void main(String[] args) {
        //连接服务器端的socket
        Socket socket = null;
        try {
            socket=new Socket("10.129.25.253",8888);

            //接收服务器端信息
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line=null;
            while ((line=br.readLine())!=null){
                System.out.println(line);
            }
            //从键盘输入数据 发送给服务器

        } catch (IOException e) {
            System.out.println("网络出错 请重新链接");
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
               e.printStackTrace();
            }
        }
    }
}

模拟服务器

class MyServer{
    public static void main(String[] args) {
        //创建ServerSocket
        try(ServerSocket ss=new ServerSocket(8888)) {
            //监听客服端的链接
            Socket socket=ss.accept();

            //从终端接收数据
            BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
            //获取向客服端输出数据的输出流
            PrintStream sp=new PrintStream(socket.getOutputStream());
            String line=null;
            while((line=keyin.readLine())!=null){
                //发送给客服端
                sp.println(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

还有个注意点就是在运行时一定要先运行服务器,在运行客服端,不然就连不上服务器
效果:
在服务器中输入:


image.png

在客服端显示:


image.png

实现双向聊天

image.png

为啥要用多线程来实现:为了能够让聊天更加流畅,不至于出现阻塞的现象,线程是一直运行着的,主线程和子线程两者不是同时运行的,他们没有先后顺序

1.创建一个子线程处理客服端接收服务器端的数据

class ClientThread implements Runnable{
     private  Socket socket;
     public ClientThread(Socket socket){
         this.socket=socket;
     }
    @Override
    public void run() {
        BufferedReader bufferedReader=null;
        try {
            //获取服务器端地输入流对象
           bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //读取数据
            String line=null;
            while ((line=bufferedReader.readLine())!=null){
                System.out.println(line);
            }
        } catch (IOException e) {
            System.out.println("网络出错 请重新链接");
            System.exit(-1);
        }finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if(socket!=null){
                    socket.close();
                }
            }catch (IOException e){

            }
        }
    }
}

2.模拟客服端,实现子线程

class Client{
    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("10.129.25.253",2584);

        //用一个子线程处理服务器处理服务器端数据
        new Thread(new ClientThread(socket)).start();
        //主线程处理终端输入 发送给服务器端
         BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
         PrintStream printStream=new PrintStream(socket.getOutputStream());
         String line=null;
         while ((line=bufferedReader.readLine())!=null){
             printStream.println(line);
         }
    }
}

3.创建一个子线程处理服务器端接收客服端地数据

class ServerThead extends Thread{
    private Socket socket;
    public  ServerThead(Socket socket){
       this.socket=socket;
    }

    @Override
    public void run() {
        BufferedReader bufferedReader=null;

        try{
            bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line=null;
            while ((line=bufferedReader.readLine())!=null){
                System.out.println(line);
            }
        }catch (IOException e){
            System.out.println("网络异常 请重新链接");
            System.exit(-1);
        }finally {
            try{
                if(bufferedReader!=null){
                    bufferedReader.close();
                }
                if(socket!=null){
                    socket.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        super.run();
    }
}

4.模拟服务器端,实现子线程

class Server{
    public static void main(String[] args)throws IOException {
        ServerSocket serverSocket=new ServerSocket(2584);
        Socket socket=serverSocket.accept();

        //创建子线程 处理客服端地输入信息
        new ServerThead(socket).start();

        //终端输入
        BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
        //客服端地输入流对象
        PrintStream ps=new PrintStream(socket.getOutputStream());
        //读取终端的输入 输出给客服端
        String line=null;
        while ((line=keyin.readLine())!=null){
            ps.println(line);
        }
    }
}

实行效果:
在服务器端(图中绿色表示自己发出的)

image.png

在客服端(图中绿色表示自己发出的)

image.png

实现群聊

如何实现群聊:
在服务器端维护一个数组(socket)这个数组是用来存放每个客户端的,以便于在发群发消息的时候,能够群发消息,通过数组的遍历来实现群发消息
群聊的机制:

image.png

1.在服务器端实现一个数组

public static ArrayList<Socket> sockets = new ArrayList<>();

2.创建一个子线程处理服务器端接收客服端地数据

static class ServerThead extends Thread {
        private Socket socket;

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

        @Override
        public void run() {
            BufferedReader bufferedReader = null;

            try {
                bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    //群发消息
                    //遍历数组
                    for (Socket socket : sockets){
                        //socket对应的客服端发送消息
                        PrintStream printStream=new PrintStream(socket.getOutputStream());
                        printStream.println(line);
                    }
                }

            } catch (IOException e) {
                System.out.println("网络异常 请重新链接");
                System.exit(-1);
            } finally {
                try {
                    if (bufferedReader != null) {
                        bufferedReader.close();
                    }
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            super.run();
        }
    }
}

3.在服务器中等待客服端来连接,并把他们放入数组保存,开启一个线程处理每个客服端的输入

public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(1111);

        //不停地等待客服端来链接
        while (true) {
            Socket socket = serverSocket.accept();
            sockets.add(socket);
            //开启一个线程处理每个客服端的输入
            new ServerThead(socket).start();
        }
    }

4.创建一个子程序 接收服务器发送的数据

class ClientThread extends Thread{
    private Socket socket;
    public ClientThread(Socket socket){
        this.socket=socket;
    }
    BufferedReader bufferedReader=null;
    @Override
    public void run() {
        try {
           bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
           String line=null;
           while ((line=bufferedReader.readLine())!=null){
               System.out.println(line);
           }
        } catch (IOException e) {
            System.out.println("网络错误 请重新链接");
            System.exit(-1);
        }finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
                if(socket!=null){
                    socket.close();
                }
            }catch (IOException e){

            }
        }
        super.run();
    }
}

5.创建客服端,并开始子程序

class Client{
    public static void main(String[] args) throws IOException{
        Socket socket=new Socket("10.129.25.253",1111);
        //开启子程序
        new ClientThread(socket).start();
         //客服端的终端输出给服务器
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
        PrintStream printStream=new PrintStream(socket.getOutputStream());
        String line=null;
        while((line=bufferedReader.readLine())!=null){
            printStream.println(line);
        }
    }
}
class Client1{
    public static void main(String[] args) throws IOException{
        Socket socket=new Socket("10.129.25.253",1111);
        //开启子程序
        new ClientThread(socket).start();
        //客服端的终端输出给服务器
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
        PrintStream printStream=new PrintStream(socket.getOutputStream());
        String line=null;
        while((line=bufferedReader.readLine())!=null){
            printStream.println(line);
        }
    }
}
class Client2{
    public static void main(String[] args) throws IOException{
        Socket socket=new Socket("10.129.25.253",1111);
        //开启子程序
        new ClientThread(socket).start();
        //客服端的终端输出给服务器
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
        PrintStream printStream=new PrintStream(socket.getOutputStream());
        String line=null;
        while((line=bufferedReader.readLine())!=null){
            printStream.println(line);
        }
    }
}

实现效果:
客服端1:(图中绿色表示自己发出的)


image.png

客服端2(图中绿色表示自己发出的)

image.png

客服端3(图中绿色表示自己发出的)

image.png

(3)实现向服务器上传图片和在服务器上下载图片
实现客服端向服务器上传照片,实现客户端在服务器中下载照片。在这两个过程中图片的传输是用字节流来传输的,不能用字符串或者是字符数组来传递。在这两个过程流程都是一样的,只是在不同的过程中

向服务器上传图片

class Client{
    public static void main(String[] args) throws IOException {
         //链接服务器端
        Socket socket=new Socket("10.129.25.253",6666);

        //创建服务器端的输入流 用于接收服务器端发来的数据
        BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        System.out.println(br.readLine());

        //向服务器端发送一张图片
        //1.将文件写到内存
        FileInputStream fis=new FileInputStream("C:\\Users\\ASUS\\Desktop\\1.jpg");
        //2.创建字节流 outputStream
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] buf = new byte[1024];
        int len = -1;
        while ((len = fis.read(buf)) != -1){
            bos.write(buf,0,len);
        }
        socket.shutdownOutput();
    }
}
class Server{
    public static void main(String[] args)throws IOException {
       //从创建服务器端ServerSocket
        ServerSocket ss=new ServerSocket(6666);
        //监听客服端连接
        //当有客服端来连接这个服务器时 就可以得到对应的socket
        Socket socket= ss.accept();

        //创建客服端对应的输出流 用于向这个客服端发送数据
        PrintStream ps=new PrintStream(socket.getOutputStream());
        ps.println("连接成功 可以发数据了");

        // 接收客服端传递过来的图片
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());

        //文件对应的输出流
        String path = "D:\\android\\Javaday1\\java\\src\\main\\java\\day12media\\1.jpg";
        FileOutputStream fos = new FileOutputStream(path);

        byte[] buf = new byte[1024];
        int len = -1;
        while((len = bis.read(buf)) != -1){
            fos.write(buf,0,len);
        }
    }
}

在服务器上下载图片

//模拟客户端
class Client {
    public static void main(String[] args) throws IOException {
        Socket socket=new Socket("10.129.25.253",3333);
        //接收
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        System.out.println(bufferedReader.readLine());

        //接收服务器传来的信息
        BufferedInputStream b=new BufferedInputStream(socket.getInputStream());
        String path="D:\\android\\Javaday1\\java\\src\\main\\java\\day12media1\\1.jpg";
        FileOutputStream f =new FileOutputStream(path);
        byte[] j=new byte[1024];
        int len=0;
        while((len = b.read(j)) != -1){
            f.write(j,0,len);
        }

    }
}
//模拟服务器
class Server{
    public static void main(String[] args)throws IOException {
        ServerSocket serverSocket=new ServerSocket(3333);
        Socket socket=serverSocket.accept();
        PrintStream printStream=new PrintStream(socket.getOutputStream());
        printStream.println("下载成功!");

        FileInputStream fileInputStream=new FileInputStream("C:\\Users\\ASUS\\Desktop\\1.jpg");
        //向客服端传输数据
        BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(socket.getOutputStream());
        byte[] i=new byte[1024];
        int len=-1;
        while((len=fileInputStream.read(i))!=-1){
            bufferedOutputStream.write(i,0,len);
        }
        socket.shutdownOutput();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容