Java:TCP/UDP网络编程
目录
一、前言
1、计算机网络
2、软件结构
3、通信三要素
二、TCP和UDP简单介绍
TCP的三次握手四次挥手
三、TCP网络编程
1、通信流程
2、构造方法
3、基础案例
4、多线程反转案例
5、传输对象
四、UDP网络编程
一、前言
网络编程的历史可以追溯到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;}@Overridepublic 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();}}}
}