目录
一、前言
网络编程的历史可以追溯到20世纪60年代,当时美国国防部高级研究计划局(ARPA)开发了ARPANET,这是互联网的前身。1983年,TCP/IP协议成为ARPANET的标准协议,奠定了现代互联网的基础
Java的网络编程能力让开发者能够轻松构建客户端和服务器端应用程序,这也是Java在企业级应用中广泛使用的重要原因之一
网络编程作用就是:
通过编程的方式,使得计算机网络中不同计算机上的应用程序间能够进行数据的传输
1、计算机网络
最简单的计算机网络:
两台计算机,连接它们的一条链路
所以计算机网络其实就是
利用通信线路将分散在各个不同地方的、具有独立计算功能的计算机系统和通信设备
按照不同的形式连接起来
以功能完善的网络软件及协议实现资源共享和信息传递的系统
2、软件结构
常见的主要有 C/S 和 B/S
Client / Server :
客户端 / 服务器 的软件结构
eg:QQ、微信、等各种软件,只要是我们下载安装的并且和服务器通信的
都属于 C/S 软件结构
Browser / Server :
浏览器 / 服务器 的软件结构
eg:淘宝网、凤凰网、4399等,只要是使用浏览器并且和服务器通信的
都属于 B/S 软件结构
C/S 和 B/S 对比:
1、CS图形表现及运行速度 > BS
2、CS需要客户端,不能跨平台,不同的操作系统安装包不一样
BS不需要客服端,只要浏览器即可,BS基于网页语言,与操作系统无关,可以跨平台
3、通信三要素
- IP地址:
域名:本质上也是ip,只是为了让ip更好记忆
就是那些 www.xxx.com 网址
- 端口:
端口号可以用来标识计算机中唯一的那个应用程序
一台计算机中的应用的端口号不能重复
常见端口号:
- 80端口:前端的静态服务器
- 网页服务一般都是80端口
- 所以80可以省略
- BS架构的默认端口号是80,可以省略
- HTTP占用80
- FTP占用21 文件传输服务
- MySQL用3306
- oracle启动后默认占用端口号1521
- redis启动后默认占用端口号6379
- tomcat启动后默认占用端口号8080
- 协议:
计算机与计算机通过网络进行数据和信息交换的时候
也要使用同样的“语言”,这个语言被称为网络通讯协议。
通信双方必须同时遵守才能完成数据交换,常见的协议有UDP协议和TCP协议
接下来进入本篇文章重点:
二、TCP和UDP简单介绍
完整的通信过程比较复杂
不过JavaAPI帮我们进行了细节的封装
给我们提供了类和接口直接使用
十分便利
java.net包 对这两种常见的通信协议进行了封装
UDP(不常用)
传输层协议
无连接通信协议在数据传输时,数据的发送端和接收端不建立连接,也不能保证对方能成功接收
优点:
通信效率高
缺点:
不能保证数据传输的成功率和完整性
TCP(重点)
传输层协议面向连接
优点:可靠的,无差错的
缺点:效率低针对客户端和服务器,都进行了抽象:
客户端:java.net.Socket
服务器:java.net.ServerSocket
TCP的三次握手四次挥手
三次握手(建立连接):
- 客户端向服务端发送连接请求
- 服务端收到请求后向客户端发送确认
- 客户端收到服务端发来的确认信息后,向服务端发送确认收到的信息
四次挥手(断开连接):
- 客户端向服务端发送断开请求
- 服务端收到断开请求后,向客户端发送确认信息
- 服务端处理完剩余数据后,向客户端发送断开请求
- 客户端收到服务端的断开请求后,向服务端发送确认断开信息,然后客户端进入TIME_WAIT状态开始等待,等待2MSL时间后确保连接断开(服务端无需等待直接关闭)
三、TCP网络编程
- java.net.ServerSocket 类表示服务端
- java.net.Socket 类表示客户端
1、通信流程
服务器端:
- 创建ServerSocket(需要绑定端口号,方便客户连接)
- 调用ServerSocket对象的 accept() 方法接收一个客户端请求,得到一个Socket
- 调用Socket的 getInputStream() 和 getOutputStream() 方法获取和客户端相连的IO流
输入流读取客户端发来的数据
输出流发送数据到客户端
- 操作完成,关闭资源
客户端:
- 创建Socket连接服务端(需指定服务器ip地址、端口),找对应的服务器进 行连接
- 调用Socket的 getInputStream() 和 getOutputStream() 方法获取和服务端相连的 IO流
输入流可以读取服务端输出流写出的数据
输出流可以写出数据到服务端的输入流
- 操作完成,关闭资源
整个过程中
服务器不能主动连接客户端
必须等客户端先发起连接
2、构造方法
Socket socket2 = new Socket(InetAddress.getByName("www.baidu.com"), 80);
Socket socket = new Socket("www.baidu.com", 80);
3、基础案例
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TestSocket {
public static void main(String[] args) throws IOException {
String ip = "127.0.0.1";
int port = 8888;
Socket socket = new Socket(ip, port);
System.out.println("成功连接:" + socket);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
//传输数据给服务器
os.write("hello server,我是客户端".getBytes());
// 刷新缓冲区
os.flush();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println("服务器说:" + new String(bytes, 0, len));
os.close();
is.close();
socket.close();
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TestServerSocket {
public static void main(String[] args) throws IOException {
int port = 8888;
ServerSocket server = new ServerSocket(port);
System.out.println("服务器启动成功,等待客户端连接,端口:" + port);
System.out.println("server:" + server);
// 等待客户端连接,这里终端会阻塞
Socket socket = server.accept();
System.out.println("socket:" + socket);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println("客户端说:" + new String(bytes, 0, len));
os.write("hello client,我是服务器".getBytes());
os.flush();
os.close();
is.close();
socket.close();
server.close();
}
}
4、多线程反转案例
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServerSocket {
public static void main(String[] args) {
int port = 8989;
try {
ServerSocket server = new ServerSocket(port);
while(true){
Socket socket = server.accept();
Thread t = new Thread(new Runnable() {
public void run() {
try {
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1){
String msg = new String(buffer, 0, len);
System.out.println("客户端说:" + msg);
if(msg.equals("quit")){
break;
}
// 反转字符串
String reverse = new StringBuffer(msg).reverse().toString();
os.write(reverse.getBytes());
os.flush();
}
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
});
t.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class MySocket {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 8989);
Scanner sc = new Scanner(System.in);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
System.out.println("请输入聊天信息,输入quit退出:");
while (true) {
String str = sc.nextLine();
if(str.equals("quit")){
os.write(str.getBytes());
os.flush();
break;
}
os.write(str.getBytes());
os.flush();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
System.out.println("反转后的信息:" + new String(bytes, 0, len));
}
is.close();
os.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
5、传输对象
基础类:
import java.io.Serializable; public class Teacher implements Serializable{ private String name; private int age; private double salary; public Teacher(String name, int age, double salary) { this.name = name; this.age = age; this.salary = salary; } public String getName() { return name; } public int getAge() { return age; } public double getSalary() { return salary; } @Override public String toString() { return "Teacher [name=" + name + ", age=" + age + ", salary=" + salary + "]"; } }
客户端:
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; import java.util.Scanner; public class MySocket { public static void main(String[] args) { try { Socket socket = new Socket("127.0.0.1", 8002); Scanner sc = new Scanner(System.in); ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream()); ObjectInputStream is = new ObjectInputStream(socket.getInputStream()); System.out.println("请输入老师信息(格式:姓名-年龄-工资),输入quit结束:"); while(true){ String msg = sc.next(); if(msg.equals("quit")){ os.writeObject(null); os.flush(); break; } String[] parts = msg.split("-"); if(parts.length == 3){ Teacher t = new Teacher(parts[0],Integer.parseInt(parts[1]), Double.parseDouble(parts[2])); os.writeObject(t); os.flush(); }else{ System.out.println("输入格式错误"); } } String response = (String) is.readObject(); System.out.println("服务端回复:" + response); os.close(); is.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } }
服务端:
import java.io.FileWriter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Writer; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; public class MyServerSocket { public static void main(String[] args) { int port = 8002; try { ServerSocket server = new ServerSocket(port); Socket socket = server.accept(); ObjectInputStream is = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream()); List<Teacher> teachers = new ArrayList<>(); Teacher t; while((t = (Teacher)is.readObject()) != null){ teachers.add(t); } Writer writer = new FileWriter("day30/homework/T1/teacher.txt"); for(Teacher teacher : teachers){ writer.write(teacher.toString()); writer.write("\n"); } os.writeObject("ok"); writer.flush(); writer.close(); os.close(); is.close(); socket.close(); server.close(); } catch (Exception e) { e.printStackTrace(); } } }
四、UDP网络编程
java.net.DatagramSocket 和 java.net.DatagramPacket
是UDP编程中使用到的两个类,客户端和服务器端都使用这两个类
- java.net.DatagramSocket 负责接收和发送数据
- java.net.DatagramPacket 负责封装要发送的数据和接收到的数据
简单写一下了解即可:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class TestSocket {
public static void main(String[] args) {
String ip = "127.0.0.1";
int port = 8888;
DatagramSocket socket = null;
DatagramPacket packet = null;
try {
socket = new DatagramSocket();
byte[] buff = "hello server".getBytes();
packet = new DatagramPacket(buff,0,buff.length,InetAddress.getByName(ip),port);
socket.send(packet);
System.out.println("客户端发送成功");
} catch (Exception e) {
e.printStackTrace();
}finally{
if(socket != null){
socket.close();
}
}
}
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class TestServerSocket {
public static void main(String[] args) {
byte[] buff = new byte[1024];
int port = 8888;
DatagramSocket socket = null;
DatagramPacket packet = null;
try {
socket = new DatagramSocket(port);
packet = new DatagramPacket(buff, 0 , buff.length);
socket.receive(packet);
System.out.println("客户端说:" + new String(buff, 0, packet.getLength()));
} catch (Exception e) {
e.printStackTrace();
}finally{
if(socket != null){
socket.close();
}
}
}
}