1. 实现多线程 1.1 进程 进程:是正在运行的程序
是系统进行资源分配郑调用的独立单位
每一个进程都有它自己的内存空间和系统资源
1.2 线程 线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序
举例:
1.3 多线程的实现方式(方式1) 方式1:继承Thread类
定义一个类MyThread继承Thread类
在MyThread类中重写run()方法
创建MyThread类的对象
启动线程
两个小问题:
因为run() 是用来封装被线程执行的代码
run() :封装线程执行的代码,直接调用,相当于普通方法的调用 start():启动线程;然后由JVM调用此线程的run()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package demo_01;public class MyThread extends Thread { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(i); } } } package demo_01;public class MyThreadDemo { public static void main (String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); mt1.start(); mt2.start(); } }
1.4 设置和获取线程名称 Thread类中设置和获取线程名称的方法
void setName(String name):将此线程的名称更改为等于参数name
String getName() :返回此线程的名称
通过构造方法也可以设置线程名称
如何获取main() 方法所在的线程名称?
public static Thread currentThread() 返回对当前正在执行的线程对象的引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package demo_02;public class MyThread extends Thread { public MyThread () { } public MyThread (String name) { super (name); } @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(getName()+":" +i); } } } package demo_02;public class MyThreadDemo { public static void main (String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); mt1.setName("高铁" ); mt2.setName("飞机" ); mt1.start(); mt2.start(); System.out.println(Thread.currentThread().getName()); } }
1.5 线程调度 线程有两种调度模型
分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些
Java使用的是抢占式调度模型
假如计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权, 才可以执行指令。所以说多线程程序的执行是有随机性 ,因为谁抢到CPU的使用权是不一定的
Thread类中设置和获取线程优先级的方法
public final int getPriority() :返回此线程的优先级
public final void setPriority(int newPriority):更改此线程的优先级
线程默认优先级是5;线程优先级的范围是:1-10
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package demo_03;public class ThreadPriority extends Thread { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(getName()+":" +i); } } } package demo_03;public class ThreadPriorityDemo { public static void main (String[] args) { ThreadPriority tp1 = new ThreadPriority(); ThreadPriority tp2 = new ThreadPriority(); ThreadPriority tp3 = new ThreadPriority(); tp1.setName("高铁" ); tp2.setName("飞机" ); tp3.setName("汽车" ); tp1.setPriority(5 ); tp2.setPriority(10 ); tp3.setPriority(1 ); tp1.start(); tp2.start(); tp3.start(); } }
1.6 线程控制
方法名
说明
static void sleep(long millis)
使当前正在执行的线程停留(暂停执行)指定的毫秒数
void join()
等待这个线程死亡
void setDaemon(boolean on)
将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package demo_04;public class ThreadSleep extends Thread { public ThreadSleep () { } public ThreadSleep (String name) { super (name); } @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(getName() + ":" + i); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } } } package demo_04;public class ThreadSleepDemo { public static void main (String[] args) { ThreadSleep ts1 = new ThreadSleep("曹操" ); ThreadSleep ts2 = new ThreadSleep("刘备" ); ThreadSleep ts3 = new ThreadSleep("孙权" ); ts1.start(); ts2.start(); ts3.start(); } }
void join() 等待这个线程死亡
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package demo_04;public class ThreadJoin extends Thread { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(getName()+":" +i); } } } package demo_04;public class ThreadJoinDemo { public static void main (String[] args) { ThreadJoin tj1 = new ThreadJoin(); ThreadJoin tj2 = new ThreadJoin(); ThreadJoin tj3 = new ThreadJoin(); tj1.setName("张三" ); tj2.setName("李四" ); tj3.setName("王五" ); tj1.start(); try { tj1.join(); } catch (InterruptedException e) { e.printStackTrace(); } tj2.start(); tj3.start(); } }
void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package demo_04;public class ThreadDaemon extends Thread { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(); } } } package demo_04;public class ThreadDaemonDemo { public static void main (String[] args) { ThreadDaemon td1 = new ThreadDaemon(); ThreadDaemon td2 = new ThreadDaemon(); td1.setName("关羽" ); td1.setName("张飞" ); Thread.currentThread().setName("刘备" ); td1.setDaemon(true ); td2.setDaemon(true ); for (int i = 0 ; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+":" +i); } } }
1.7 线程生命周期
1.8 多线程的实现方式(方式2) 方式2:实现Runnable接口
定义一个类MyRunnable:实现Runnablef接口
在MyRunnable类中重写run()方法
创建MyRunnable类的对象
创建Thread类的对象,把MyRunnable对像作为构造方法的参数
启动线程
多线程的实现方案有两种
相比继承Thread类,实现Runnable接口的好处
避免了Java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package demo_05;public class MyRunnable implements Runnable { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println(Thread.currentThread().getName()+":" +i); } } } package demo_05;public class MyRunnableDemo { public static void main (String[] args) { MyRunnable mr = new MyRunnable(); Thread t1 = new Thread(mr,"高铁" ); Thread t2 = new Thread(mr,"飞机" ); t1.start(); t2.start(); } }
2. 线程同步 案例:卖票 需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
思路: ①定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets=100; ②在SellTicket类中重写run() 方法实现卖票,代码步骤如下: A:判断票数大于0,就卖票,并告知是哪个窗口卖的 B:卖了票之后,总票数要减1 C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行 ③定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下 A:创建SellTicket类的对象 B:创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称 C:启动线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package demo_06;public class SellTicket implements Runnable { private int tickets = 100 ; @Override public void run () { while (true ) { if (tickets > 0 ) { System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票" ); tickets--; } } } } package demo_06;public class SellTicketDemo { public static void main (String[] args) { SellTicket st = new SellTicket(); Thread th1 = new Thread(st,"窗口1" ); Thread th2 = new Thread(st,"窗口2" ); Thread th3 = new Thread(st,"窗口3" ); th1.start(); th2.start(); th3.start(); } }
2.1 买票案例的思考 刚才讲解了电影院卖票程序,好像没有什么问题。但是在实际生活中,售票时出票也是需要时间的所以,在出售一张票的时候,需要一点时间的延迟,接下来我们去修改卖票程序中卖票的动作:每次出票时间100毫秒,用sleep() 方法实现
卖票出现了问题:
相同的票出现了多次
出现了负数的票
问题原因:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 package demo_06;public class SellTicket implements Runnable { private int tickets = 100 ; @Override public void run () { while (true ) { if (tickets > 0 ) { try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票" ); tickets--; } } } } package demo_06;public class SellTicketDemo { public static void main (String[] args) { SellTicket st = new SellTicket(); Thread th1 = new Thread(st,"窗口1" ); Thread th2 = new Thread(st,"窗口2" ); Thread th3 = new Thread(st,"窗口3" ); th1.start(); th2.start(); th3.start(); } }
2.2 卖票案例数据安全问题解决 为什么出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准)
是否是多线程环境
是否有共享数据
是否有多条语句操作共享数据
如何解决多线程安全问题呢?
怎么实现呢?
把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
Java提供了同步代码块的方式来解决(如下2.3)
2.3 同步代码块 锁多条语句操作共享数据,可以使用同步代码块实现
格式:
1 2 3 synchronized (任意对象){ 多条语句操作共享数据的代码 }
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
同步的好处和弊端:
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package demo_06;public class SellTicket implements Runnable { private int tickets = 100 ; private Object obj = new Object(); @Override public void run () { while (true ) { synchronized (obj){ if (tickets > 0 ) { try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票" ); tickets--; } } } } } package demo_06;public class SellTicketDemo { public static void main (String[] args) { SellTicket st = new SellTicket(); Thread th1 = new Thread(st,"窗口1" ); Thread th2 = new Thread(st,"窗口2" ); Thread th3 = new Thread(st,"窗口3" ); th1.start(); th2.start(); th3.start(); } }
2.4 同步方法 同步方法:就是把synchronized关键字加到方法上
格式:
1 修饰符 synchronized 返回值类型 方法名(方法参数){}
同步方法的锁对象是什么呢?
同步静态方法:就是把synchronized关键字加到静态方法上
格式:
1 修饰符 static synchronized 返回值类型 方法名(方法参数){}
同步静态方法的锁对象是什么呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 package demo_06;public class SellTicket implements Runnable { private static int tickets = 100 ; private Object obj = new Object(); private int x = 0 ; @Override public void run () { while (true ) { if (x % 2 == 0 ) { synchronized (SellTicket.class) { if (tickets > 0 ) { try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票" ); tickets--; } } } else { sellticks(); } x++; } } private static synchronized void sellticks () { if (tickets > 0 ) { try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票" ); tickets--; } } } package demo_06;public class SellTicketDemo { public static void main (String[] args) { SellTicket st = new SellTicket(); Thread th1 = new Thread(st,"窗口1" ); Thread th2 = new Thread(st,"窗口2" ); Thread th3 = new Thread(st,"窗口3" ); th1.start(); th2.start(); th3.start(); } }
2.5 线程安全的类 StringBuffer
线程安全,可变的字符序列
从版本JDK5开始,被StringBuilder替代。通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步
Vector
从Java2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。与新的集合实现不同,Vector被同步。如果不需要线程安全的实现,建议使用ArrayList代替Vector
Hashtable
该类实现了一个哈希表,它将键映射到值。任何非null对像都可以用作键或者值
从Java2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成员。 与新的集合实现不同,Hashtable被同步。如果不需要线程安全的实现,建议使用HashMap代替Hashtable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package demo_07;import java.util.*;public class ThreadDemo { public static void main (String[] args) { StringBuffer sb2 = new StringBuffer(); StringBuilder sb = new StringBuilder(); Vector<String> v = new Vector<String>(); ArrayList<String> array = new ArrayList<String>(); Hashtable<String, String> ht = new Hashtable<String, String>(); HashMap<String, String> hm = new HashMap<String, String>(); List<String> lise = Collections.synchronizedList(new ArrayList<String>()); } }
2.6 Lock锁 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized() 方法和语句可以获得更广泛的锁定操作 Lock中提供了获得锁和释放锁的方法:
void lock() :获得锁
void unlock() :释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化 ReentrantLock的构造方法:
ReentrantLock() :创建一个ReentrantLock的实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package demo_08;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable { private int tickets = 100 ; private Lock lock = new ReentrantLock(); @Override public void run () { while (true ) { try { lock.lock(); if (tickets > 0 ) { try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票" ); tickets--; } } finally { lock.unlock(); } } } } package demo_08;public class SellTicketDemo { public static void main (String[] args) { SellTicket st = new SellTicket(); Thread th1 = new Thread(st,"窗口1" ); Thread th2 = new Thread(st,"窗口2" ); Thread th3 = new Thread(st,"窗口3" ); th1.start(); th2.start(); th3.start(); } }
4. 网络编程入门 4.1 网络编程概述 计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
网络编程
在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
4.2 网络编程三要素 IP地址
要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数据的计算机和识别发送的计算机,而P地址就是这个标识号。也就是设备的标识
端口
网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说P地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识
协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规呗则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议
4.3 IP地址 IP地址:是网络中设备的唯一标识
P地址分为两大类
IPv4:是给每个连接在网络上的庄机分配一个32bi地址。按照TCP八P规定,P地址用二进制来表示,每个P地址长32bit,也就是4个字节。例破如一个采用二进制形式的P地址是“11000000101010000000000101000010°”,这么长的地址,处理起来也太费劲了。为了方便使用,P地址经常被写成十进制的形式,中间使用符号“”分隔不同的字节。于是,上面的1P地址可以表示为”192.168.1.66”。P地址的这种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多
IPV6:由于互联网的蓬勃发展,P地址的需求量愈来愈大,但是网络地址资源有限,使得P的分配越发紧张。为了扩大地址空间,通过Pv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
常用命令:
ipconfig:查看本机IP地址
ping IP地址:检查网络是否连通
特殊IP地址:
127.0.0.1:是回送地址,可以代表本机地址,般用来测试使用
4.4 InetAddress的使用 为了方便我们对lP地址的获取和操作,Java提供了一个类InetAddress供我们使用
InetAddress:此类表示Internet协议 (IP) 地址
方法明
说明
static InetAddress getByName(String host)
确定主机名称的P地址。主机名称可以是机器名称,也可以是P地址
String getHostName()
获取此P地址的主机名
String getHostAddress()
返回文本显示中的IP地址字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package demo_01;import java.io.IOException;import java.net.InetAddress;public class InetAddressDemo { public static void main (String[] args) throws IOException { InetAddress address = InetAddress.getByName("192.168.31.81" ); String name = address.getHostName(); String ip = address.getHostAddress(); System.out.println("主机名:" +name); System.out.println("IP地址:" +ip); } }
4.5 端口 端口:设备上应用程序的唯一标识
端口号:用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
4.6 协议 协议:计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议:
用户数据报协议(User Datagram Protocol)
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输
例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议
TCP协议:
传输控制协议(Transmission Control Protocol)
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收总建立逻辑连接,然后再传输数据, 它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端 向服务端发出连接请求,每次连接的创建都需要经过“三次握手”
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠 第一次握手,客户端向服务器端发出连接请求,等待服务器确认 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求 第三次握手,客户端再次向服务器端发送确认信息,确认连接
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等
5. UDP通信原理 5.1 UDP通信原理 UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念Java提供了DatagramSocket类作为基于UDP协议的Socket
5.2 UDP发送数据 发送数据的步骤
①创建发送端的Socket对象(DatagramSocket)
②创建数据,并把数据打包
1 DatagramPacket(byte []buf,int length,InetAddress address,int port)
③调用DatagramSocket对象的方法发送数据
1 void send (DatagramPacket p)
④关闭发送端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package demo_02;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class SendDemo { public static void main (String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(); byte [] bys = "hello,java" .getBytes(); DatagramPacket dp = new DatagramPacket(bys,bys.length, InetAddress.getByName("192.168.31.81" ),10010 ); ds.send(dp); ds.close(); } }
5.3 UDP接收数据 接收数据的步骤
①创建接收端的Socket对象(DatagramSocket)
1 DatagramSocket(int port)
②创建一个数据包,用于接收数据
1 DatagramPacket(byte []buf,int length)
③调用DatagramSocket对象的方法接收数据
1 void receive (DatagramPacket p)
④解析数据包,并把数据在控制台显示
1 2 byte [] getData()int getLength ()
⑤关闭接收端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package demo_02;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;public class ReceiveDemo { public static void main (String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(10010 ); byte [] bys = new byte [1024 ]; DatagramPacket dp = new DatagramPacket(bys,bys.length); ds.receive(dp); byte [] datas = dp.getData(); int len = dp.getLength(); System.out.println("数据是:" +new String(datas,0 ,len)); ds.close(); } }
5.4 UDP通信程序练习 按照下面的要求实现程序:
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package demo_03;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.InetAddress;public class SendDemo { public static void main (String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line; while ((line=br.readLine())!=null ){ if ("886" .equals(line)){ break ; } byte [] bys = line.getBytes(); DatagramPacket dp = new DatagramPacket(bys,bys.length, InetAddress.getByName("192.168.31.81" ),10010 ); ds.send(dp); } } } package demo_03;import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketException;public class ServerDemo { public static void main (String[] args) throws IOException { DatagramSocket ds = new DatagramSocket(10010 ); while (true ){ byte [] bys = new byte [1024 ]; DatagramPacket dp = new DatagramPacket(bys,bys.length); ds.receive(dp); System.out.println("数据是:" +new String(dp.getData(),0 ,dp.getLength())); } } }
6. TCP通信原理 6.1 TCP通信原理
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端新形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就河以通过虚拟链路进行通信
Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
6.2 TCP发送数据 发送数据的步骤:
①创建客户端的Socket对象(Socket)
1 Socket(String host,int port)
②获取输出流,写数据
1 OutputStream getOutputStream ()
③释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package TCPDemo_04;import java.io.IOException;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;public class ClientDemo { public static void main (String[] args) throws IOException { Socket s= new Socket("192.168.31.81" ,10010 ); OutputStream os = s.getOutputStream(); os.write("hello,java" .getBytes()); s.close(); } }
6.3 TCP接受数据 接收数据的步骤:
①创建服务器端的Socket对象(ServerSocket)
②监听客户端连接,返回一个Socket对象
③获取输入流,读数据,并把数据显示在控制台
1 InputStream getlnputStream ()
④释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package TCPDemo_04;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class Serverdemo { public static void main (String[] args) throws IOException { ServerSocket ss = new ServerSocket(10010 ); Socket s = ss.accept(); InputStream is = s.getInputStream(); byte [] bys = new byte [1024 ]; int len = is.read(bys); String data = new String(bys,0 ,len); System.out.println("数据是:" +data); ss.close(); s.close(); } }
6.4 TCP通信程序练习 练习1
客户端:发送数据,接收服务器反馈
服务端:接收数据,给出反馈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package TcpPracticeDemo_01;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main (String[] args) throws IOException { ServerSocket ss = new ServerSocket(10010 ); Socket s = ss.accept(); InputStream is = s.getInputStream(); byte [] bys = new byte [1024 ]; int len = is.read(bys); String data = new String(bys,0 ,len); System.out.println("服务器" +data); OutputStream os = s.getOutputStream(); os.write("over" .getBytes()); ss.close(); } } package TcpPracticeDemo_01;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;public class ClientDemo { public static void main (String[] args) throws IOException { Socket ss = new Socket("192.168.31.81" ,10010 ); OutputStream os = ss.getOutputStream(); os.write("hello,java" .getBytes()); InputStream is = ss.getInputStream(); byte [] bys = new byte [1024 ]; int len; while ((len = is.read(bys)) != -1 ) { System.out.println("客户端:" + new String(bys, 0 , len)); } ss.close(); } }
练习2
客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
服务器:接收到的数据在控制台输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package TPracticeDemo_02;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main (String[] args) throws IOException { ServerSocket ss = new ServerSocket(10010 ); Socket s = ss.accept(); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); String line; while ((line = br.readLine()) != null ) { String data = new String(line); System.out.println("服务器:" +data); } ss.close(); } } package TPracticeDemo_02;import java.io.*;import java.net.InetAddress;import java.net.Socket;public class ClientDemo { public static void main (String[] args) throws IOException { Socket s= new Socket(InetAddress.getByName("192.168.31.81" ),10010 ); BufferedReader be = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line=be.readLine())!=null ){ if ("886" .equals(line)){ break ; } bw.write(line); bw.newLine(); bw.flush(); } s.close(); } }
练习3
客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
服务器:接收到的数据写入文本文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package TPracticeDemo_03;import com.sun.source.tree.WhileLoopTree;import java.io.*;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main (String[] args) throws IOException { ServerSocket ss = new ServerSocket(10010 ); Socket s = ss.accept(); BufferedReader data = new BufferedReader(new InputStreamReader(s.getInputStream())); BufferedWriter bw = new BufferedWriter(new FileWriter("myInet\\java.txt" )); String line; while ((line = data.readLine()) != null ) { bw.write(line); bw.newLine(); bw.flush(); } ss.close(); bw.close(); } } package TPracticeDemo_03;import java.io.*;import java.net.InetAddress;import java.net.Socket;public class ClientDemo { public static void main (String[] args) throws IOException { Socket s = new Socket(InetAddress.getByName("192.168.31.81" ),10010 ); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null ) { if ("886" .equals(line)) { break ; } bw.write(line); bw.newLine(); bw.flush(); } s.close(); } }
练习4
客户端:数据来自于文本文件
服务器:接收到的数据写入文本文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package TPracticeDemo_04;import java.io.*;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main (String[] args) throws Exception { ServerSocket ss = new ServerSocket(10010 ); Socket s = ss.accept(); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); BufferedWriter bw = new BufferedWriter(new FileWriter("myInet\\java.txt" )); String line; while ((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); bw.flush(); } ss.close(); bw.close(); } } package TPracticeDemo_04;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileReader;import java.io.OutputStreamWriter;import java.net.Socket;public class ClientDemo { public static void main (String[] args) throws Exception { Socket s = new Socket("192.168.31.81" ,10010 ); BufferedReader br = new BufferedReader(new FileReader("myInet\\copy.txt" )); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); bw.flush(); } s.close(); br.close(); } }
练习5
客户端:数据来自于文本文件,接收服务器反馈
服务器:接收到的数据写入文本文件,给出反馈
出现问题:程序一直等待
原因:读数据的方法是阻塞式的 解决办法:自定义结束标记使用shutdownOutput()方法(推荐)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package TPracticeDemo_05;import java.io.*;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main (String[] args) throws Exception { ServerSocket ss = new ServerSocket(10010 ); Socket s = ss.accept(); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); BufferedWriter bw= new BufferedWriter(new FileWriter("myInet\\copy.txt" )); String line; while ((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); bw.flush(); } System.out.println(11111 ); OutputStream outputStream = s.getOutputStream(); outputStream.write("数据已接收" .getBytes()); ss.close(); bw.close(); } } package TPracticeDemo_05;import java.io.*;import java.net.InetAddress;import java.net.Socket;public class ClientDemo { public static void main (String[] args) throws Exception { Socket s = new Socket("192.168.31.81" , 10010 ); BufferedReader br = new BufferedReader(new FileReader("myInet\\java.txt" )); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); bw.flush(); } s.shutdownOutput(); InputStream inputStream = s.getInputStream(); byte [] bys = new byte [1024 ]; int len; while ((len=inputStream.read(bys))!=-1 ){ System.out.println(new String(bys,0 ,len)); } s.close(); br.close(); } }
练习6
客户端:数据来自于文本文件,接收服务器反馈
服务器:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 package TPracticeDemo_06;import java.io.*;import java.net.Socket;public class ClientDemo { public static void main (String[] args) throws Exception { Socket s = new Socket("192.168.31.81" ,10010 ); BufferedReader br = new BufferedReader(new FileReader("myInet\\java.txt" )); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line; while ((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); bw.flush(); } s.shutdownOutput(); InputStream inputStream = s.getInputStream(); byte [] bys = new byte [1024 ]; int len; while ((len = inputStream.read(bys)) != -1 ) { System.out.println(new String(bys,0 ,len)); } s.close(); br.close(); } } package TPracticeDemo_06;import java.net.ServerSocket;import java.net.Socket;public class ServerDemo { public static void main (String[] args) throws Exception { ServerSocket ss = new ServerSocket(10010 ); while (true ) { Socket s = ss.accept(); new Thread(new ServerThread(s)).start(); } } } package TPracticeDemo_06;import java.io.*;import java.net.Socket;public class ServerThread implements Runnable { private Socket s; public ServerThread (Socket s) { this .s = s; } @Override public void run () { try { BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); int count = 0 ; File file = new File("myInet\\copy" +count+".txt" ); while (file.exists()) { count++; file = new File("myInet\\copy" +count+".txt" ); } BufferedWriter bw= new BufferedWriter(new FileWriter(file)); String line; while ((line = br.readLine()) != null ) { bw.write(line); bw.newLine(); bw.flush(); } OutputStream outputStream = s.getOutputStream(); outputStream.write("服务器已成功接收数据" .getBytes()); } catch (Exception e){ e.printStackTrace(); } } }