网络编程三要素之IP概述:
每个设备在网络中的唯一标识
每台网络终端在网络中都要一个独立的地址,我们在网络中传输数据就是使用这个地址
ipconfig:查看本机IP
ping:测试连接
本地回路地址: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
feiQ:2425
网络编程三要素协议:
为计算机网络中进行数据交换而建立的规则、标准或约定的集合
UDP
面向无连接,数据不安全,速度快,不区分客户端与服务端
TCP
面向连接(三次握手),数据安全,速度略低。分为客户端和服务端
三次握手:客户端先想服务端发起请求,服务端响应请求,传输数据
UDP传输优化:
接收端Receive
import java.net.DatagramPacket; import java.net.DatagramSocket; public class Demo1_Receive { public static void main(String[] args) throws Exception { 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 + ":" + new String(arr,0,len)); } } }
发送端Send
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; public class Demo1_Send { public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); //创建键盘录入对象 DatagramSocket socket = new DatagramSocket(); //创建socket相当于创建码头 while(true){ String line = sc.nextLine(); //获取键盘录入的字符串 if ("quit".equals(line)) { break; } DatagramPacket packet = //创建packet相当于集装箱 new DatagramPacket(line.getBytes(), line.getBytes().length,InetAddress.getByName("127.0.0.1"),6666); socket.send(packet); } socket.close(); } }
UDP传输多线程:
发送和接收在一个窗口完成
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 Demo2 { public static void main(String[] args) { new Receive().start(); new Send().start(); } } class Receive extends Thread{ @Override 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 + ":" + new String(arr,0,len)); } } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } class Send extends Thread{ @Override 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 = //创建packet相当于集装箱 new DatagramPacket(line.getBytes(), line.getBytes().length,InetAddress.getByName("127.0.0.1"),6666); socket.send(packet); } socket.close(); } catch (SocketException e) { e.printStackTrace(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
UDP聊天图形化界面:
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.WindowAdapter; import java.awt.event.WindowEvent; public class Demo3_GUIChat extends Frame { public Demo3_GUIChat(){ init(); southPanel(); centerPanel(); event(); } private void event() { this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); } private void centerPanel() { Panel center = new Panel(); //创建中间的Panel TextArea viewText = new TextArea(); //显示的文本区域 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("hhh",Font.PLAIN ,16 )); viewText.setFont(new Font("hhh",Font.PLAIN ,16 )); this.add(center,BorderLayout.CENTER); } public void southPanel() { Panel south = new Panel(); //创建南边的Panel TextField tf = new TextField(15); //创建文本字段存储IP地址 Button send = new Button("发送"); //创建发送按钮 Button log = new Button("记录"); //创建记录按钮 Button clear = new Button("清屏"); //创建清屏按钮 Button shake = new Button("震动"); //创建震动按钮 south.add(tf); south.add(send); south.add(log); south.add(clear); south.add(shake); this.add(south,BorderLayout.SOUTH); //将Panel放在Frame的南边 } public void init() { this.setLocation(500,50); this.setSize(400, 600); this.setVisible(true); } public static void main(String[] args) { new Demo3_GUIChat(); } }
UDP聊天发送功能
UDP聊天记录功能
UDP聊天清屏功能
UDP聊天振动功能
UDP聊天快捷键和代码优化
UDP聊天生成jar文件
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.net.SocketException; import java.net.UnknownHostException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo3_GUIChat extends Frame { private TextField tf; private Button send; private Button log; private Button clear; private Button shake; private TextArea viewText; private TextArea sendText; private DatagramSocket socket; private BufferedWriter bw; public Demo3_GUIChat(){ init(); southPanel(); centerPanel(); event(); } private void event() { this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { try { socket.close(); bw.close(); } catch (IOException e1) { e1.printStackTrace(); } System.exit(0); } }); send.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { send(); } catch (IOException e1) { e1.printStackTrace(); } } }); log.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { try { logFile(); } catch (IOException e) { e.printStackTrace(); } } }); clear.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.isControlDown()) { //isControlDown Ctrl键是否被按下 if (e.getKeyCode() == KeyEvent.VK_ENTER) { try { send(); } catch (IOException e1) { e1.printStackTrace(); } } } }); } private void shake() { int x = this.getLocation().x; //获取横坐标位置 int y = this.getLocation().y; //获取纵坐标位置 for(int i = 0; i < 10; i++){ try { this.setLocation(x + 10,y + 10); Thread.sleep(10); this.setLocation(x + 10,y - 10); Thread.sleep(10); this.setLocation(x + 10,y - 10); Thread.sleep(10); this.setLocation(x - 10,y - 10); Thread.sleep(10); this.setLocation(x,y); } catch (InterruptedException e) { e.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); fis.close(); } private void send(byte[] arr,String ip) throws IOException{ DatagramPacket packet = new DatagramPacket(arr,arr.length,InetAddress.getByName(ip),9999); socket.send(packet); //发送数据 } private void send() throws IOException { String message = sendText.getText(); //获取发送区域的内容 String ip = tf.getText(); //获取IP地址 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) + "说 " +message + " "; viewText.append(str); //将信息添加到显示区域中 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); //将时间格式化 } 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("hhh",Font.PLAIN ,20 )); viewText.setFont(new Font("hhh",Font.PLAIN ,20 )); this.add(center,BorderLayout.CENTER); } public void southPanel() { Panel south = new Panel(); //创建南边的Panel tf = new TextField(15); tf.setText("127.0.0.1"); send = new Button("发送"); log = new Button("记录"); clear = new Button("清屏"); shake = new Button("震动"); south.add(tf); south.add(send); south.add(log); south.add(clear); south.add(shake); this.add(south,BorderLayout.SOUTH); //将Panel放在Frame的南边 } public void init(){ this.setLocation(500,50); this.setSize(600, 800); new Receive().start(); try { socket = new DatagramSocket(); bw = new BufferedWriter(new FileWriter("config.txt",true)); //需要在尾部追加 } catch (Exception e) { e.printStackTrace(); } this.setVisible(true); } private class Receive extends Thread { //接收和发送需要同时执行,所以定义成多线程的 @Override public void run() { try { DatagramSocket socket = new DatagramSocket(9999); 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(); //获取ip地址 String str = time + " " + ip + "对我说: " + message + " "; viewText.append(str); bw.write(str); } } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { new Demo3_GUIChat(); } }
TCP协议:
1、客户端
创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器
调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流
输入流可以读取服务端输出流写出的数据
输出流可以写出数据到服务端的输入流
package com.tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class Demo1_Client { public static void main(String[] args) throws UnknownHostException, IOException { Socket socket = new Socket("127.0.0.1", 12345); InputStream is = socket.getInputStream(); //获取客户端输入流 OutputStream os = socket.getOutputStream(); //获取客户端的输出流 byte[] arr = new byte[1024]; int len = is.read(arr); //读取服务器发过来的数据 System.out.println(new String(arr,0,len)); //将数据转换成字符串并打印 os.write("我爱学习".getBytes()); //客户端向服务器写数据 socket.close(); } }
2、服务端
创建ServerSocket(需要指定端口号)
调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket
调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
输入流可以读取客户端输出流写出的数据
输出流可以写出数据到客户端的输入流
package com.tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class Demo1_Server { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(12345); Socket socket = server.accept(); //接受客户端的请求 InputStream is = socket.getInputStream(); //获取客户端输入流 OutputStream os = socket.getOutputStream(); //获取客户端的输出流 os.write("学习爱我".getBytes()); //服务器向客户端写数据 byte[] arr = new byte[1024]; int len = is.read(arr); //读取客户端发过来的数据 System.out.println(new String(arr,0,len)); //将数据转换成字符串并打印 socket.close(); } }
TCP协议代码优化:
package com.tcp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import java.net.UnknownHostException; public class Demo2_Client { public static void main(String[] args) throws UnknownHostException, IOException { Socket socket = new Socket("127.0.0.1", 12345); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //将字节流包装成了字符流 PrintStream ps = new PrintStream(socket.getOutputStream()); //PrintStream中有写出换行的方法 System.out.println(br.readLine()); ps.println("我爱学习"); System.out.println(br.readLine()); ps.println("好好学习,天天向上"); socket.close(); } }
package com.tcp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; public class Demo2_Server { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(12345); Socket socket = server.accept(); //接受客户端的请求 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //将字节流包装成了字符流 PrintStream ps = new PrintStream(socket.getOutputStream()); //PrintStream中有写出换行的方法 ps.println("你爱学习吗?"); System.out.println(br.readLine()); ps.println("真的吗?"); System.out.println(br.readLine()); socket.close(); } }
服务端是多线程的:
public static void demo2() throws IOException { ServerSocket server = new ServerSocket(12345); Socket socket = server.accept(); // 接受客户端的请求 new Thread(){ public void run() { try { BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 将字节流包装成了字符流 PrintStream ps = new PrintStream(socket.getOutputStream()); // PrintStream中有写出换行的方法 ps.println("你爱学习吗?"); System.out.println(br.readLine()); ps.println("真的吗?"); System.out.println(br.readLine()); socket.close(); } catch (IOException e) { e.printStackTrace(); } }; }.start(); }
练习1:
客户端向服务端写字符串(键盘录入),服务端(多线程)将字符串反转后写会,客户端再次读取到时反转后的字符串
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 Text1_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 { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(54321); System.out.println("服务器启动,绑定54321端口"); while(true){ 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(); } } }
练习2:
客户端向服务器上传文件
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 { 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 { 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(); } } }