day26(网络编程)
网络编程
网络编程概述(了解)
-
- A:计算机网络 - 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。 - B:网络编程 - 就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换
网络编程三要素之IP概述(掌握)
-
- 每个设备在网络中的唯一标识 - 每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。 - ipconfig:查看本机IP192.168.12.42 - ping:测试连接192.168.40.62 - 本地回路地址:127.0.0.1 255.255.255.255是广播地址 - IPv4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。 - IPv6:8组,每组4个16进制数。 - 1a2b:0000:aaaa:0000:0000:0000:aabb:1f2f - 1a2b::aaaa:0000:0000:0000:aabb:1f2f - 1a2b:0000:aaaa::aabb:1f2f - 1a2b:0000:aaaa::0000:aabb:1f2f - 1a2b:0000:aaaa:0000::aabb:1f2f
网络编程三要素之端口号概述(掌握)
-
- 每个程序在设备上的唯一标识 - 每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序。 - 端口号范围从0-65535 - 编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。 - 常用端口 - mysql: 3306 - oracle: 1521 - web: 80 - tomcat: 8080 - QQ: 4000
网络编程三要素协议(掌握)
-
- 为计算机网络中进行数据交换而建立的规则、标准或约定的集合。 - UDP - 面向无连接,数据不安全,速度快。不区分客户端与服务端。 - TCP - 面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。 - 三次握手: 客户端先向服务端发起请求, 服务端响应请求, 传输数据
Socket通信原理图解(了解)
-
-
A:Socket套接字概述: - 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。 - 通信的两端都有Socket。 - 网络通信其实就是Socket间的通信。 - 数据在两个Socket间通过IO流传输。 - Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port。
-
UDP传输(了解)
-
- 1.发送Send - 创建DatagramSocket, 随机端口号 - 创建DatagramPacket, 指定数据, 长度, 地址, 端口 - 使用DatagramSocket发送DatagramPacket - 关闭DatagramSocket - 2.接收Receive - 创建DatagramSocket, 指定端口号 - 创建DatagramPacket, 指定数组, 长度 - 使用DatagramSocket接收DatagramPacket - 关闭DatagramSocket - 从DatagramPacket中获取数据 - 3.接收方获取ip和端口号 - String ip = packet.getAddress().getHostAddress(); - int port = packet.getPort();
UDP传输优化
-
- 接收端Receive - DatagramSocket socket = new DatagramSocket(6666); //创建socket相当于创建码头 DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); //创建packet相当于创建装箱
while(true) { socket.receive(packet); //接收货物 byte[] arr = packet.getData(); int len = packet.getLength(); String ip = packet.getAddress().getHostAddress(); System.out.println(ip + ":" + new String(arr,0,len)); }
-
发送端Send DatagramSocket socket = new DatagramSocket(); //创建socket相当于创建码头 Scanner sc = new Scanner(System.in);
while(true) { String str = sc.nextLine(); if("quit".equals(str)) break; DatagramPacket packet = //创建packet相当于创建集装箱 new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666); socket.send(packet); //发货 } socket.close();
UDP传输多线程
-
A发送和接收在一个窗口完成
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Scanner; public class Demo3_MoreThread { /** * @param args */ public static void main(String[] args) { new Receive().start(); new Send().start(); } } class Receive extends Thread { public void run() { try { DatagramSocket socket = new DatagramSocket(6666); //创建Socket相当于创建码头 DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); //创建Packet相当于创建集装箱 while(true) { socket.receive(packet); //接货,接收数据 byte[] arr = packet.getData(); //获取数据 int len = packet.getLength(); //获取有效的字节个数 String ip = packet.getAddress().getHostAddress(); //获取ip地址 int port = packet.getPort(); //获取端口号 System.out.println(ip + ":" + port + ":" + new String(arr,0,len)); } } catch (IOException e) { e.printStackTrace(); } } } class Send extends Thread { public void run() { try { Scanner sc = new Scanner(System.in); //创建键盘录入对象 DatagramSocket socket = new DatagramSocket(); //创建Socket相当于创建码头 while(true) { String line = sc.nextLine(); //获取键盘录入的字符串 if("quit".equals(line)) { break; } DatagramPacket packet = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);//创建Packet相当于集装箱 socket.send(packet); //发货,将数据发出去 } socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
UDP聊天图形化界面
package com.ning.gui;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo2_GUIChat extends Frame {
private TextField tf;
private Button send;
private Button log;
private Button cls;
private Button shake;
private TextArea viewText;
private TextArea sendText;
private DatagramSocket socket;
private BufferedWriter bw;
public Demo2_GUIChat() {
try {
init();
southPanel();
centerPanel();
event();
} catch (Exception e) {
e.printStackTrace();
}
}
// 设置窗口关闭
private void event() {
// 1.窗口关闭
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
socket.close();
try {
bw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}
});
// 2.send按钮
send.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
send();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
// 记录按钮
log.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
logFile();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
// 清屏按钮
cls.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
viewText.setText("");
}
});
// 震动按钮
shake.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
send(new byte[] { -1 }, tf.getText());
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
// 快捷键发送
sendText.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if ((e.getKeyCode() == KeyEvent.VK_ENTER)
|| (e.getKeyCode() == KeyEvent.VK_ENTER && e.isControlDown())) {
// isControlDown 指的是ctrl是否被按下
try {
send();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
});
}
// 记录方法
private void logFile() throws IOException {
bw.flush(); // 刷新缓冲区
FileInputStream fis = new FileInputStream("config.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 在内存中创建缓冲区
int len;
byte[] arr = new byte[8192];
while ((len = fis.read(arr)) != -1) {
baos.write(arr, 0, len);
}
String str = baos.toString();
viewText.setText(str);
}
// 震动方法
private void shake() throws Exception {
int x = this.getLocation().x;
int y = this.getLocation().y;
for (int i = 0; i < 10; i++) {
this.setLocation(x + 20, y + 20);
Thread.sleep(20);
this.setLocation(x + 20, y + 20);
Thread.sleep(20);
this.setLocation(x - 20, y - 20);
Thread.sleep(20);
this.setLocation(x, y);
}
}
// 发送方法
private void send(byte[] arr, String ip) throws IOException {
DatagramPacket packet = new DatagramPacket(arr, arr.length, InetAddress.getByName(ip), 8888);
socket.send(packet);
}
private void send() throws Exception {
String message = sendText.getText();
String ip = tf.getText();
ip = ip.trim().length() == 0 ? "255.255.255.255" : ip;
send(message.getBytes(), ip);
String time = getCurrentTime();
String str = time + "我对" + (ip.equals("255.255.255.255") ? "所有人" : ip) + "说:\r\n" + message + "\r\n\r\n";
viewText.append(str);// Alt+Shift+L,抽取出来一个局部变量
bw.write(str);// 将信息写到数据库中
sendText.setText("");
}
// 获取时间
private String getCurrentTime() {
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH :mm:ss");
return sdf.format(d);
}
// 设置中间panel
private void centerPanel() {
Panel center = new Panel(); // 创建中间的panel
viewText = new TextArea();
sendText = new TextArea(5, 1);
center.setLayout(new BorderLayout()); // 设置为边界布局管理器
center.add(sendText, BorderLayout.SOUTH); // 发送的文本区域放置在南边
center.add(viewText, BorderLayout.CENTER); // 显示的文本区域放在中间
viewText.setEditable(false); // 设置可编辑性
viewText.setBackground(Color.white); // 设置背景颜色
sendText.setFont(new Font("xxx", Font.PLAIN, 10));// 设置发送区域字体
viewText.setFont(new Font("xxx", Font.PLAIN, 10));// 设置显示区域字体
this.add(center, BorderLayout.CENTER);
}
// 设置南边panel
private void southPanel() {
Panel south = new Panel(); // 创建南边的panel
tf = new TextField(20);
tf.setText("192.168.1.2");
send = new Button("发送");
log = new Button("记录");
cls = new Button("清屏");
shake = new Button("震动");
south.add(tf);
south.add(send);
south.add(log);
south.add(cls);
south.add(shake);
this.add(south, BorderLayout.SOUTH); // 将panel放在Frame的南边
}
// 初始化
private void init() throws Exception {
this.setLocation(300, 20);
this.setSize(400, 600);
new Receive().start();
socket = new DatagramSocket();
bw = new BufferedWriter(new FileWriter("config.txt", true));
this.setVisible(true);
}
// 接收数据
private class Receive extends Thread { // 接收和发送要同时进行,所以定义成多线程的
public void run() {
DatagramSocket socket;
try {
socket = new DatagramSocket(8888);
DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
while (true) {
socket.receive(packet); // 接收信息
byte[] arr = packet.getData(); // 获取字节数据
int len = packet.getLength(); // 获取有效的字节数据
if (arr[0] == -1 && len == 1) { // 如果发过来的数据第一个存储值是-1,并且数组长度是1
shake();
continue; // 终止本次循环,继续下次循环
}
String message = new String(arr, 0, len);// 转换成字符串
String time = getCurrentTime();
String ip = packet.getAddress().getHostAddress();
String str = time + " " + ip + "对我说:\r\n" + message + "\r\n\r\n";
viewText.append(str);
bw.write(str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 主方法
public static void main(String[] args) {
new Demo2_GUIChat();
}
}
UDP聊天发送功能
UDP聊天记录功能
UDP聊天清屏功能
UDP聊天震动功能
UDP聊天快捷键和代码优化
UDP聊天生成jar文件
网络编程(TCP协议)(掌握)
-
- 1.客户端 - 创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器 - 调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流 - 输入流可以读取服务端输出流写出的数据 - 输出流可以写出数据到服务端的输入流 - 2.服务端 - 创建ServerSocket(需要指定端口号) - 调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket - 调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流 - 输入流可以读取客户端输出流写出的数据 - 输出流可以写出数据到客户端的输入流
TCP协议代码优化
-
客户端
Socket socket = new Socket("127.0.0.1", 9999); //创建Socket指定ip地址和端口号 InputStream is = socket.getInputStream(); //获取输入流 OutputStream os = socket.getOutputStream(); //获取输出流 BufferedReader br = new BufferedReader(new InputStreamReader(is)); PrintStream ps = new PrintStream(os);
System.out.println(br.readLine()); ps.println("好好学习"); System.out.println(br.readLine()); ps.println("老子不学了"); socket.close();
-
服务端
ServerSocket server = new ServerSocket(9999); //创建服务器 Socket socket = server.accept(); //接受客户端的请求 InputStream is = socket.getInputStream(); //获取输入流 OutputStream os = socket.getOutputStream(); //获取输出流
BufferedReader br = new BufferedReader(new InputStreamReader(is)); PrintStream ps = new PrintStream(os); ps.println("欢迎咨询传智播客"); System.out.println(br.readLine()); ps.println("报满了,请报下一期吧"); System.out.println(br.readLine()); server.close(); socket.close();
服务端是多线程的(掌握)
ServerSocket server = new ServerSocket(9999); //创建服务器
while(true) {
final Socket socket = server.accept(); //接受客户端的请求
new Thread() {
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("欢迎咨询传智播客");
System.out.println(br.readLine());
ps.println("报满了,请报下一期吧");
System.out.println(br.readLine());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
练习
-
客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串
-
客户端
-
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.net.UnknownHostException; import java.util.Scanner; public class Test1_Client { /** * 客户端向服务器写字符串(键盘录入),服务器(多线程)将字符串反转后写回,客户端再次读取到是反转后的字符串 * @throws IOException * @throws UnknownHostException */ public static void main(String[] args) throws UnknownHostException, IOException { Scanner sc = new Scanner(System.in); //创建键盘录入对象 Socket socket = new Socket("127.0.0.1", 54321); //创建客户端,指定ip地址和端口号 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取输入流 PrintStream ps = new PrintStream(socket.getOutputStream());//获取输出流 ps.println(sc.nextLine()); //将字符串写到服务器去 System.out.println(br.readLine()); //将反转后的结果读出来 socket.close(); } }
-
服务器
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class Test1_Server { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(54321); System.out.println("服务器启动,绑定54321端口"); while(true) { final Socket socket = server.accept(); //接受客户端的请求 new Thread() { //开启一条线程 public void run() { try { BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //获取输入流 PrintStream ps = new PrintStream(socket.getOutputStream());//获取输出流 String line = br.readLine(); //将客户端写过来的数据读取出来 line = new StringBuilder(line).reverse().toString(); //链式编程 ps.println(line); //反转后写回去 socket.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } } }
练习
- 客户端向服务器上传文件
客户端
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class Test2_UpdateClient {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException, IOException {
// 1.提示输入要上传的文件路径, 验证路径是否存在以及是否是文件夹
File file = getFile();
// 2.发送文件名到服务端
Socket socket = new Socket("127.0.0.1", 12345);
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println(file.getName());
// 6.接收结果, 如果存在给予提示, 程序直接退出
String result = br.readLine(); //读取存在后不存在的结果
if("存在".equals(result)) {
System.out.println("您上传的文件已经存在,请不要重复上传");
socket.close();
return;
}
// 7.如果不存在, 定义FileInputStream读取文件, 写出到网络
FileInputStream fis = new FileInputStream(file);
byte[] arr = new byte[8192];
int len;
while((len = fis.read(arr)) != -1) {
ps.write(arr, 0, len);
}
fis.close();
socket.close();
}
private static File getFile() {
Scanner sc = new Scanner(System.in); //创建键盘录入对象
System.out.println("请输入一个文件路径:");
while(true) {
String line = sc.nextLine();
File file = new File(line);
if(!file.exists()) {
System.out.println("您录入的文件路径不存在,请重新录入:");
}else if(file.isDirectory()) {
System.out.println("您录入的是文件夹路径,请输入一个文件路径:");
}else {
return file;
}
}
}
}
服务器
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Test2_UpdateServer {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
//3,建立多线程的服务器
ServerSocket server = new ServerSocket(12345);
System.out.println("服务器启动,绑定12345端口号");
//4.读取文件名
while(true) {
final Socket socket = server.accept(); //接受请求
new Thread() {
public void run() {
try {
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
PrintStream ps = new PrintStream(socket.getOutputStream());
String fileName = br.readLine();
//5.判断文件是否存在, 将结果发回客户端
File dir = new File("update");
dir.mkdir(); //创建文件夹
File file = new File(dir,fileName); //封装成File对象
if(file.exists()) { //如果服务器已经存在这个文件
ps.println("存在"); //将存在写给客户端
socket.close(); //关闭socket
return;
}else {
ps.println("不存在");
}
//8.定义FileOutputStream, 从网络读取数据, 存储到本地
FileOutputStream fos = new FileOutputStream(file);
byte[] arr = new byte[8192];
int len;
while((len = is.read(arr)) != -1) {
fos.write(arr, 0, len);
}
fos.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}