java网络编程学习基础篇
Posted adventure.Li
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java网络编程学习基础篇相关的知识,希望对你有一定的参考价值。
一、基础背景
时代背景
自2000年左右,Web的快速发展以及2010左右的云原生和云计算的提出,网络通信的重要性越来越凸显出来;
- 对于用户来说:软件的响应速度和体验是越来越重要的,而网络通信是决定响应速度关键因素之一。
- 对于分布式系统:由于需要解决用户的大规模计算、存储需求,便采用分治的手段,进行分布式系统的构建。但分布式也具有一定的困难,例如一致性协调问题,通信的效率问题以及容错等,其中通信也是主要的考虑因素,因此现有大多数RPC框架都需要去解决该问题,使节点之间通信尽可能的快。
- 对于Java开发者:拥有网络编程技术,将打开一扇通往世界的窗,多线程、数据结构解决的是内部(单机)的数据组织和计算、存储,而网络编程将会将视角放大到整个世界的计算、存储。
重要知识
- 基本的网络通信模型、通信单位
- TCP、UDP的通信过程
- 序列化、IO的理解
- 应用协议的理解(HTTP、WS、FTP)
- RPC远程通信框架的理解(常见RPC :grpc,thrift)
- 理解Docker中的网络模型
- 考虑的基本问题:超时,请求与响应,C\\S模型
相关书籍
- 计算机网络基础(谢希仁),并采用cisco tracert进行搭建基础的网络拓扑,理解宏观的通信过程。
- 网络是怎么连接的,较为贴切、易懂地理解网络通信
- Unix网络编程,从OS角度去审视、分析网络编程
- netty权威指南,构建Java高性能通信框架
二、Java的相关知识
基本IO
对于计算机通信的过程,实际上计算机将其抽象为IO,从字面上理解即输入、输出;该模型可理解为 A — B;A和B可以为机器、进程、文件等端或点,中间的线则是通信的通道,即IO流,Channel,管道,物理上的光缆等,而在其中还需要进行传输信息,信息的载体即数据也是决定其传输效率的关键。因此网络编程基本围绕以下几点讨论:
- 端点的处理:File,Socket,进程,机器,数据库(本质也是file),考虑阻塞、非阻塞等,IO模型
- 消息的处理:对象,字节(byte[])、xml、JSON等,序列化问题
- 通信的协议:通信的标准协议,跨语言、跨机器等考虑
基本要点
- 理解字节流和字符流
- 理清IO的方向
- IO的开销很大,注意关闭流
- Java中的基础IO和NIO(JDK1.5后提出)
简单实践
进行文本的写入和读出;此外还可进行采用递归删除文件(或其他端节点)、创建文件等。
public class IoDemo
private final String FILE_PARENT_PATH = "E:\\\\JavaProjects\\\\LearnProjects\\\\java-backend\\\\java-base\\\\src\\\\main\\\\java\\\\com\\\\lyf\\\\network\\\\netty\\\\io";
@Test
public void testWriteToFile()
FileOutputStream fileOutputStream = null;
try
fileOutputStream = new FileOutputStream(FILE_PARENT_PATH+"\\\\test.txt");
fileOutputStream.write("hello".getBytes());
catch (IOException e)
e.printStackTrace();
finally
if(fileOutputStream!=null)
try
fileOutputStream.close();
catch (IOException e)
e.printStackTrace();
@Test
public void testReadFromFile()
FileInputStream fileInputStream = null;
try
fileInputStream = new FileInputStream(FILE_PARENT_PATH+"\\\\test.txt");
byte[] bytes = new byte[1024];
fileInputStream.read(bytes);
System.out.println(new String(bytes));
catch (IOException e)
e.printStackTrace();
finally
if(fileInputStream!=null)
try
fileInputStream.close();
catch (IOException e)
e.printStackTrace();
Socket通信-传输层
在Java中提供了传输层的通信能力;
- TCP:端:Socket 、ServerSocket;消息:IO流,写字节流
- UDP: 端:DatagramSocket ;消息:datagramPacket;
理解要点
- TCP和UDP的基本通信模型,理解TCP是如何保证可靠性的(可采用wireshark进行抓包看看)。
- 理解机器的IP和进程的确定的Port端口,进程冲突即端口冲突也是常见问题。
简单实践
设计一个简单的TCP和UDP通信(关于UDP的通信可靠保证,可参考nacos的早期推送模型com.alibaba.nacos.naming.remote.udp ),可在此基础上进行简单的聊天室设计。
public class TcpUdpTests
@Test
public void testUdpServer()
try
DatagramSocket datagramSocket = new DatagramSocket(90, InetAddress.getLocalHost());
// datagramSocket.bind(new InetSocketAddress(100));
DatagramPacket datagramPacket = new DatagramPacket("receive".getBytes(), 4);
datagramSocket.receive(datagramPacket);
System.out.println("receive::" + Arrays.toString(datagramPacket.getData()));
catch (IOException e)
e.printStackTrace();
@Test
public void testUdpClient()
try
DatagramSocket datagramSocket = new DatagramSocket(80);
//datagramSocket.connect(new InetSocketAddress(90));
DatagramPacket datagramPacket = new DatagramPacket("hello".getBytes(), 0, 5, InetAddress.getLocalHost(),
90);
System.out.println("发送UDP报");
datagramSocket.send(datagramPacket);
catch (IOException e)
e.printStackTrace();
应用层通信
- URLConnection :相关的详细使用
@Test
public void testApplicationProtUrl()
URL url = null;
InputStream inputStream = null;
try
url = new URL("http://www.baidu.com");
URLConnection connection = url.openConnection();
inputStream = connection.getInputStream();
byte[]bytes = new byte[1024];
inputStream.read(bytes);
System.out.println(new String(bytes));
catch (IOException e)
e.printStackTrace();
finally
try
inputStream.close();
catch (IOException e)
e.printStackTrace();
- HttpClient
httpClient = HttpClient.newHttpClient();
HttpRequest.BodyPublisher bodyPublisher = new HttpRequest.BodyPublisher()
@Override
public long contentLength()
return 0;
@Override
public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber)
;
httpRequest = HttpRequest.newBuilder().POST(bodyPublisher)
.header("Content-Type", "application/json")
.uri(URI.create("http://localhost:8080/test"))
.build();
更多具体可以直接看jdk-的java.net包。
常见的通信框架
- Apache的HttpClient
- OkHttp
对比可参考:该文章
RCP框架
现有问题
- Java的序列化问题:序列化的流比较大、开销时间比较大
- Java的自带框架通信效率比较低:阻塞问题
带来NIO的设计,提出selector\\channel(解决端的问题)、buffer(解决传输介质、消息的问题)的概念。在此基础上Java领域netty进行友好地封装。
理解序列化的问题(具体见 github java-base 模块 com.lyf.network.netty.sequence包下):
class Person implements Serializable
@java.io.Serial
private static final long serialVersionUID = -6849794470754667720L;
private String username;
private Integer age;
public String getUsername()
return username;
public void setUsername(String username)
this.username = username;
public Integer getAge()
return age;
public void setAge(Integer age)
this.age = age;
byte[] codeByNio()
ByteBuffer buffer = ByteBuffer.allocate(1024);
byte[] val = username.getBytes(StandardCharsets.UTF_8);
buffer.putInt(val.length);
buffer.put(val);
buffer.putInt(age);
buffer.flip();
byte[] rs = new byte[buffer.remaining()];
buffer.get(rs);
return rs;//buffer.array();
public class JavaSerialTests
@Test
public void testStreamSize() throws IOException
Person person = new Person();
person.setAge(0);
person.setUsername("Alan");
//java序列化
// ObjectOutputStream objectOutputStream = ObjectOutputStream.nullOutputStream();//new ObjectOutputStream();
// objectOutputStream.writeObject(person);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
objectOutputStream.writeObject(person);
objectOutputStream.flush();
objectOutputStream.close();
byte[] bytes = baos.toByteArray();
System.out.println(bytes.length+":"+new String(bytes));
System.out.println(person.codeByNio().length+":"+new String(person.codeByNio()));
// java 附带很多Java相关的信息导致?流过大
// 202:�� sr %com.lyf.network.netty.sequence.Person��8z;�8 L aget Ljava/lang/Integer;Lusernamet Ljava/lang/String;xpsr java.lang.Integer⠤���8 I valuexr java.lang.Number������ xp t Alan
// 12: Alan
/**
* cost by java serial:202
* cost by buffer:105
* @throws IOException
*/
@Test
public void testSpeed() throws IOException
final int loop = 10000;
Person person = new Person();
person.setAge(0);
person.setUsername("Alan");
long c1 = System.currentTimeMillis();
for(int i=0;i<loop;i++)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(baos);
objectOutputStream.writeObject(person);
objectOutputStream.flush();
objectOutputStream.close();
byte[] bytes = baos.toByteArray();
System.out.println("cost by java serial:"+(System.currentTimeMillis()-c1));
c1 = System.currentTimeMillis();
for(int i=0;i<loop;i++)
person.codeByNio();
System.out.println("cost by buffer:"+(System.currentTimeMillis()-c1));
-
采用NIO和Netty进行简单的通信:
-
分析nacos中的Grpc的设计:
以上是关于java网络编程学习基础篇的主要内容,如果未能解决你的问题,请参考以下文章