网络编程
大家肯定对微信是特别的了解了,那么你知道他是如何我发送消息之后你就可以接收消息,他是如何实现的吗?这里简单的给大家讲述一下微信信息的收发原理,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命令,对目标主机进行数据包的发送,查看是否两者保持联系
- 获取到程序运行时状态
- 在运行时对系统输入命令
- 读取输入流,使用Input(字节)
- 将字节转换为字符
- 将转换后的字符使用BufferedReader()将字符放在缓冲区,方便读取
- 将字符输出流进行遍历,最后输出
代码实现:
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();
清空缓存区,将之前接收到的信息,写入到客户端中。
客户端
实现目标: 客户端发送消息,服务端可以接受到消息
实现步骤:
- 创建Socket对象,指定请求的ip地址和端口号
- 创建输出流使用
socket.getOutputStream()
进行写入网络数据,并使用OutputStreamWrite()
将字节转换为字符,再使用BufferWriter()
将数据放在缓冲区,方便操作。 - 使用
write()
进行写入数据 - 使用
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();
}
}
}
服务端
实现步骤:
- 创建
ServerSocket()
并设置需要监听的端口号 - 使用
while(true)
死循环进行持续接受客户端的请求 - 使用
accept()
打开服务,进入待连接状态 - 获取到当前连接主机的名字和ip地址
- 在请求体内使用输入流,使用获取网络
ServerSocker.getInputStream()
获取网络输入流,并使用InputStreamRead()
将获取到的网络字节输入流转换为字符输入流,最后交给BufferWrite()
进行缓冲区处理 - 使用
read.readLine()
进行读取数据 - 之后输出是谁,给服务端发送了什么信息
全部代码:
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客户端:
实现步骤:
- 使用Scanner进行输入操作,并且是一行一行的输入
- 使用Socket设置
对应的服务器的ip地址和对应的端口号
,创建提出问题的输出流(字节转字符并使用BufferWrite()缓冲区)
创建接收问题的输入流(字节转字符使用BufferRead()缓冲区)
- 使用
write(“Scanner”input.nextLine())
- 使用
flush()
进行清空缓冲区 - 同样暂时关闭输出流
- 使用输入流接收每一行的数据
代码实现:
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服务端
实现步骤:
- 使用
serverSocket
进行监听对应的接口 - 使用
accept()
打开 端口进行等待连接 - 定义输入流(接收消息)和输出流(返回响应),都是需要转换为字符
- 使用
read.readLine()
拿到客户端的问题,如果问题为空或者问题的长度为0则跳出循环 - 输出客户端的ip地址和问题
- 再使用
map.get()
通过question拿到answer - 如果拿到的的
answer
等于空,则使用三元表达式返回我不知道你在说什么!
- 之后将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,做一个照片上传的服务端和客户端
具体实现功能:
服务端收到客户端的照片之后,服务端向客户端返回一个成上传的响应,表示图片上传成功
具体实现步骤:
服务端:
- 使用socket()监听端口之后,等待连接,
- 首先知道是谁在向我传输图片,并把他主机的ip地址打印输出到控制台
- 定义输入流(接收客户端的图片流)和输出流(向客户端发送图片上传成功响应)和输出流(在本地保存接收到的客户端照片)
- 使用
while(true)
持续接受客户端的网络请求, - 读取网络图片(客户端图片)每次1024个字节
char[] buff = new char()
,定义一个len = 0
直到所有字节读取完成才算结束,while((len = reader.read(buff)) != 0)
表示所有字节读取完毕,进行写入write.write(参数一:buff(所有读到的字节),参数二(开始位置),参数三(结束位置))
- 最后将响应信息返回到客户端,也就是
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();
}
}
}
客户端
实现步骤:
- 使用socket,指定与服务器的连接及其连接服务的端口号
- 定义输入流(本地拿到图片),定义输出流(将图片传输到服务端),定义输入流(接收服务端返回的响应)
- 使用每次读取1024个字节进行对本地的图片进行读取,之后将结果写入到服务端
使用flush()
进行缓冲区的清空,将读取到的信息进行写入shutdownOutput() 暂时关闭输出流
- 在使用reader.read()读取一下来自服务端的消息
- 打印在输出台上
代码实现:
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网络编程
实现要求: 通过谷歌浏览器进行访问服务器,就可以在页面上显示自己服务器定义的响应数据
实现步骤: 哈哈,老步骤啦~大家都记住了吧
- 使用
serverSocket
进行端口的监听(ip地址默认是本地) - 使用
accept()
进行开启端口,等待连接 - 定义输出流(给浏览器200的响应码),定义输入流,接收浏览器(接收请求头信息),定义一个输出字节流(想浏览器上传图片)
- 使用
while()
读浏览器返回的每一样数据()【接收请求头信息已经完成】
//读取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();
小知识:
什么时候会用到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();
}
}
}