一、多线程
1. 进程 & 线程
1.1 进程
进程:正在运行的应用长须 是系统进行资源分配和调用的独立单位 每一个进程都有它自己的内存空间和系统资源
1.2. 线程
线程:是进程中的单个顺序控制流,是一条执行路径 单线程:一个进程如果只有一条执行路径,则称为单线程程序 多线程:一个进程如果有多条执行路径,则称为多线程程序
2. 实现多线程<两种方式>
2.1 继承Thread类<方式一>
2.1.1 方法
方法名 说明 void run() 在线程开启后,此方法将被调用执行 void start() 使此线程开始执行,Java虚拟机会调用run方法()
2.1.2 实现步骤
定义一个类MyThread 继承 Thread类 在MyThread类中重写 run() 方法
run() 是用来封装被线程执行的代码 如果直接调用,相当于普通方法的调用 创建MyThread类的对象 启动线程 start()
2.1.3 代码示例
package com. wang ;
public class MyThread extends Thread {
@Override
public void run ( ) {
for ( int i = 0 ; i < 100 ; i++ ) {
System . out. println ( i) ;
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
/* 区别run() 与 start()
run()方法的调用并没有启动此线程
my1.run();
my2.run();
*/
//void start () 导致此线程开始执行;Java虚拟机调用此线程的run()方法
my1.start();
my2.start();
}
}
2.2 设置 & 获取线程名称
2.2.1 相关方法
方法名 说明 void setName(String name) 将此线程的名称更改为等于参数name String getName() 返回此线程的名称 Thread currentThread() 返回对当前正在执行的线程对象的引用
2.2.2 代码示例
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) ;
}
}
}
public class MyThreadDemo {
public static void main ( String [ ] args) {
System . out. println ( Thread . currentThread ( ) . getName ( ) ) ;
}
}
2.3 线程优先级
2.3.1 线程调度
分时调度模型 :所有线程 轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片抢占式调度模型 :优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一 个,优先级高的线程获取的 CPU 时间片相对多一些
注意:并不是优先级最高就一定能获取到CPU时间片 Java使用的就是 抢占式调度模型 具有随机性
假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也 就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一 定的
2.3.2 相关方法
方法名 说明 inal int getPriority() 返回此线程的优先级 final void setPriority(int newPriority) 更改此线程的优先级 线程默认优先级是5;线程优先级的范围 是:1-10
2.3.3 代码示例
public class ThreadPriority extends Thread {
@Override
public void run ( ) {
for ( int i = 0 ; i < 100 ; i++ ) {
System . out. println ( getName ( ) + ":" + i) ;
}
}
}
public class ThreadPriorityDemo {
public static void main ( String [ ] args) {
ThreadPriority t1 = new ThreadPriority ( ) ;
ThreadPriority t2 = new ThreadPriority ( ) ;
ThreadPriority t3 = new ThreadPriority ( ) ;
t1. setName ( "汽车" ) ;
t2. setName ( "高铁" ) ;
t3. setName ( "飞机" ) ;
System . out. println ( t1. getPriority ( ) ) ;
System . out. println ( Thread . MAX_PRIORITY) ;
System . out. println ( Thread . MIN_PRIORITY) ;
t1. setPriority ( 10 ) ;
System . out. println ( t1. getPriority ( ) ) ;
t1. start ( ) ;
t2. start ( ) ;
t3. start ( ) ;
}
}
2.4 线程控制
2.4.1 相关方法
方法名 说明 static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数 void join() 等待这个线程死亡,再执行其他线程 void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
2.5 线程的生命周期
2.6 实现Runnable接口<方式二>
2.6.1 实现步骤
定义一个类 MyRunnable 实现 Runnable接口 在 MyRunnable类中重写 run() 方法 创建MyRunnable类的对象 创建Thread类的对象,把MyRunnable对象作为构造方法的参数 启动线程
2.6.2 Thread的构造方法
方法名 说明 Thread(Runnable target) 分配一个新的Thread对象 Thread(Runnable target, String name) 分配一个新的Thread对象
2.6.3 代码示例
public class MyRunnable implements Runnable {
@Override
public void run ( ) {
for ( int i = 0 ; i < 100 ; i++ ) {
System . out. println ( Thread . currentThread ( ) . getName ( ) + ":" + i) ;
}
}
}
public class MyRunnableDemo {
public static void main ( String [ ] args) {
MyRunnable my = new MyRunnable ( ) ;
Thread t1 = new Thread ( my, "飞机" ) ;
Thread t2 = new Thread ( my, "汽车" ) ;
t1. start ( ) ;
t2. start ( ) ;
}
}
2.6.4 优势
避免了Java单继承的局限性 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
3. 线程同步
3.1 数据安全问题
3.1.1 问题出现的条件
3.1.2 解决问题的基本思想
3.2 同步代码块
3.2.1 基本格式
synchronized ( 任意对象) {
多条语句操作共享数据的代码
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
3.2.2 优点 & 缺点
3.3 同步方法
3.3.1 基本格式
修饰符 synchronized 返回值类型 方法名( 方法参数) {
方法体;
}
修饰符 static synchronized 返回值类型 方法名( 方法参数) {
方法体;
}
3.3.2 注意事项
同步方法的锁(任意对象)为 this 同步静态方法的锁为(任意对象) 类名.class
3.4 线程安全的类
StringBuffer
Vector
从Java 2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。与新的集合实现不同, Vector被同步。如果不需要线程安全的实现,建议使用ArrayList代替Vector Hashtable
该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键或者值 从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成 员。与新的集合实现不同,Hashtable被同步。 如果不需要线程安全的实现,建议使用HashMap代替 Hashtable Collections工具类下的方法
3.5 Lock锁
3.5.1 概述
3.5.2 构造方法
方法名 说明 ReentrantLock() 创建一个ReentrantLock的实例
3.5.3 加锁 & 解锁
方法名 说明 void lock() 获得锁 void unlock() 释放锁
3.5.4 代码示例
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 ( 10 ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
System . out. println ( Thread . currentThread ( ) . getName ( ) + "正在出售第" + tickets + "张票" ) ;
tickets-- ;
}
} finally {
lock. unlock ( ) ;
}
}
}
}
public class SellTicketDemo {
public static void main ( String [ ] args) {
SellTicket st = new SellTicket ( ) ;
Thread t1 = new Thread ( st, "窗口1" ) ;
Thread t2 = new Thread ( st, "窗口2" ) ;
Thread t3 = new Thread ( st, "窗口3" ) ;
t1. start ( ) ;
t2. start ( ) ;
t3. start ( ) ;
}
}
3.5.5 注意事项
获得锁 与 释放锁 结合try - finally使用,主要是防止在上锁后操作过程中出现异常,导致释放锁无法被执行
4. 生产者消费者模式
4.1 概述
生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻
所谓生产者消费者问题,实际上主要是包含了两类线程:
一类是生产者线程用于生产数据 一类是消费者线程用于消费数据 为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为 消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为
4.2 相关方法
方法名 说明 void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法 void notify() 唤醒正在等待对象监视器的单个线程 void notifyAll() 唤醒正在等待对象监视器的所有线程
4.3 案例
奶箱类(Box):即共享区域,定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
public class Box {
private int milk;
private boolean state = false ;
public synchronized void put ( int milk) {
if ( state) {
try {
wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
this . milk = milk;
System . out. println ( "送奶工将第" + this . milk + "瓶奶放入奶箱" ) ;
state = true ;
notifyAll ( ) ;
}
public synchronized void get ( ) {
if ( ! state) {
try {
wait ( ) ;
} catch ( InterruptedException e) {
e. printStackTrace ( ) ;
}
}
System . out. println ( "用户拿到第" + this . milk + "瓶奶" ) ;
state = false ;
notifyAll ( ) ;
}
}
生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作
public class Producer implements Runnable {
private Box b;
public Producer ( Box b) {
this . b = b;
}
@Override
public void run ( ) {
for ( int i = 1 ; i <= 5 ; i++ ) {
b. put ( i) ;
}
}
}
消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
public class Customer implements Runnable {
private Box b;
public Customer ( Box b) {
this . b = b;
}
@Override
public void run ( ) {
while ( true ) {
b. get ( ) ;
}
}
}
public class BoxDemo {
public static void main ( String [ ] args) {
Box b = new Box ( ) ;
Producer p = new Producer ( b) ;
Customer c = new Customer ( b) ;
Thread t1 = new Thread ( p) ;
Thread t2 = new Thread ( c) ;
t1. start ( ) ;
t2. start ( ) ;
}
}
二、网络编程
1. 计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系 统,网络管理软件及网络通信协议的管理和协调下,实现 资源共享 和 信息传递 的计算机系统
2. 网络编程
2.1 概述
在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
2.2 三要素
IP地址 端口 协议 要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数 据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识 网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区 分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序 了。也就是应用程序的标识 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定 的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则 被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守 才能完成数据交换。常见的协议有UDP协议和TCP协议
3. IP地址
3.1 概述
3.2 分类
IPV4
是给每个连接在网络上的主机分配一个32bit地址。按照TCP/IP规定,IP地址用二进制来表示,每 个IP地址长32bit,也就是4个字节。例如一个采用二进制形式的IP地址是“11000000 10101000 00000001 01000010”,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制 的形式,中间使用符号“.”分隔不同的字节。于是,上面的IP地址可以表示为“192.168.1.66”。IP地址的这 种表示法叫做“点分十进制表示法”,这显然比1和0容易记忆得多 IPV6
由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发 紧张。为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16位一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
3.3 DOS常用命令
命令 功能 ipconfig 查看本机IP地址 ping IP地址 检查网络是否连通
3.4 特殊IP地址
127.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用(localhost)
4. InetAddress类
4.1 概述
4.2 相关方法
方法名 说明 static InetAddress getByName(String host) 确定主机名称的IP地址。主机名称可以是机器名称,也可以 是IP地址 String getHostName() 获取此IP地址的主机名 String getHostAddress() 返回文本显示中的IP地址字符串
4.3 代码示例
import java. net. InetAddress ;
import java. net. UnknownHostException ;
public class InetAddressDemo {
public static void main ( String [ ] args) throws UnknownHostException {
InetAddress byName = InetAddress . getByName ( "localhost" ) ;
System . out. println ( byName) ;
String hostName = byName. getHostName ( ) ;
String hostAddress = byName. getHostAddress ( ) ;
System . out. println ( "ip:" + hostAddress) ;
System . out. println ( "主机名:" + hostName) ;
}
}
5. 端口
5.1 概述
5.2 端口号
用两个字节表示的整数,它的取值范围是065535。其中,0 1023之间的端口号用于一些知名的网络服 务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
6. 协议
6.1 概述
计算机网络中, 连接和通信的规则被称为网络通信协议
6.2 UDP协议
用户数据报协议(User Datagram Protocol) UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台 计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在 收到数据时,也不会向发送端反馈是否收到数据。 由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输 例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太 大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在 传输重要数据时不建议使用UDP协议
6.3 TCP协议
传输控制协议 (Transmission Control Protocol) TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数 据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由 客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手” 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
第一次握手,客户端向服务器端发出连接请求,等待服务器确认 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求 第三次握手,客户端再次向服务器端发送确认信息,确认连接 完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性, TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等 四次挥手:终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开
7. UDP通信程序
7.1 概述
UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发 送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念 Java提供了DatagramSocket类作为基于UDP协议的Socket
7.2 发送数据
7.2.1 构造方法
方法名 说明 DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任 何可用端口 DatagramPacket(byte[] buf,int len,InetAddress add,int port) 创建数据包,发送长度为len的数据包到指定主机 的指定端口
7.2.2 相关方法
方法名 说明 void send(DatagramPacket p) 发送数据报包 void close() 关闭数据报套接字 void receive(DatagramPacket p) 从此套接字接受数据报包
7.2.3 步骤
创建发送端Socket对象(DatagramSocket)
创建数据,并把数据打包
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 调用DatagramSocket对象的方法发送数据
void send(DatagramPacket p) 关闭发送端
7.2.4 代码示例
import java. io. IOException ;
import java. net. * ;
public class SendDemo {
public static void main ( String [ ] args) throws IOException {
DatagramSocket ds = new DatagramSocket ( ) ;
byte [ ] bys = "hello,udp,我来了" . getBytes ( ) ;
int length = bys. length;
InetAddress address = InetAddress . getByName ( "localhost" ) ;
int port = 9999 ;
DatagramPacket dp = new DatagramPacket ( bys, length, address, port) ;
ds. send ( dp) ;
ds. close ( ) ;
}
}
7.3 接收数据
7.3.1 构造方法
方法名 说明 DatagramPacket(byte[] buf, int len) 创建一个DatagramPacket用于接收长度为len的数据包
7.3.2 相关方法
方法名 说明 byte[] getData() 返回数据缓冲区 int getLength() 返回要发送的数据的长度或接收的数据的长度
7.3.3 步骤
创建接收端Socket对象(DatagramSocket)
创建一个数据包,用于接收数据
DatagramPacket(byte[] buf, int length) 调用DatagramSocket对象的方法接收数据
void receive(DatagramPacket p) 解析数据包,并把数据在控制台显示
byte[] getData() int getLength() 关闭接收端
7.3.4 代码示例
import java. io. IOException ;
import java. net. DatagramPacket ;
import java. net. DatagramSocket ;
public class ReceivDemo {
public static void main ( String [ ] args) throws IOException {
DatagramSocket ds = new DatagramSocket ( 9999 ) ;
byte [ ] bys = new byte [ 1024 ] ;
DatagramPacket dp = new DatagramPacket ( bys, bys. length) ;
ds. receive ( dp) ;
byte [ ] dates = dp. getData ( ) ;
int len = dp. getLength ( ) ;
String dateString = new String ( dates, 0 , len) ;
System . out. println ( "数据是:" + dateString) ;
ds. close ( ) ;
}
}
7.4 案例
需求
UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束 UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收 发送端
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 SendTest {
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 ( "localhost" ) , 9999 ) ;
ds. send ( dp) ;
}
ds. close ( ) ;
}
}
import java. io. IOException ;
import java. net. DatagramPacket ;
import java. net. DatagramSocket ;
public class ReceiveTest {
public static void main ( String [ ] args) throws IOException {
DatagramSocket ds = new DatagramSocket ( 9999 ) ;
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 ( ) ) ) ;
}
}
}
8. TCP通信程序
8.1 概述
Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过 Socket产生IO流来进行网络通信 Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
8.2 发送数据
8.2.1 构造方法
方法名 说明 Socket(InetAddress address,int port) 创建流套接字并将其连接到指定IP指定端口号 Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
8.2.2 相关方法
方法名 说明 InputStream getInputStream() 返回此套接字的输入流 OutputStream getOutputStream() 返回此套接字的输出流
8.2.3 步骤
创建客户端的Socket对象(Socket)
Socket(String host, int port) 获取输出流、写数据
OutputStream getOutputStream() 释放资源
8.2.4 代码示例
import java. io. IOException ;
import java. io. OutputStream ;
import java. net. Socket ;
public class ClientDemo {
public static void main ( String [ ] args) throws IOException {
Socket s = new Socket ( "localhost" , 9999 ) ;
OutputStream os = s. getOutputStream ( ) ;
os. write ( "hello,tcp,我来了" . getBytes ( ) ) ;
s. close ( ) ;
}
}
8.3 接收数据
8.3.1 构造方法
方法名 说明 ServletSocket(int port) 创建绑定到指定端口的服务器套接字
8.3.2 相关方法
方法名 说明 Socket accept() 监听要连接到此的套接字并接受它
8.3.3 步骤
创建服务器端的Socket对象(ServerSocket)
监听客户端连接,返回一个Socket对象
获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream() 释放资源
8.3.4 代码示例
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 ( 9999 ) ;
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) ;
s. close ( ) ;
ss. close ( ) ;
}
}
8.4 注意事项
8.5 案例
8.5.1 案例 1
8.5.1.1 需求
客户端:发送数据,接收服务器反馈 服务器:接收数据,给出反馈
8.5.1.2 代码
import java. io. IOException ;
import java. io. InputStream ;
import java. io. OutputStream ;
import java. net. Socket ;
public class ClientTest {
public static void main ( String [ ] args) throws IOException {
Socket s = new Socket ( "localhost" , 9999 ) ;
OutputStream os = s. getOutputStream ( ) ;
os. write ( "这是第一个案例" . getBytes ( ) ) ;
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) ;
is. close ( ) ;
os. close ( ) ;
s. close ( ) ;
}
}
import java. io. IOException ;
import java. io. InputStream ;
import java. io. OutputStream ;
import java. net. ServerSocket ;
import java. net. Socket ;
public class ServerTest {
public static void main ( String [ ] args) throws IOException {
ServerSocket ss = new ServerSocket ( 9999 ) ;
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 ( "数据已经收到" . getBytes ( ) ) ;
ss. close ( ) ;
}
}
8.5.2 案例 2
8.5.2.1 需求
客户端:数据来自键盘录入,知道输入的数据是886,发送数据结束 服务器:接收到的数据在控制台输出
8.5.2.2 代码
import java. io. * ;
import java. net. Socket ;
public class ClientTest {
public static void main ( String [ ] args) throws IOException {
Socket s = new Socket ( "localhost" , 9999 ) ;
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 ( ) ;
}
bw. close ( ) ;
br. close ( ) ;
s. close ( ) ;
}
}
import java. io. * ;
import java. net. ServerSocket ;
import java. net. Socket ;
public class ServerTest {
public static void main ( String [ ] args) throws IOException {
ServerSocket ss = new ServerSocket ( 9999 ) ;
Socket s = ss. accept ( ) ;
BufferedReader br = new BufferedReader ( new InputStreamReader ( s. getInputStream ( ) ) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
System . out. println ( line) ;
}
br. close ( ) ;
s. close ( ) ;
ss. close ( ) ;
}
}
8.5.3 案例 3
8.5.3.1 需求
客户端:数据来自键盘录入,知道输入的数据是886,发送数据结束 服务器:接收数据,将接收到的数据写入文本文档
8.5.3.2 代码
import java. io. * ;
import java. net. Socket ;
public class ClientTest {
public static void main ( String [ ] args) throws IOException {
Socket s = new Socket ( "localhost" , 9999 ) ;
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 ( ) ;
}
}
import java. io. * ;
import java. net. ServerSocket ;
import java. net. Socket ;
public class ServerTest {
public static void main ( String [ ] args) throws IOException {
ServerSocket ss = new ServerSocket ( 9999 ) ;
Socket s = ss. accept ( ) ;
BufferedReader br = new BufferedReader ( new InputStreamReader ( s. getInputStream ( ) ) ) ;
BufferedWriter bw = new BufferedWriter ( new FileWriter ( "demo\\s.txt" ) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
bw. write ( line) ;
bw. newLine ( ) ;
bw. flush ( ) ;
}
bw. close ( ) ;
br. close ( ) ;
ss. close ( ) ;
}
}
8.5.4 案例 4
8.5.4.1 需求
客户端:数据来自于文本文件 服务器:接收到的数据写入文本文件
8.5.4.2 代码
import java. io. * ;
import java. net. Socket ;
public class ClientTest {
public static void main ( String [ ] args) throws IOException {
Socket s = new Socket ( "localhost" , 9999 ) ;
BufferedReader br = new BufferedReader ( new FileReader ( "demo\\src\\com\\wang\\tcptest\\ClientTest.java" ) ) ;
BufferedWriter bw = new BufferedWriter ( new OutputStreamWriter ( s. getOutputStream ( ) ) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
bw. write ( line) ;
bw. newLine ( ) ;
bw. flush ( ) ;
}
br. close ( ) ;
s. close ( ) ;
}
}
import java. io. * ;
import java. net. ServerSocket ;
import java. net. Socket ;
public class ServerTest {
public static void main ( String [ ] args) throws IOException {
ServerSocket ss = new ServerSocket ( 9999 ) ;
Socket s = ss. accept ( ) ;
BufferedReader br = new BufferedReader ( new InputStreamReader ( s. getInputStream ( ) ) ) ;
BufferedWriter bw = new BufferedWriter ( new FileWriter ( "demo\\s.txt" ) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
bw. write ( line) ;
bw. newLine ( ) ;
bw. flush ( ) ;
}
bw. close ( ) ;
ss. close ( ) ;
}
}
8.5.5 案例 5
8.5.5.1 需求
客户端:数据来自于文本文件,接收服务器反馈 服务器:接收到的数据写入文本文件,给出反馈
8.5.5.2 代码
import java. io. * ;
import java. net. Socket ;
public class ClientTest {
public static void main ( String [ ] args) throws IOException {
Socket s = new Socket ( "localhost" , 9999 ) ;
BufferedReader br = new BufferedReader ( new FileReader ( "demo\\src\\com\\wang\\tcpdemo\\ClientDemo.java" ) ) ;
BufferedWriter bw = new BufferedWriter ( new OutputStreamWriter ( s. getOutputStream ( ) ) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
bw. write ( line) ;
bw. newLine ( ) ;
bw. flush ( ) ;
}
s. shutdownOutput ( ) ;
BufferedReader brClient = new BufferedReader ( new InputStreamReader ( s. getInputStream ( ) ) ) ;
String data = brClient. readLine ( ) ;
System . out. println ( data) ;
}
}
import java. io. * ;
import java. net. ServerSocket ;
import java. net. Socket ;
public class ServerTest {
public static void main ( String [ ] args) throws IOException {
ServerSocket ss = new ServerSocket ( 9999 ) ;
Socket s = ss. accept ( ) ;
BufferedReader br = new BufferedReader ( new InputStreamReader ( s. getInputStream ( ) ) ) ;
BufferedWriter bw = new BufferedWriter ( new FileWriter ( "demo\\ss.txt" ) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
bw. write ( line) ;
bw. newLine ( ) ;
bw. flush ( ) ;
}
BufferedWriter bwServer = new BufferedWriter ( new OutputStreamWriter ( s. getOutputStream ( ) ) ) ;
bwServer. write ( "文件上传成功" ) ;
bwServer. newLine ( ) ;
bwServer. flush ( ) ;
}
}
8.5.6 案例 6
8.5.6.1 需求
客户端:数据来自于文本文件,接收服务器反馈 服务器:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程 多线程类:在run()方法中读取客户端发送的数据,并且用计数器给文件名编号防止文件重名,接受结束后给客户端反馈信息
8.5.6.2 代码
import java. io. * ;
import java. net. Socket ;
public class ClientTest {
public static void main ( String [ ] args) throws IOException {
Socket s = new Socket ( "localhost" , 9999 ) ;
BufferedReader br = new BufferedReader ( new FileReader ( "demo\\src\\com\\wang\\tcptest\\ClientTest.java" ) ) ;
BufferedWriter bw = new BufferedWriter ( new OutputStreamWriter ( s. getOutputStream ( ) ) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
bw. write ( line) ;
bw. newLine ( ) ;
bw. flush ( ) ;
}
s. shutdownOutput ( ) ;
BufferedReader bwServer = new BufferedReader ( new InputStreamReader ( s. getInputStream ( ) ) ) ;
String data = bwServer. readLine ( ) ;
System . out. println ( data) ;
bw. close ( ) ;
bwServer. close ( ) ;
s. close ( ) ;
}
}
import java. io. IOException ;
import java. net. ServerSocket ;
import java. net. Socket ;
public class ServerTest {
public static void main ( String [ ] args) throws IOException {
ServerSocket ss = new ServerSocket ( 9999 ) ;
while ( true ) {
Socket s = ss. accept ( ) ;
new Thread ( new ServerThread ( s) ) . start ( ) ;
}
}
}
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 ( "demo\\sss[" + count+ "].txt" ) ;
while ( file. exists ( ) ) {
count++ ;
file = new File ( "demo\\sss[" + count+ "].txt" ) ;
}
BufferedWriter bw = new BufferedWriter ( new FileWriter ( file) ) ;
String line;
while ( ( line = br. readLine ( ) ) != null ) {
bw. write ( line) ;
bw. newLine ( ) ;
bw. flush ( ) ;
}
BufferedWriter bwClient = new BufferedWriter ( new OutputStreamWriter ( s. getOutputStream ( ) ) ) ;
bwClient. write ( "文件上传成功" ) ;
bwClient. newLine ( ) ;
bwClient. flush ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
8.5.7 总结
问题
在案例 5 中,出现了程序一直等待的问题,,服务器端一直在等待接收数据,客户端一直在等待服务器端的反馈数据 原因
解决
自定义结束标记;
在客户端定义一个标记,表示客户端完成了写数据 如果服务器端收到886这个标记,就开始读数据 缺点:如果文本中有这个标记,会导致标记后的内容丢失 使用shutdownOutput()方法 扩展方法
方法名 说明 shutdownOutput() 禁用此套接字的输出流。 shutdownInput() 将此套接字的输入流放置在“流的末尾”。
参考视频:黑马程序员全套Java教程