Java网络编程简明教程

网络编程 

计算机网络相关概念

计算机网络是两台或更多的计算机组成的网络,同一网络内的任意两台计算机可以直接通信,所有计算机必须遵循同一种网络协议。

  • 互联网

    • 互联网是连接计算机网络的网络
    • 互联网采取TCP/IP协议
    • 其中最重要的两个协议是TCP协议和IP协议
  • IP地址和网关

    • IP地址用于唯一标识一个网络接口

    • IPv4采用32位地址
      IPv4地址实际是一个二进制32位的整数,为了便于识别,用十六进制表示后可以分为4组数字,每组数字转换成十进制后用“.”隔开就是我们见到的IP地址:

    • IPv6采用128位地址

    • 公网IP地址可以直接被访问

    • 内网IP地址只能在内网访问

    • 本机地址使用127.0.0.1

    • 通常路由器或交换机有两个网卡(两个IP地址),分别连接两个不同的网络:

    • 同一网络下的计算机可以直接通信,他们的网络号相同,网络号由IP地址和子掩码按组对齐做与运算得到:

    • 不同网络下的计算机需要通过路由器或交换机网络设备间接通信,这样的网络设备叫做网关:

    • 网关的作用是连接多个网络,负责把一个网络的数据包发送到另一个网络,过程叫做路由:

    • 一台计算机的网络拥有IP地址,子网掩码和网关(路由器)三个关键配置:

  • 域名

由于IP地址不便于记忆,通常使用域名来访问特定的服务,域名解析服务器DNS负责将域名翻译成对应的IP地址,客户端再根据IP地址访问服务器:

  • TCP/IP协议

    • IP协议是一个分组交换协议,不保证可靠传输,一个数据包通过IP协议传输会自动分成若干小的数据包然后通过网络进行传输
    • TCP(Transmission Control Protocol)协议是一个传输控制协议,建立再IP协议之上,IP协议负责传输数据包,TCP协议负责控制传输数据包;TCP协议传输之前需要先建立连接,然后才能传输数据,传输完成后断开连接;TCP协议是一个可靠传输协议,他通过接收确认,超时重传实现;TCP协议支持双向通信,双方可以同时传输和接收数据
  • UDP协议

UDP(User Datagram Protocol)协议是数据报文协议,不面向连接,不保证可靠传输,由于UDP协议传输效率高,通常用来传输视频等能容忍丢失部分数据的文件。

  • Socket

Socket通常称为套接字,用于应用程序之间建立远程连接,Socket内部通过TCP/IP协议进行数据传输,可以简单的理解为对IP地址和端口号的描述。Socket接口是由计算机操作系统提供的,编程语言提供对Socket接口调用的封装。通常计算机同时运行多个应用程序,仅仅有IP地址是无法确定由哪个应用程序接收数据,所以操作系统抽象出Socket接口,每个应用程序对应不同的socket(每个网络应用程序分配不同的端口号)。端口号的范围是0~65535,小于1024的端口需要管理员权限,大于1024的端口可以任意用户的应用程序打开。

Socket编程需要实现服务器端和客户端,因为这两个设备通讯时,需要知道对方的IP和端口号。通常服务器端有个固定的端口号,客户端直接通过服务器的IP地址和端口号进行访问服务器端,同时告知客户端的端口号,于是他们之间就可以通过socket进行通信。

TCP编程

  • TCP客户端

Java提供了Socket类ServerSocket类对计算机操作系统的Socket进行调用。客户端使用Socket(InetAddress, port)构造方法传入IP地址和端口号打开Socket,与远程服务区指定端口进行连接, 然后调用socket的getInputStream和getOutputStream方法获取输入和输出流就可以读写TCP的字节流:

// 连接远程服务器
Socket socket = new Socket(InetAddress, port);
// 读写字节流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
  • TCP服务端

服务器端通过ServerSocket(port)构造方法传入端口号来监听指定的端口,然后通过accept()方法得到一个Socket对象与远程客户端建立连接,同样调用Socket对象的getInputStream和getOutputStream方法就可以读写字节流,服务器端完成传输后可以通过close()方法关闭远程连接和监听端口:

// 监听端口
ServerSocket serverSocket = new ServerSocket(port);
// 建立远程连接
Socket socket = serverSocket.accept();
// 读写字节流
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
// 关闭连接
socket.close();
// 关闭监听端口
serverSocket.close();
  • TCP编程实验

我们可以在本机做一个小实验,首先编写一个客户端的TCPClient类,通过Java提供的InetAddress类的getLoopbackAddress()方法获得localhost地址,然后使用Java的Socket类创建一个与本机8090端口的连接,再将读取字节流包装成一个BufferedReader对象、写入字节流包装成BufferedWriter对象。使用BufferedWriter写入一个“time”字符串并发送到本机的8090端口,再用BufferedReader读取本机8090端口返回的数据并打印出来。代码如下:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets; public class TCPClient { public static void main(String[] args) throws IOException {
// 获取本机地址,即“127.0.0.1”
InetAddress addr = InetAddress.getLoopbackAddress();
// 与本机8090端口建立连接
try (Socket sock = new Socket(addr, 8090)) {
// 将读写字节流包装成BufferedReader和BufferedWriter对象
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8))) {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.UTF_8))) {
// 写入“time”字符串
writer.write("time\n");
// 将写入内存缓冲区的数据立即发送
writer.flush();
// 读取本机8090端口返回的数据
String resp = reader.readLine();
System.out.println("Response: " + resp);
}
}
}
}
}

在相同包下写一个服务端的TCPServer类,利用Java的ServerSocket类监听8090端口并打印一句话“TCP server ready.”,然后用ServerSocket类的accept()方法与监听到的访问8090端口的客户端请求建立连接,然后和客户端一样包装读写字节流。服务端首先读取数据,如果读取到的数据是一个"time"字符串,则将当前时间信息返回给客户端,如果不是则返回一个“require data”字符串给客户端,最后关闭连接和关闭监听接口。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime; public class TCPServer { public LocalDateTime currentTime() {
return LocalDateTime.now();
} public static void main(String[] args) throws Exception {
// 监听8090端口
ServerSocket ss = new ServerSocket(8090);
System.out.println("TCP server ready.");
// 建立连接
Socket sock = ss.accept();
// 包装读写字节流
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8))) {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.UTF_8))) {
// 读取发送到服务端的数据
String cmd = reader.readLine();
// 如果数据是“time”字符串则将当前时间信息返回客户端
if ("time".equals(cmd)) {
writer.write(LocalDateTime.now().toString() + "\n");
// 将写入内存缓冲区的数据立即发送
writer.flush();
} else {
writer.write("require data\n");
writer.flush();
}
}
}
// 关闭连接
sock.close();
// 关闭监听端口
ss.close();
}
}

我们首先运行服务端TCPServer类的main方法,开始监听8090端口,并且Console打印出“TCP server ready.”,然后运行客户端TCPClient的main方法,我们得到Response信息,终端打印出了当前的时间信息:

如果我们先运行客户端的main方法,我们会得到一个异常ConnectException: Connection refused,因为服务端并没有开始监听8090端口,无法与客户端建立socket连接。

  • TCP多线程

服务端的一个ServerSocket可以同时和多个客户端建立连接进行双向通信,实现起来也很简单,在设置好监听端口后,在一个无限for循环中调用ServerSocket的accept()方法,返回与客户端新建的连接,再启动线程或者使用线程池来处理客户端的请求,这样就可以同时处理多个客户端的连接,代码如下:

public class TCPServer {

    public static void main(String[] args) throws Exception {
@SuppressWarnings("resource")
ServerSocket ss = new ServerSocket(8090);
System.out.println("TCP server ready.");
for (;;) { // 无限for循环中返回客户端新建的连接
Socket sock = ss.accept();
// 设置线程要处理的任务
Runnable handler = new TimeHandler(sock);
// 使用Java提供的ExecutorService创建线程池
ExecutorService executor = Executors.newCachedThreadPool();
// 线程处理任务
executor.submit(handler);
// 任务处理完毕,关闭线程
executor.shutdown();
}
}
} class TimeHandler implements Runnable { Socket sock; TimeHandler(Socket sock) {
this.sock = sock;
} @Override
public void run() {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8))) {
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.UTF_8))) {
for (;;) {
String cmd = reader.readLine();
if ("q".equals(cmd)) {
writer.write("bye!\n");
writer.flush();
break;
} else if ("time".equals(cmd)) {
writer.write(LocalDateTime.now().toString() + "\n");
writer.flush();
} else {
writer.write("require data\n");
writer.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
this.sock.close(); } catch (IOException e) {
e.printStackTrace();
}
}
}
}

UDP编程

UDP协议不需要建立连接,可以直接发送和接收数据,UDP协议不保证可靠传输,使用Java提供的DatagramSocket的send()和receive()方法就可以发送和接收数据,UDP协议接收和发送数据没有IO流接口。

  • 客户端
public class UDPClient {

    public static void main(String[] args) throws Exception {
InetAddress addr = InetAddress.getLoopbackAddress();
try (DatagramSocket sock = new DatagramSocket()) { // 创建DatagramSocket对象
sock.connect(addr, 9090); // 设置要访问的服务端地址和端口
byte[] data = "time".getBytes(StandardCharsets.UTF_8);
DatagramPacket packet = new DatagramPacket(data, data.length); // 将字节流包装成DatagramPacket对象
sock.send(packet); // 发送数据
System.out.println("Data was sent.");
Thread.sleep(1000);
byte[] buffer = new byte[1024];
DatagramPacket resp = new DatagramPacket(buffer, buffer.length);
sock.receive(resp); // 接收数据
byte[] respData = resp.getData();
String respText = new String(respData, 0, resp.getLength(), StandardCharsets.UTF_8);
System.out.println("Response: " + respText);
}
}
}
  • 服务端
public class UDPServer {

    public LocalDateTime currentTime() {
return LocalDateTime.now();
} public static void main(String[] args) throws Exception {
@SuppressWarnings("resource")
DatagramSocket ds = new DatagramSocket(9090); // 设置要监听的端口
System.out.println("UDP server ready.");
for (;;) {
// 接收数据:
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
byte[] data = packet.getData();
String s = new String(data, StandardCharsets.UTF_8);
System.out.println("Packet received from: " + packet.getSocketAddress() + " " + s);
// 发送数据:
String resp = LocalDateTime.now().toString();
packet.setData(resp.getBytes(StandardCharsets.UTF_8));
ds.send(packet);
}
}
}

Email编程

电子邮件的一些基本概念:

  • MUA(Mail User Agent)发送和接收邮件所使用的邮件客户端,通常使用IMAP或POP3协议与服务器通信
  • MTA(Mail Transfer Agent) 通过SMTP协议发送、转发邮件
  • MDA(Mail Deliver Agent)将MTA接收到的邮件保存到磁盘或指定地方
  • SMTP协议(Simple Mail Transfer Protocol)是发送邮件使用的标准协议,建立在TCP协议之上,标准端口25,加密端口为465/587
  • POP3协议(Post Office Protocol version3) 是接收邮件使用的标准协议之一,建立在TCP协议之上,标准端口为110,加密端口为995
  • IMAP协议 (Internet Mail Access Protocol )是接收邮件使用的标准协议之一,和POP3协议的区别是IMAP协议允许用户创建文件夹,用户在本地的任何操作都自动同步到邮件服务器,准备端口为143,加密端口为993

Java提供了一个javax.mail包,可以很方便的实现发送和接收邮件,而不用去关系SMTP协议和POP3协议的原理,方法如下:

  • 发送邮件
    1. 首先创建一个Session对象,传入邮件服务器信息和用户名密码认证信息

      Session session = Session.getInstance(props, new Authticator());
    2. 然后创建MimeMessage对象,封装邮件发件人地址、收件人地址,邮件主题,邮件正文等信息

      MimeMessage message = new MimeMessage(session);
      message.setFrom(new InternetAddress("from@email.com"));
      message.setRecipient(Message.RecipientType.TO, new InternetAddress("to@email.com"));
      message.setSubject("subject", "UTF-8");
      message.setText("body", "UTF-8");
    3. 最后使用Transport工具类的send()方法发送邮件

      Transport.send(message);
  • 接收邮件
    1. 首先创建Session对象,同样需要传入服务器信息和登录名密码认证

      Session session = Session.getInstance(props, null);
    2. 然后创建Store对象,Store对象代表用户在服务器上的整个存储

      Store store = new POP3SSLStore(session, url)
    3. 通过Store对象获取Folder对象,获取用户相应的文件夹,比如收件箱“INBOX”

       Folder folder = store.getFolder("INBOX");
    4. 从Folder对象中获取所有的邮件

      Message[] messages = folder.getMessage();
      for (Message message : messages) { ... };

由于本文只是Java编程简明教程,对Email编程就不作过多的讲述,更多的功能可以参考JAVA MAIL相关API文档。

HTTP编程

HTTP协议(HyperText Transfer Protocol)又叫做超文本传输协议,它是基于TCP协议上的请求和响应协议,是目前使用最广泛的高级协议。最早的HTTP协议版本是HTTP 1.0,每一次请求,都会创建一个TCP连接,由于浏览器打开网页通常会请求不同的资源(例如图片,CSS等其他资源),创建TCP连接会有一定耗时,所以传输效率比较低;HTTP 1.1 则做出改进,多个HTTP请求可以通过一个TCP连接完成,效率得到提高;HTTP 2.0 同样也是多个请求通过一个TCP连接完成,但是浏览器发送一个请求后不需要等待服务器的响应就可以立刻发送后续的请求,服务器只要有了响应数据立刻返回,不关心请求的顺序,也就是说HTTP 2.0 不需要严格按照收到请求再响应的方式进行。

HTTP服务器用于处理HTTP请求,发送HTTP响应。在Java中,HTTP服务器通过JAVA EE的Servlet API定义,通常Servlet容器根据收到的HTTP请求信息创建一个Request对象,再创建一个Response对象用来向Web客户端发送响应,调用Servlet对象的service()方法处理Request和Response,具体参考Servlet教程。

HTTP客户端用于发送HTTP请求,接收HTTP响应。Java提供的java.net.HttpURLConnection类可以实现HTTP客户端功能:

  • 发送GET请求
URL url = new URL("http://www.example.com/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
int code = conn.getResponseCode(); //获得响应代码
try (InputStream input = conn.getInputStream()) {
// 读取响应
}
conn.disconnect(); // 断开连接
  • 发送POST请求
URL url = new URL("http://www.example.com/");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true); // 需要发送请求数据
byte[] postData = contentData.getBytes(StandardCharsets.UTF_8); // 将发送请求数据转换为数组
conn.setRequestProperty("Content-Type", contentType); // 设置请求Content-Type
conn.setRequestProperty("Content-Length", String.valueOf(postData.length)); 设置请求Content-Length
try (OutputStream output = conn.getOutputStream()) {
output.write(postData); // 写入请求数据
}
int code = conn.getResponseCode(); // 获取响应代码
try (InputStream input = conn.getInputStream()) {
// 读取响应数据
}
conn.disconnect(); // 断开连接

总结

Java提供了Socket和ServerSocket类,让我们可以实现TCP/UDP协议的连接;Java还提供了MAIL API,我们可以实现基于SMTP/POP3协议的收发邮件功能;最后Java还提供了HttpURLConnection类,用于实现HTTP客户端功能,以及提供了Servlet API用于实现HTTP服务端的编程。

Java网络编程简明教程的更多相关文章

  1. java网络编程小白教程

    1 网络编程 1.1 网络 把多台终端(计算机)通过物理线路连接起来,形成网络.便于交换数据.共享信息.组成更强大的逻辑体. 1.1.1 网络通信三要素 [1]IP地址:唯一标识网络上的每一台计算机 ...

  2. Java - 30 Java 网络编程

    Java 网络编程 网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java.net包中J2SE的API包含有类和接口,它们提供低层次的通信细节.你可以直接使用这些类和接 ...

  3. Java网络编程和NIO详解开篇:Java网络编程基础

    Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...

  4. 实验五 Java网络编程

    实验五 Java网络编程 实验五 Java网络编程 实验五所涉及的密码学算法及编程思路 ## Java对称加密-DES算法 (1) 获取密钥生成器 KeyGenerator kg=KeyGenerat ...

  5. Java-Runoob-高级编程:Java 网络编程

    ylbtech-Java-Runoob-高级编程:Java 网络编程 1.返回顶部 1. Java 网络编程 网络编程是指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来. java.n ...

  6. Vbs 脚本编程简明教程之一

    —为什么要使用 Vbs ? 在 Windows 中,学习计算机操作也许很简单,但是很多计算机工作是重复性劳动,例如你每周也许需要对一些计算机文件进行复制.粘贴.改名.删除,也许你每天启动 计算机第一件 ...

  7. 从零探索Java网络编程01之 TCP/IP 与 Socket

    最近完成了几项比较简单的项目, 终于是在996里偷了点闲暇时光, 想着来研究研究些啥吧?  一个普通的控制台日志映入了我的眼帘(孽缘呀): (图中使用 SpringBoot 的 log4j 来输出日志 ...

  8. Java网络编程——Socket

    网络是连接不同计算机的媒介,不同的计算机依靠网络来互相通信,即传递数据. Java中与网络编程相关的部分主要是Socket(套接字),它作为一种抽象的结构,实现了与通信相关的各类方法,构成一套完整的通 ...

  9. 20145205 《Java程序设计》实验报告五:Java网络编程及安全

    20145205 <Java程序设计>实验报告五:Java网络编程及安全 实验要求 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.客户端中输入明文,利用DES算法加密,D ...

随机推荐

  1. spark数据倾斜处理

    spark数据倾斜处理 危害: 当出现数据倾斜时,小量任务耗时远高于其它任务,从而使得整体耗时过大,未能充分发挥分布式系统的并行计算优势. 当发生数据倾斜时,部分任务处理的数据量过大,可能造成内存不足 ...

  2. hdfs冷热数据分层存储

    hdfs如何让某些数据查询快,某些数据查询慢? hdfs冷热数据分层存储 本质: 不同路径制定不同的存储策略. hdfs存储策略 hdfs的存储策略 依赖于底层的存储介质. hdfs支持的存储介质: ...

  3. UVALive 6862——结论题&&水题

    题目 链接 题意:求满足$0 \leq x \leq y \leq z \leq m$且$x^j + y^j = z^j, \ j=2 \cdots n$的三元组的对数 分析 由费马大定理:整数$n ...

  4. nginx反向代理tomcat 时,出现https redirect后变成http的问题解决方法

    需要修改两个配置 1.nginx配置 location / { proxy_pass http://test-server; proxy_set_header Host $host; proxy_se ...

  5. PHP mysqli_multi_query() 函数

    实例 执行多个针对数据库的查询: <?php $con=mysqli_connect("localhost","my_user","my_pas ...

  6. [Loj] 数列分块入门 1 - 9

    数列分块入门 1 https://loj.ac/problem/6277 区间加 + 单点查询 #include <iostream> #include <cstdio> #i ...

  7. 北京清北 综合强化班 Day2

    a [问题描述] 你是能看到第一题的 friends呢.                                                —— hja世界上没有什么比卖的这 贵弹丸三还令 ...

  8. Postman集合/文件夹/请求中脚本的执行顺序

    Postman的Collection(集合)/Folder(集合的子文件夹)/Request(请求)中都有Pre-request Script(请求前脚本)和Tests(请求后脚本) 这个功能类似于不 ...

  9. redis,memcached,mongodb之间的区别

    Redis Redis的优点: 支持多种数据结构,如 string(字符串). list(双向链表).dict(hash表).set(集合).zset(排序set).hyperloglog(基数估算) ...

  10. Educational Codeforces Round 64 (Rated for Div. 2) A,B,C,D,E,F

    比赛链接: https://codeforces.com/contest/1156 A. Inscribed Figures 题意: 给出$n(2\leq n\leq 100)$个数,只含有1,2,3 ...