计算机网络是两台或更多的计算机组成的网络,同一网络内的任意两台计算机能够直接通讯,全部计算机必须遵循同一种网络协议。html
IP地址用于惟一标识一个网络接口java
IPv4采用32位地址
IPv4地址实际是一个二进制32位的整数,为了便于识别,用十六进制表示后能够分为4组数字,每组数字转换成十进制后用“.”隔开就是咱们见到的IP地址:
编程
IPv6采用128位地址数组
公网IP地址能够直接被访问浏览器
内网IP地址只能在内网访问服务器
本机地址使用127.0.0.1网络
一般路由器或交换机有两个网卡(两个IP地址),分别链接两个不一样的网络:
session
同一网络下的计算机能够直接通讯,他们的网络号相同,网络号由IP地址和子掩码按组对齐作与运算获得:
多线程
不一样网络下的计算机须要经过路由器或交换机网络设备间接通讯,这样的网络设备叫作网关:
并发
网关的做用是链接多个网络,负责把一个网络的数据包发送到另外一个网络,过程叫作路由:
一台计算机的网络拥有IP地址,子网掩码和网关(路由器)三个关键配置:
因为IP地址不便于记忆,一般使用域名来访问特定的服务,域名解析服务器DNS负责将域名翻译成对应的IP地址,客户端再根据IP地址访问服务器:
UDP(User Datagram Protocol)协议是数据报文协议,不面向链接,不保证可靠传输,因为UDP协议传输效率高,一般用来传输视频等能容忍丢失部分数据的文件。
Socket一般称为套接字,用于应用程序之间创建远程链接,Socket内部经过TCP/IP协议进行数据传输,能够简单的理解为对IP地址和端口号的描述。Socket接口是由计算机操做系统提供的,编程语言提供对Socket接口调用的封装。一般计算机同时运行多个应用程序,仅仅有IP地址是没法肯定由哪一个应用程序接收数据,因此操做系统抽象出Socket接口,每一个应用程序对应不一样的socket(每一个网络应用程序分配不一样的端口号)。端口号的范围是0~65535,小于1024的端口须要管理员权限,大于1024的端口能够任意用户的应用程序打开。
Socket编程须要实现服务器端和客户端,由于这两个设备通信时,须要知道对方的IP和端口号。一般服务器端有个固定的端口号,客户端直接经过服务器的IP地址和端口号进行访问服务器端,同时告知客户端的端口号,因而他们之间就能够经过socket进行通讯。
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();
服务器端经过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();
咱们能够在本机作一个小实验,首先编写一个客户端的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链接。
服务端的一个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协议不保证可靠传输,使用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); } } }
电子邮件的一些基本概念:
Java提供了一个javax.mail包,能够很方便的实现发送和接收邮件,而不用去关系SMTP协议和POP3协议的原理,方法以下:
首先建立一个Session对象,传入邮件服务器信息和用户名密码认证信息
Session session = Session.getInstance(props, new Authticator());
而后建立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");
最后使用Transport工具类的send()方法发送邮件
Transport.send(message);
首先建立Session对象,一样须要传入服务器信息和登陆名密码认证
Session session = Session.getInstance(props, null);
而后建立Store对象,Store对象表明用户在服务器上的整个存储
Store store = new POP3SSLStore(session, url)
经过Store对象获取Folder对象,获取用户相应的文件夹,好比收件箱“INBOX”
Folder folder = store.getFolder("INBOX");
从Folder对象中获取全部的邮件
Message[] messages = folder.getMessage();
for (Message message : messages) { ... };
因为本文只是Java编程简明教程,对Email编程就不做过多的讲述,更多的功能能够参考JAVA MAIL相关API文档。
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客户端功能:
URL url = new URL("http://www.example.com/"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); int code = conn.getResponseCode(); //得到响应代码 try (InputStream input = conn.getInputStream()) { // 读取响应 } conn.disconnect(); // 断开链接
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服务端的编程。