赞
踩
线程与进程
进程就是一个运行中的程序。
一个进程中可以有多个线程,线程是CPU调度和分派的基本单位。我们可以理解为线程就是程序运行中的一条路径。
多线程存在的意义
允许多个线程并发执行,提高程序运行效率。
例如:迅雷多线程下载,QQ多个人同时聊天,凌波多个人同时共享屏幕。
创建线程有两种方式
自定义一个类继承Thread类,将线程要做的事写在run()方法中,由于子类可以当父类来用,创建自定义子类对象就是创建了一个线程。
自定义一个类实现Runnable接口,将要做的事写在run()方法中。创建Thread对象时在构造函数中传入Runnable实现类对象。
线程的启动
两种创建方式都是调用Thread对象的start()方法。
当调用start()方法时,CPU会开启一条新线程,并在新线程上执行run()方法。
public class ThreadDemo {
public static void main(String[] args) {
// demo1();
// demo2();
}
private static void demo2() {
MyRunnable r = new MyRunnable(); // 创建Runnable对象
Thread t = new Thread(r); // 创建Thread对象, 指定一个Runnable
t.start(); // 开启线程, 会判断是否传入了Runnable, 传了就用Runnable的run()
for (int i = 0; i < 100; i++)
System.out.println("A: " + i);
}
private static void demo1() {
MyThread t = new MyThread();
t.start(); // 开启一条新线程, 该线程上会自动执行run()方法
for (int i = 0; i < 100; i++)
System.out.println("A: " + i);
}
}
class MyRunnable implements Runnable{ // 定义一个类实现Runnable接口
public void run() { // 重写run()方法
for (int i = 0; i < 100; i++)
System.out.println("B: " + i);
}
}
class MyThread extends Thread { // 定义类继承Thread
public void run() { // 重写run()方法, 线程启动后就会运行run()方法
for (int i = 0; i < 100; i++)
System.out.println("B: " + i);
}
}
线程常用方法
currentThread
静态方法,用来获取当前线程
getName、setName
用来获取、设置当前线程的名字
sleep
控制线程休眠,单位为毫秒
setDaemon
将线程设置为守护线程。线程默认是非守护线程,守护线程不能单独执行。
join
当前线程暂停,等待加入的线程运行结束,当前线程继续执行。
package cn.itcast.day19;
public class ThreadMethodDemo {
public static void main(String[] args) {
// demo1();
// demo2();
// demo3();
}
private static void demo3() {
final Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + " A: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread(new Runnable(){
public void run() {
for (int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + " B: " + i);
try {
if(i==3)
t1.join(); // 当前线程暂停, 等待加入的线程运行结束, 当前线程再继续
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.start();
t2.start();
}
private static void demo2() {
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 10; i++){
System.out.println(Thread.currentThread().getName() + " A: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread(new Runnable(){
public void run() {
for (int i = 0; i < 20; i++){
System.out.println(Thread.currentThread().getName() + " B: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread t3 = new Thread() {
public void run() {
for (int i = 0; i < 5; i++){
System.out.println(Thread.currentThread().getName() + " C: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.setDaemon(true); // 必须在开启之前设置
t2.setDaemon(true); // 将t2设置为守护线程, 不会单独运行. 有其他非守护线程运行该线程才运行
t1.start(); // 10 setDaemon
t2.start(); // 20 setDaemon
t3.start(); // 5
}
private static void demo1() {
Thread.currentThread().setName("主线程");
System.out.println(Thread.currentThread().getName()); // 主线程 main
Thread t1 = new Thread() { // 定义Thread子类, 创建该类对象, 调用start()
public void run() {
for (int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + " A: " + i);
try {
Thread.sleep(1000); // 线程休眠10毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread(new Runnable(){ // 定义Runnable实现类, 创建对象, 传入Thread构造函数, 调用start()
public void run() {
for (int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + " B: " + i);
try {
Thread.sleep(1000); // 线程休眠10毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
线程安全问题
多线程并发访问同一数据,有可能出现线程安全问题。
一条线程的访问还没有结束,CPU切换到另一条线程工作,导致数据访问出错。
使用同步解决线程安全问题
使用同步代码块synchronized(锁对象){需要同步的代码...}形式将访问数据的代码锁住,在同步代码块中的内容同一时间内只能一个线程执行。
使用同步方法,用synchronized修饰方法,整个方法的代码都是同步的,只能一个线程运行。同步方法使用this作为锁。
死锁
在多个线程并发执行使用多个锁来同步时,有可能互相冲突,导致程序无法继续执行。
同步的优点与缺点
同步可以解决多个线程同时访问一个共享数据的问题,只要加上同一个锁,在同一时间内只能有一条线程执行。
在执行同步代码时每次都会判断锁,非常消耗资源,效率较低。
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.List;
import java.awt.Panel;
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.File;
import java.io.IOException;
public class Explorer {
private Frame frame;
private Panel panel;
private TextField textField;
private Button turnButton;
private Button upButton;
private List list;
public Explorer(){
createFrame(); // 创建窗体
createPanel(); // 创建Panel, 包含地址栏, 2个按钮
createList(); // 创建List
handleEvent();
frame.setVisible(true);
}
/*
* 事件处理
*/
private void handleEvent() {
turnButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
turn();
}
});
upButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
up();
}
});
textField.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER)
turn();
}
});
list.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
click();
}
});
}
/*
* 点击List
* 获取选中的文本
* 获取textField中的路径
* 封装成一个File对象
* 将textField中的路径设置为File对象的绝对路径
* 跳转
*/
private void click() {
String name = list.getSelectedItem();
String path = textField.getText();
File file = new File(path, name);
textField.setText(file.getAbsolutePath());
turn();
}
/*
* 跳转
* 清除List中所有条目
* 获取textFile中的文本
* 封装成File对象
* 如果File对象是文件, 就运行该文件
* 如果是文件夹, 调用list()方法获取所有子文件名, 添加到List中
*/
private void turn() {
list.removeAll();
String path = textField.getText();
if (path != null && path.length() > 0) {
File file = new File(path);
if (file.isFile()) {
if (file.getName().endsWith(".java") || file.getName().endsWith(".txt")) {
new Notepad().loadFile(file);
} else
try {
Runtime.getRuntime().exec("cmd /c \"" + file.getAbsolutePath() + "\"");
} catch (IOException e) {
throw new RuntimeException(e);
}
} else if (file.isDirectory()) {
String[] names = file.list();
for (String name : names)
list.add(name);
}
}
}
/*
* 向上
* 获取textField中的文本
* 封装成File对象
* 获取父级路径
* 设置回textField
* 跳转
*/
private void up() {
String path = textField.getText();
File file = new File(path);
String parentPath = file.getParent();
textField.setText(parentPath);
turn();
}
/*
* 创建Panel
*/
private void createPanel() {
// 创建Panel, 默认就是FlowLayout
panel = new Panel();
// 创建文本框和按钮
textField = new TextField(50);
turnButton = new Button("跳转");
upButton = new Button("向上");
// 将文本框和按钮添加到Panel
panel.add(textField);
panel.add(turnButton);
panel.add(upButton);
// 将Panel放在frame的北边
frame.add(panel, BorderLayout.NORTH);
}
/*
* 创建List
*/
private void createList() {
list = new List();
frame.add(list);
}
/*
* 创建窗体
*/
private void createFrame() {
frame = new Frame("资源管理器");
frame.setSize(600, 400);
frame.setLocation(200,200);
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
frame.dispose();
}
});
}
public static void main(String[] args) {
new Explorer();
}
}
在同步代码中可以使用锁对象的wait()方法让当前线程等待
使用锁对象的notify()方法可以将正在等待的线程唤醒
如果多个线程都在等待,notify()唤醒随机1个
notifyAll()方法可以唤醒所有在等待的线程
public class NotifyDemo {
public static void main(String[] args) {
final NotifyService service = new NotifyService();
new Thread() {
public void run() {
for (int i = 0; i < 5; i++)
service.print1();
}
}.start();
new Thread() {
public void run() {
for (int i = 0; i < 5; i++)
service.print2();
}
}.start();
new Thread() {
public void run() {
for (int i = 0; i < 5; i++)
service.print3();
}
}.start();
}
}
class NotifyService {
private int n = 1;
private int flag = 1; // 标识变量, 如果该值是1就轮到1, 是2就轮到2
public void print1() {
synchronized (this) {
while(flag != 1) // 如果不该1, 当前线程等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++)
System.out.println("线程1: " + n++);
System.out.println();
flag = 2; // 该轮到2了
this.notifyAll(); // 唤醒在该对象上等待的线程
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void print2() {
synchronized (this) {
while(flag != 2) // 如果不该2, 当前线程等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++)
System.out.println("线程2: " + n++);
System.out.println();
flag = 3; // 该轮到3了
this.notifyAll(); // 唤醒在该对象上等待的线程
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void print3(){
synchronized (this) {
while(flag != 3) // 如果不该3, 当前线程等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++)
System.out.println("线程3: " + n++);
System.out.println();
flag = 1; // 该轮到1了
this.notifyAll(); // 唤醒在该对象上等待的线程
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
同步
使用java.util.concurrent.locks.Lock接口的实现类对象来进行同步
ReentrantLock就是Lock的实现类,可以实现synchronized的功能
在需要同步的代码块前后使用lock()和unlock()方法来完成同步
unlock()最好放在finally中,因为如果上面代码抛出异常没有解锁的话,会导致其他线程无法运行,程序卡死。
import java.util.concurrent.locks.ReentrantLock;
public class SyncDemo {
public static void main(String[] args) {
final Service service = new Service();
new Thread() {
public void run() {
while (true) {
service.fun1();
}
}
}.start();
new Thread() {
public void run() {
while (true) {
service.fun2();
}
}
}.start();
}
}
class Service {
private ReentrantLock lock = new ReentrantLock(); // 创建锁对象
public void fun1() {
lock.lock(); // 开始同步
try {
System.out.print("闫");
System.out.print("成");
System.out.println("龙");
} finally {
lock.unlock(); // 结束同步
}
}
public void fun2() {
lock.lock();
try {
System.out.print("沙");
System.out.print("诗");
System.out.println("博");
} finally {
lock.unlock();
}
}
}
通信
使用Lock对象的newCondition()方法获取一个Condition对象,Condition对象可以控制指定线程的等待与唤醒。
await()方法可以控制线程等待。
signal()方法可以唤醒等待的线程。
signalAll()方法可以唤醒所有等待线程。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class NotifyDemo {
public static void main(String[] args) {
final NotifyService service = new NotifyService();
new Thread() {
public void run() {
for (int i = 0; i < 5; i++)
service.print1();
}
}.start();
new Thread() {
public void run() {
for (int i = 0; i < 5; i++)
service.print2();
}
}.start();
new Thread() {
public void run() {
for (int i = 0; i < 5; i++)
service.print3();
}
}.start();
}
}
class NotifyService {
private int n = 1;
private int flag = 1; // 标识变量, 如果该值是1就轮到1, 是2就轮到2
private ReentrantLock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print1() {
lock.lock();
while(flag!=1)
try {
c1.await(); // 使用c1对象等待
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++)
System.out.println("线程1: " + n++);
System.out.println();
flag = 2;
c2.signal(); // 指定唤醒c2对象
lock.unlock();
}
public void print2() {
lock.lock();
while(flag!=2)
try {
c2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++)
System.out.println("线程2: " + n++);
System.out.println();
flag = 3;
c3.signal();
lock.unlock();
}
public void print3() {
lock.lock();
while(flag!=3)
try {
c3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++)
System.out.println("线程3: " + n++);
System.out.println();
flag = 1;
c1.signal();
lock.unlock();
}
}
什么是GUI
GUI是Graphical User Interface的缩写,图形化用户界面
awt和swing
Java为GUI提供的对象都存在java.awt,javax.swing两个包中
awt依赖于本地系统平台,如颜色样式显示
swing跨平台
组件与容器
组件 Component,是GUI图形界面的组成单元。
容器Container,可以存放组件,也可以存放容器。
布局管理
FlowLayout(流式布局管理器)
从左到右的顺序排列。
BorderLayout(边界布局管理器)
东,南,西,北,中
GridLayout(网格布局管理器)
规则的矩阵
CardLayout(卡片布局管理器)
选项卡
GridBagLayout(网格包布局管理器)
非规则的矩阵
建立一个窗体
窗体中可以存放各种组件,所以窗体是容器Container。创建时我们使用的是它的子类
Container的常用子类有两个,Window和Panel。Window是我们常用的窗体,Panel是用来布局的不可见的。
Window也有两个常用子类,Frame和Dialog。Frame是我们常用的带有标题和边框的顶层窗口,Dialog是对话框。
所有AWT包中的类都会运行在AWT线程上
package cn.itcast.day20;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class FrameDemo {
public static void main(String[] args) {
Frame frame = new Frame("节日快乐!");
frame.setSize(600, 400);
frame.setLocation(200, 200);
frame.setLayout(new FlowLayout());
Button button = new Button("关闭");
frame.add(button);
frame.addWindowListener(new MyWindowListener()); // 给frame添加窗体监听器
// button.addMouseListener(new MyMouseListener()); // 给button添加鼠标监听器
// button.addKeyListener(new MyKeyListener()); // 给button添加键盘监听器
button.addActionListener(new MyActionListener()); // 给button添加动作监听器
frame.setVisible(true); // 设置可见性. 也可以当做刷新用
}
}
class MyActionListener implements ActionListener { // 动作监听器
public void actionPerformed(ActionEvent e) {
Button button = (Button) e.getSource();
Frame frame = (Frame) button.getParent();
frame.dispose();
}
}
class MyKeyListener extends KeyAdapter { // 键盘监听器
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) { // 判断按下的键是空格
Button button = (Button) e.getSource();
Frame frame = (Frame) button.getParent();
frame.dispose();
}
}
}
class MyMouseListener extends MouseAdapter { // 鼠标监听器
public void mouseClicked(MouseEvent e) {
Button button = (Button) e.getSource(); // 获取事件源(Button)
Frame frame = (Frame) button.getParent(); // 获取父级容器(Frame)
frame.dispose();
}
}
class MyWindowListener extends WindowAdapter { // 窗体监听器
public void windowClosing(WindowEvent e) {
Frame frame = (Frame) e.getSource(); // 获取事件源, 强转为frame
frame.dispose(); // 关闭窗体
}
}
事件处理机制
事件:用户对组件的一个操作。
事件源:发生事件的组件。
监听器:我们需要处理某个事件,就需要在发生事件的组件上添加监听器,也就是java.awt.event包中XxxListener接口的子类。
事件处理器:监听器中的方法。监听器被添加在组件上之后,组件上发生了对应事件就会执行指定方法。
常用事件分类
窗体事件,WindowEvent,窗体打开、关闭、正在关闭、激活、最小化等。
鼠标事件,MouseEvent,鼠标按下、抬起、进入、移出等。
键盘事件,KeyEvent,键盘按下、抬起等。
动作事件,ActionEvent,在某一组件上发生了定义好的动作,例如按钮上鼠标点击或按空格,菜单上鼠标点击或按回车等。
Adapter
通常Listener接口中都是有多个抽象方法的,而我们使用的时候有可能不会全部需要,如果定义子类实现接口那么就必须重写所有方法,这是比较麻烦的。Java中为我们提供了一些抽象的实现类,在类中对接口的抽象方法进行了空的实现,我们再定义监听器时只要继承这些抽象类就可以,这样那些不需要的方法就可以不再去重写了。
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.List;
import java.awt.Panel;
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.File;
import java.io.IOException;
public class Explorer {
private Frame frame;
private Panel panel;
private TextField textField;
private Button turnButton;
private Button upButton;
private List list;
public Explorer(){
createFrame(); // 创建窗体
createPanel(); // 创建Panel, 包含地址栏, 2个按钮
createList(); // 创建List
handleEvent();
frame.setVisible(true);
}
/*
* 事件处理
*/
private void handleEvent() {
turnButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
turn();
}
});
upButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
up();
}
});
textField.addKeyListener(new KeyAdapter(){
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER)
turn();
}
});
list.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
click();
}
});
}
/*
* 点击List
* 获取选中的文本
* 获取textField中的路径
* 封装成一个File对象
* 将textField中的路径设置为File对象的绝对路径
* 跳转
*/
private void click() {
String name = list.getSelectedItem();
String path = textField.getText();
File file = new File(path, name);
textField.setText(file.getAbsolutePath());
turn();
}
/*
* 跳转
* 清除List中所有条目
* 获取textFile中的文本
* 封装成File对象
* 如果File对象是文件, 就运行该文件
* 如果是文件夹, 调用list()方法获取所有子文件名, 添加到List中
*/
private void turn() {
list.removeAll();
String path = textField.getText();
if (path != null && path.length() > 0) {
File file = new File(path);
if (file.isFile()) {
if (file.getName().endsWith(".java") || file.getName().endsWith(".txt")) {
new Notepad().loadFile(file);
} else
try {
Runtime.getRuntime().exec("cmd /c \"" + file.getAbsolutePath() + "\"");
} catch (IOException e) {
throw new RuntimeException(e);
}
} else if (file.isDirectory()) {
String[] names = file.list();
for (String name : names)
list.add(name);
}
}
}
/*
* 向上
* 获取textField中的文本
* 封装成File对象
* 获取父级路径
* 设置回textField
* 跳转
*/
private void up() {
String path = textField.getText();
File file = new File(path);
String parentPath = file.getParent();
textField.setText(parentPath);
turn();
}
/*
* 创建Panel
*/
private void createPanel() {
// 创建Panel, 默认就是FlowLayout
panel = new Panel();
// 创建文本框和按钮
textField = new TextField(50);
turnButton = new Button("跳转");
upButton = new Button("向上");
// 将文本框和按钮添加到Panel
panel.add(textField);
panel.add(turnButton);
panel.add(upButton);
// 将Panel放在frame的北边
frame.add(panel, BorderLayout.NORTH);
}
/*
* 创建List
*/
private void createList() {
list = new List();
frame.add(list);
}
/*
* 创建窗体
*/
private void createFrame() {
frame = new Frame("资源管理器");
frame.setSize(600, 400);
frame.setLocation(200,200);
frame.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
frame.dispose();
}
});
}
public static void main(String[] args) {
new Explorer();
}
}
IP地址
每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。
ipconfig:查看本机IP
ping:测试连接
本地回路地址:127.0.0.1
IPv4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。已经用尽。
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
import java.net.*;
class IpDemo
{
public static void main(String[] args) throws Exception
{
//获取本机ip对象。
InetAddress ip = InetAddress.getLocalHost();
System.out.println("ip="+ip);
//单独获取名称和地址。
System.out.println("address="+ip.getHostAddress());
System.out.println("name="+ip.getHostName());
//将主机名称或者ip地址字符串变成ip对象。
InetAddress ia = InetAddress.getByName("192.168.1.109");
System.out.println("ia address="+ia.getHostAddress());
System.out.println("ia name="+ia.getHostName());
System.out.println("----------------------------");
InetAddress[] ip2 = InetAddress.getAllByName("www.baidu.com");
System.out.println("ip2 address="+ip2[0].getHostAddress());
System.out.println("ip2 name="+ip2[0].getHostName());
}
}
端口号
每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序。
端口号范围从0-65535
编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。
常用端口
mysql: 3306
oracle: 1521
web: 80
tomcat: 8080
QQ: 4000
feiQ: 2425
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class Sender {
public static void main(String[] args) throws Exception {
// 要发送的数据
byte[] data = "Hello 传智播客!".getBytes();
// 创建DatagramSocket, 绑定本机IP, 绑定随机一个端口号
DatagramSocket socket = new DatagramSocket();
// 创建DatagramPacket, 包含要发送的数据, 数据长度, IP地址, 端口号
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 8888);
// 发送数据
socket.send(packet);
// 释放资源
socket.close();
}
}
网络协议
为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
UDP
面向无连接,数据不安全,速度快。不区分客户端与服务端。
TCP
面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。
Socket
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。
Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port。
UDP传输
发送
创建DatagramSocket
创建DatagramPacket
使用DatagramSocket发送DatagramPacket
关闭DatagramSocket
接收
创建DatagramSocket
创建DatagramPacket
使用DatagramSocket接收DatagramPacket
关闭DatagramSocket
import java.net.*;
//定义发送端,通过udp协议完成。
/*
用UDP发送数据。
思路:
1,建立一个UDPsocket服务。
2,定义要发送的数据。
3,将数据打成数据包。
4,通过socket服务将数据包发出去。
5,关闭资源。
*/
class SendDemo
{
public static void main(String[] args) throws Exception
{
//1,创建udpsocket服务。
DatagramSocket ds = new DatagramSocket();
//2,定义数据。
String str = "udp demo ,o le ";
byte[] buf = str.getBytes();
//3,将数据打成数据包.
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.254"),10000);
//4,使用socket服务的send方法将数据发送出去。
ds.send(dp);
//5,关闭资源。
ds.close();
}
}
//使用了udp服务的接收端。
/*
建立UDP的接收端。
思路:
1,建立udpsocket服务。需要监听一个端口,简单说,就是给接收端这个应用程序定义一个数字标识。
2,定义数据包用于存储接收到的数据。
3,使用socket服务的接收功能,将数据都存储到数据包中。
4,通过数据包对象的方法获取具体的数据内容。
5,关闭资源。
*/
class ReceDemo
{
public static void main(String[] args) throws Exception
{
//1,创建udpsocket服务,并监听一个端口
DatagramSocket ds = new DatagramSocket(10000);
//2,建立数据包,用于存储数据。存储时需要定义个字节数组缓冲区。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
//3,使用socket服务的receive方法将接收到的数据存储到数据包中。
ds.receive(dp);
//4,通过数据包对象的方法获取具体数据内容。
//4.1获取地址。
String ip = dp.getAddress().getHostAddress();
//4.2获取端口
int port = dp.getPort();
//4.3获取数据。
String data = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"::"+data+"::"+port);
//5,关闭资源。
ds.close();
}
}
TCP传输
客户端
创建Socket连接服务端
调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的管道流
输入流可以读取服务端输出流写出的数据
输出流可以写出数据到服务端的输入流
服务端
创建ServerSocket
调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket
调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的管道流
输入流可以读取客户端输出流写出的数据
输出流可以写出数据到客户端的输入流
import java.net.*;
import java.io.*;
//建立tcp传输。
/*
创建客户端。
思路:
1,建立客户端socket服务。并同时指定要连接的服务端也就是指定ip和端口。
2,如果连接成功,就会有通道,就会有socket流。
通过socket服务的方法可以获取其中的读写流对象。
3,通过读写流对象的读写方法将数据和服务端进行传输。
4,关闭资源。
需求:客户端发送一句话给服务端。
*/
class ClientDemo
{
public static void main(String[] args) throws Exception
{
//1,建立客户端socket服务。并指定要连接的ip和端口。
Socket s = new Socket("192.168.1.254",10004);
//2,通过socket服务的getOutputStream获取socket写入流对象。
OutputStream out = s.getOutputStream();
//3,将数据写到socket流中。
out.write("tcp demo ,o le ".getBytes());
s.close();
}
}
/*
建立服务端:
思路:
1,建立服务端的socket服务。ServerSocket,服务端需要监听一个端口。
给它分配一个数字标识。
2,获取具有socket流的Socket对象。
3,通过socket对象获取其中的流对数据进行操作。
4,关闭资源。
需求:服务端接收客户端的数据并打印即可。
*/
class ServerDemo
{
public static void main(String[] args) throws Exception
{
//1,建立服务端socket服务。并监听一个端口。
ServerSocket ss = new ServerSocket(10004);
//2,获取socket对象。
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"......connected");l
//3,通过socket对象获取socket读取流。
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
s.close();
ss.close();
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。