java如何使用Socket(套接字)实现网络编程?

网络编程

大家肯定对微信是特别的了解了,那么你知道他是如何我发送消息之后你就可以接收消息,他是如何实现的吗?这里简单的给大家讲述一下微信信息的收发原理,A发送消息之后,消息首先传输到微信的服务器,之后再以极快的速度传输到B的手机。然而今天,我们带大家学习的是实现微信的类似功能,客户端可以收消息服务端也可以发消息,怎么样?听起来是不是很有意思,让我们一步一步实现这个功能~
那么具体如何实现呢?
首先我们得知道对方是谁?作为服务端。也就是接收端,我们得知道是谁发送的消息,发送消息的人的信息是什么?在计算机之间,也就是每个主机的IP地址,接下来第一步首先让我们看看,如何实现获取到对方的网络IP?

如何获取网络地址?

获取本机地址

//获取本地主机地址
InetAdress adress = InetAdress.getLocalHost();

//输出主机名和主机地址
System.out.printf(“你的主机地址是:”+address.getHostName()+"你的主机ip是:"+address.getHostAddress();

获取其他网络主机地址

  //获取制定域名主机地址
            InetAddress[] allByName = InetAddress.getAllByName("www.taobao.com");
            for (InetAddress ip :allByName){
                System.out.println("主机名称是"+ip.getHostName());
                System.out.println("主机ip是"+ip.getHostAddress());
            }
}

知识点:
获取所有主机地址 InetAddress.getAllByName()

全部代码:

package com.bohai.project.demo002;

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

/**
 * @author bohai
 * @date :2024-07-12-9:54
 * @description: 获取本地主机地址
 **/


/**
 * 获取本地主机
 *
 * 获取制定域名主机
 */
public class day1 {
    public static void main(String[] args) {
        try {
            //获取本地主机地址
            InetAddress localHost = InetAddress.getLocalHost();
            System.out.println("本地主机名"+localHost.getHostName()+"本地主机ip"+localHost.getHostAddress());

            //获取制定域名主机地址
            InetAddress[] allByName = InetAddress.getAllByName("www.taobao.com");
            for (InetAddress ip :allByName){
                System.out.println("主机名称是"+ip.getHostName());
                System.out.println("主机ip是"+ip.getHostAddress());
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}

好的,至此为止,相信你们已经学会了,如何获取主机的IP,以及对方主机的名字,接下来让我们看看,如果要发送消息,对方如果掉线了怎么办?举个例子:假如,你的女朋友要是查岗,方式是什么?肯定是不间断的给你发消息,确保你在线,对吧,这样才能给你发消息,你才能及时的收到消息,做你女朋友忠实的仆人~哈哈 ,言归正传,那么在计算机中使用编码如何实现随时让服务端一直在线呢?有一个专业名词,也就是网络心跳检测,顾名思义也就是,每隔一段时间进行一次检测,进行数据包的发送,来确保对方确实在线!

心跳检测实现

实现原理: 通过不间断的发送小的数据包,看对方是否接收到,并给出了响应信息
实现目标: 通过编程使用ping命令,对目标主机进行数据包的发送,查看是否两者保持联系

  1. 获取到程序运行时状态
  2. 在运行时对系统输入命令
  3. 读取输入流,使用Input(字节)
  4. 将字节转换为字符
  5. 将转换后的字符使用BufferedReader()将字符放在缓冲区,方便读取
  6. 将字符输出流进行遍历,最后输出

代码实现:

package com.bohai.project.demo002;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
* @author bohai
* @date :2024-07-12-10:06
* @description: 网络心跳检测
**/
public class day2 {
   public static void main(String[] args) {
       try {
           //获取当前应用程序的运行时
           Runtime runtime = Runtime.getRuntime();
           Process exec = runtime.exec("ping 8.130.33.20");
           //输入流进行读取(字节)
           InputStream inputStream = exec.getInputStream();
           //转换流进行转换(字符)
           InputStreamReader  in = new InputStreamReader(inputStream,"GBK");
           //使用BufferReader,通过行的形式进行输出
           BufferedReader read = new BufferedReader(in);

           //通过行的形式输出
           String line = null;
          while((line = read.readLine())!= null) {
              System.out.println(line);
           }
       } catch (IOException e) {
       }
   }
}

好的,我们呢已经实现了网络心跳检测,相信大家已经对网络之间的流有了初步的了解,接下来,让我们使用一个小的例子,让我们学习,如果有两个端,一个是服务端,一个是客户端,那么我们如何实现客户端发消息,之后让服务端进行收到客户端的消息,下面让我们一步一步实现~

一对一简易版通讯实现

实现原理:

【服务端】 创建ServerSocket监听一个端口,打开服务器,等待服务器进行连接,连接成功之后使用输入流,使用while(true),死循环进行接收客户端的请求,将网络上发送过来的客户端消息通过输入流读取,继续while((line = read.readLine()) != -1)使用read.readLine().读取所有信息,之后再展示到控制台上。

【客户端】 创建Socker指定目标服务器的IP地址端口号,使用输出流将信息写入到服务端,再使用write()方法将需要传输的字符进行写入,之后最重要的一步,因为是使用了BufferWriter()加快了字符流的处理速度,因为原理是BufferWriter()实现了缓冲区(积攒之后在进行处理),所以这里要使用writer.fluse();清空缓存区,将之前接收到的信息,写入到客户端中。

客户端

实现目标: 客户端发送消息,服务端可以接受到消息
实现步骤:

  1. 创建Socket对象,指定请求的ip地址和端口号
  2. 创建输出流使用socket.getOutputStream()进行写入网络数据,并使用OutputStreamWrite()将字节转换为字符,再使用BufferWriter()将数据放在缓冲区,方便操作。
  3. 使用write()进行写入数据
  4. 使用flush()清空缓冲区,之后写入数据到服务器。

客户端代码实现

package com.bohai.project.demo002;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * @author bohai
 * @date :2024-07-12-11:09
 * @description: 客户端请求
 **/
public class day3 {
    public static void main(String[] args) {
        //一定要及时关闭,否则就会报错,要放在括号里!!!!要么手动关闭!!
        try(Socket socket = new Socket("192.168.199.157",8848);) {
            //创建客户端指定
            BufferedWriter writer = new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())
            );
            writer.write("要亲亲~~~~");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端

实现步骤:

  1. 创建ServerSocket()并设置需要监听的端口号
  2. 使用while(true)死循环进行持续接受客户端的请求
  3. 使用accept()打开服务,进入待连接状态
  4. 获取到当前连接主机的名字和ip地址
  5. 在请求体内使用输入流,使用获取网络ServerSocker.getInputStream()获取网络输入流,并使用InputStreamRead()将获取到的网络字节输入流转换为字符输入流,最后交给BufferWrite()进行缓冲区处理
  6. 使用read.readLine()进行读取数据
  7. 之后输出是谁,给服务端发送了什么信息

全部代码:

public class day3_1 {
    public static void main(String[] args) {
        //创建Socket对象,监听8848端口
        try(ServerSocket serverSocket = new ServerSocket(8848)) {

            while(true){

                Socket clientSocket = serverSocket.accept();

                //
                InetAddress clientNextAddresss = clientSocket.getInetAddress();
                System.out.printf("客户端%s正在连接服务器.......\n",clientNextAddresss.getHostAddress());

                //服务端读取客户端的信息
                BufferedReader reader = new BufferedReader(
                        new InputStreamReader(clientSocket.getInputStream())
                );
                String message = reader.readLine();
                System.out.printf("客户端%s说%s\n",clientNextAddresss.getHostName(),message);
            }


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

    }
}

人工智能的实现(你问我答)

实现功能: 客户端提问一些问题,服务端进行对应的回答

AI客户端:

实现步骤:

  1. 使用Scanner进行输入操作,并且是一行一行的输入
  2. 使用Socket设置对应的服务器的ip地址和对应的端口号,创建提出问题的输出流(字节转字符并使用BufferWrite()缓冲区)创建接收问题的输入流(字节转字符使用BufferRead()缓冲区)
  3. 使用write(“Scanner”input.nextLine())
  4. 使用flush()进行清空缓冲区
  5. 同样暂时关闭输出流
  6. 使用输入流接收每一行的数据

代码实现:

package com.bohai.project.demo002;

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

/**
 * @author bohai
 * @date :2024-07-12-14:16
 * @description: Ai客户端
 **/
public class AiClient {
    public static void main(String[] args) {

        //控制台输入问题
        Scanner input = new Scanner(System.in);
        String question = input.nextLine();


        //读取输入流输出流
        try( Socket clientSocket = new Socket(InetAddress.getLocalHost(),8848)) {
            //向服务端发消息
            BufferedWriter write = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));
            //接收答案
            BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));



            //向服务端发送“问题“(传送到服务器)
            write.write(question);
            write.flush();
            //暂时关闭输出流
            clientSocket.shutdownOutput();
            //接收服务端返回的答案

            String ans = reader.readLine();
            System.out.println("服务端的回答"+ans);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

AI服务端

实现步骤:

  1. 使用serverSocket进行监听对应的接口
  2. 使用accept()打开 端口进行等待连接
  3. 定义输入流(接收消息)和输出流(返回响应),都是需要转换为字符
  4. 使用read.readLine() 拿到客户端的问题,如果问题为空或者问题的长度为0则跳出循环
  5. 输出客户端的ip地址和问题
  6. 再使用map.get()通过question拿到answer
  7. 如果拿到的的answer等于空,则使用三元表达式返回我不知道你在说什么!
  8. 之后将answer通过write写入到客户端
实现代码:
package com.bohai.project.demo002;

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

/**
 * @author bohai
 * @date :2024-07-12-12:09
 * @description: ai智能
 **/
public class AiService {
    private static HashMap<String ,String> map = new HashMap<String ,String>();
    static{
        map.put("你好", "你好呀,孙子");
        map.put("hi", "hello,孙子");
        map.put("hello", "hi,孙子");
        map.put("吃了吗", "没呢,孙子");
        map.put("很高兴认识你", "我也是哦");

    }

    public static void main(String[] args) {
        //创建一个socket服务打开8848端口
        try(ServerSocket serverSocket = new ServerSocket(8848)) {
            while (true) {
                //启动服务端,并进入“等待状态”
                Socket clientSocket = serverSocket.accept();
                String clientIp = clientSocket.getInetAddress().getHostAddress();

                //输入流:读取客户端发送的”问题"
                //输出流:发送问题答案给客户端
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(clientSocket.getInputStream()));
                     BufferedWriter writer = new BufferedWriter(
                             new OutputStreamWriter(clientSocket.getOutputStream()));) {

                    //读取来自客户端的问题
                    String question = reader.readLine();
                    if (question == null || question.length() ==0){
                        continue;
                    }
                    System.out.printf("来自客户端[%s]的问题:%s\n",clientIp,question);

                    String answer = map.get(question);
                    //如果输入其他数值,则返回固定答案

                    answer = answer == null ?"对不起,我不知道你在说什么!":answer;
                    writer.write(answer);

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

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

接下来让我们继续深一步,实现一个小demo,做一个照片上传的服务端和客户端
具体实现功能:
服务端收到客户端的照片之后,服务端向客户端返回一个成上传的响应,表示图片上传成功
具体实现步骤:
服务端:

  1. 使用socket()监听端口之后,等待连接,
  2. 首先知道是谁在向我传输图片,并把他主机的ip地址打印输出到控制台
  3. 定义输入流(接收客户端的图片流)和输出流(向客户端发送图片上传成功响应)和输出流(在本地保存接收到的客户端照片)
  4. 使用while(true)持续接受客户端的网络请求,
  5. 读取网络图片(客户端图片)每次1024个字节char[] buff = new char(),定义一个len = 0直到所有字节读取完成才算结束,while((len = reader.read(buff)) != 0)表示所有字节读取完毕,进行写入write.write(参数一:buff(所有读到的字节),参数二(开始位置),参数三(结束位置))
  6. 最后将响应信息返回到客户端,也就是read1.read()
package com.bohai.project.demo002;

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

/**
 * @author bohai
 * @date :2024-07-12-16:06
 * @description: 网络服务器
 **/
public class PictureService {
    public static void main(String[] args) {
        try(ServerSocket serverSocket= new ServerSocket(8488);) {

            while (true){
                //开启服务器等待连接。。。
                Socket accept = serverSocket.accept();
                //定义客户端的名字(谁上传)
                InetAddress inetAddress = accept.getInetAddress();
                //获取连接服务的ip地址
                String hostAddress = inetAddress.getHostAddress();
                System.out.printf("%s正在上传文件",hostAddress);

                try (   //读取客户端的输入流
                        BufferedInputStream reader = new BufferedInputStream(accept.getInputStream());
                        //保存到本地的输出流
                        BufferedOutputStream write = new BufferedOutputStream(new FileOutputStream("F:\\桌面\\捷丰星球\\西安建筑科技大学华清学院"+inetAddress.getHostAddress().replace('.','-').concat(".jpg")));
                        //返回到客户端的输出流
                        BufferedReader reader1 = new BufferedReader(new InputStreamReader(accept.getInputStream()))
                ){

                    //读取文件,并写入本地文件
                    byte[] buff = new byte[1024];
                    int len = -1;
                    while((len = reader.read(buff))!= -1){
                        write.write(buff,0,len);
                    }
                    System.out.println("图片上传完整!");

                    //将读取到的信息进行返回到客户端
                    reader1.readLine();

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

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

    }
}

客户端
实现步骤:

  1. 使用socket,指定与服务器的连接及其连接服务的端口号
  2. 定义输入流(本地拿到图片),定义输出流(将图片传输到服务端),定义输入流(接收服务端返回的响应)
  3. 使用每次读取1024个字节进行对本地的图片进行读取,之后将结果写入到服务端
  4. 使用flush()进行缓冲区的清空,将读取到的信息进行写入
  5. shutdownOutput() 暂时关闭输出流
  6. 在使用reader.read()读取一下来自服务端的消息
  7. 打印在输出台上
代码实现:
package com.bohai.project.demo002;

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

/**
 * @author bohai
 * @date :2024-07-12-14:47
 * @description: 上传图片
 **/
public class PictureClient {
    public static void main(String[] args) {
        String serverAddress = "192.168.199.157";
        int serverPort = 8848;

        // 要上传的图片文件路径
        String imageFilePath = "F:\\桌面\\捷丰星球\\西安建筑科技大学华清学院\\2024年各科作业\\软件测试上机作业\\第二次\\project001\\微信截图_20240712150012.png";

        try (Socket socket = new Socket(serverAddress, serverPort);
             //将文件转化为字节
             FileInputStream fis = new FileInputStream(imageFilePath);
             //使用Buffer处理
             BufferedInputStream bis = new BufferedInputStream(fis);
             //输出流
             OutputStream os = socket.getOutputStream()) {

            byte[] buffer = new byte[1024];
            int bytesRead;

            // 读取图片文件并发送到服务器
            while ((bytesRead = bis.read(buffer)) != -1) {
                os.write(buffer);
            }
            //刷新缓存区
            os.flush();

            //暂时关闭输出流
            socket.shutdownOutput();

            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String response = reader.readLine();
            System.out.println("服务器响应: " + response);

        } catch (UnknownHostException e) {
            System.err.println("无法找到服务器!");
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("网络错误或文件读写错误!");
            e.printStackTrace();
        }
    }
}

好的,同志们胜利就在眼前,最后一个让我们看看,如果是浏览器请求你的服务器会成功吗?如何才能使用浏览器来成功的请求你使用java写的服务端呢?接下来让我们一步一步实现~

HTTP网络编程

实现要求: 通过谷歌浏览器进行访问服务器,就可以在页面上显示自己服务器定义的响应数据
实现步骤: 哈哈,老步骤啦~大家都记住了吧

  1. 使用serverSocket进行端口的监听(ip地址默认是本地)
  2. 使用accept()进行开启端口,等待连接
  3. 定义输出流(给浏览器200的响应码),定义输入流,接收浏览器(接收请求头信息),定义一个输出字节流(想浏览器上传图片)
  4. 使用while()读浏览器返回的每一样数据()【接收请求头信息已经完成】

                //读取http报文
                String line = null;
                while ((line = reader.readLine()) != null){
                    System.out.println(line);
                }
  1. 向浏览器写入,状态码等信息(注意换行,方便浏览器识别)【浏览器已经完成了页面响应】

                    write.write("HTTP/1.1 200 OK\r\n");
                    write.write("Content-Type:text/html;charset:gbk \r\n");
                    write.write("\r\n");
                    write.write("tnianiajfjlj\r\n");
                    write.write("<br/>");

                    write.flush();
  1. 使用输入流本地拿到一个图片(字节)信息,带上请求头,请求方式等
  byte[] bytes = Files.readAllBytes(
                            Paths.get("F:\\桌面\\捷丰星球\\西安建筑科技大学华清学院\\2024年各科作业\\软件测试上机作业\\第二次\\project001\\微信截图_20240712150012.png"));

                    outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
                    outputStream.write("Content-Type:image/jpeg \r\n".getBytes(StandardCharsets.UTF_8));
                    outputStream.write("\r\n".getBytes());
                    outputStream.write(bytes);
                    outputStream.flush();


小知识:
什么时候会用到flush()?只有在输出流也就是写入是才会用到清空缓冲区~
全部代码:

package com.bohai.project.demo002;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

/**
 * @author bohai
 * @date :2024-07-12-16:29
 * @description: 浏览器请求
 **/
public class HttpService {
    public static void main(String[] args) {
        try (ServerSocket httpSocket = new ServerSocket(8008)){
            while(true){
                //开启服务器,等待请求
                Socket accept = httpSocket.accept();

                //读网络信息  输入(读取报文)
                try (
                            //向浏览器上传图片(使用字节流)
                            BufferedOutputStream outputStream = new BufferedOutputStream(accept.getOutputStream());
                            //向浏览器发送成功响应信息200字符流
                            BufferedWriter write = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));

                            //接收浏览器的请求头信息
                            BufferedReader reader = new BufferedReader(new InputStreamReader(accept.getInputStream()));

                ){

//                //读取http报文
//                String line = null;
//                while ((line = reader.readLine()) != null){
//                    System.out.println(line);
//                }


//                    write.write("HTTP/1.1 200 OK\r\n");
//                    write.write("Content-Type:text/html;charset:gbk \r\n");
//                    write.write("\r\n");
//                    write.write("tnianiajfjlj\r\n");
//                    write.write("<br/>");
//
//                    write.flush();

                    byte[] bytes = Files.readAllBytes(
                            Paths.get("F:\\桌面\\捷丰星球\\西安建筑科技大学华清学院\\2024年各科作业\\软件测试上机作业\\第二次\\project001\\微信截图_20240712150012.png"));

                    outputStream.write("HTTP/1.1 200 OK\r\n".getBytes());
                    outputStream.write("Content-Type:image/jpeg \r\n".getBytes(StandardCharsets.UTF_8));
                    outputStream.write("\r\n".getBytes());
                    outputStream.write(bytes);
                    outputStream.flush();




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


            }

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

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

银河流浪家007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值