进程和线程
57.实现多线程
1.进程和线程概念
进程:
-
是正在运行的程序
-
是系统进行资源分配粕调用的独立单位
-
每一个进程都有它自己的内存空间和系统资源
线程
线程: 是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程如果只有条执行路径,则称为单线程程序(记事本)
- 多线程:一个进程如果有多条执行路径,则称为多线程程序(扫雷游戏)
2.多线程的实现方式(方式1 继承Thread类 )
- 方式1:继承Thread类
- 定义一个类MyThread
- 继承Thread类在MyThread类中重写run()方法
- 创建MyThread类的对象
- 启动线程
Q1:为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
Q2:run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run()方法
package Threads;
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(i);
}
}
}
package Threads;
public class MyStreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
//start() 导致线程开始执行;java虚拟机调用此线程run方法
m1.start();
m2.start();
}
}
m1和m2同时执行
3.设置和获取线程名称
ffm | sm |
---|---|
void setName(Stringname): | 将此线程的名称更改为等于参数 |
nameString getName(): | 返回此线程的名称 |
static Thread currentThread() | 返回当前正在执行的线程对象的名称 |
//nameString getName():|返回此线程的名称
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(getName()+":"+i);
}
}
}
package Threads;
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(getName()+":"+i);
}
}
}
package Threads;
public class MyStreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
//start() 导致线程开始执行;java虚拟机调用此线程run方法
m1.setName("1号线:");
m2.setName("2号线:");
m1.start();
m2.start();
}
}
System.out.println(Thread.currentThread().getName()); //main
4.线程调度
线程有两种调度模型
- 分时调度模型: 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占式调度模型: 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些
Java使用的是抢占式调度模型
假如计算机只有一个CPU,那么CPU在某一个时刻只能执行─条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的。
Thread类中设置和获取线程优先级的方法
ffm | sm |
---|---|
public final int getPriority(): | 返回此线程的优先级 |
public final void setPriority(int newPriority): | 更改此线程的优先级 |
//修改线程优先级的时候注意不能超过线程规定的优先级的范围
//查询线程优先级范围
System.out.println(Thread.MAX_PRIORITY); //10
System.out.println(Thread.MIN_PRIORITY); //1
查看线程优先级
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
//start() 导致线程开始执行;java虚拟机调用此线程run方法
m1.setName("1号线:");
m2.setName("2号线:");
System.out.println(m1.getPriority()); //5
System.out.println(m2.getPriority()); //5
//设置线程优先级
m1.setPriority(9);
m2.setPriority(1);
5.线程控制
方法名 | 说明 |
---|---|
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
void join() | 等待这个线程死亡 |
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出 |
//sleep方法可以让多线程的运行更平衡
package Threads;
public class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(getName()+":"+i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
jion方法:让调用它的方法执行完毕之后才可以执行其他的方法
m1.start();
m1.join();
m2.start();
void setDaemon(boolean on):设置守护线程
当主线程运行完毕的时候守护线程也停止运行
6.线程生命周期
7.多线程的实现方式(方式2 实现接口)
方式2:实现Runnable接口
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数启动线程
相比继承Thread类,实现Runnable接口的好处
- 避免了Java单继承的局限性
- 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
package Threads;
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
//这里不能直接拿到正在运行线程的名称 需要先调用Thread类中的当前线程再获取名字
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
package Threads;
public class MyRunnableDemo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
//创建Thread类对象,把MyRunnable对象作为构造方法的参数
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.start();
t2.start();
}
}
给线程起名字:
58.线程同步
1.案例:卖票
需求:
某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
思路:
- 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量: private int tickets = 100;
- 在SellTicket类中重写run()方法实现卖票,代码步骤如下:
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:卖了票之后,总票数要减1
C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行 - 定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下
A:创建SellTicket类的对象
B:创建三个Thread类的对象,把SelTicket对象作为构造方法的参数,并给出对应的窗口名称
C:启动线程
package Threads;
import static java.lang.Thread.sleep;
public class SellTickets implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+ticket+"张");
ticket--;
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
package Threads;
public class SellDoor {
public static void main(String[] args) {
SellTickets sellTickets = new SellTickets();
Thread d1 = new Thread(sellTickets, "d1");
Thread d2 = new Thread(sellTickets, "d2");
Thread d3 = new Thread(sellTickets, "d3");
d1.start();
d2.start();
d3.start();
}
}
2.卖票案例数据安全问题的解决
为什么出现问题?(这也是我们判断多线程程序是否会有数据安全问题的标准
- 是否是多线程环境
- 是否有共享数据
- 是否有多条语句操作共享数据
如何解决多线程安全问题呢?
- 基本思想:让程序没有安全问题的环境
怎么实现呢?
- 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
2.1同步代码块
锁多条语句操作共享数据,可以使用同步代码块实现
格式:
synchronized(任意对象){
多条语句操作共享数据的代码
)
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
package Threads;
import static java.lang.Thread.sleep;
public class SellTickets implements Runnable{
private int ticket = 100;
//定义一把锁
Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张");
ticket--;
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
这样就可以有序愉快的卖票了
3.同步方法
同步方法:就是把synchronized关键字加到方法上
格式:
修饰符synchronized返回值类型方法名(方法参数){}
同步方法的锁对象是什么呢?
- this
- 同步静态方法:就是把synchronized关键字加到静态方法上
格式:
修饰符static synchronized返回值类型方法名(方法参数){}
同步静态方法的锁对象是什么呢?
- 类名.class
4.线程安全的类
StringBuffer
- 线程安全,可变的字符序列
- 从版本JDK 5开始,被StringBuilder替代。通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步
Vector
- 从Java 2平台v1.2开始,该类改进了List接口,使其成为- JavaCollectionsFramework的成员。与新的集合实现不同,Vector被同步。如果不需要线程安全的实现,建议使用ArrayList代替Vector
Hashtable
- 该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或者值
- 从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为JavaCollections Framework的成员。与新的集合实现不同,Hashtable被同步。如果不需要线程安全的实现,建议使用HashMap代替Hashtable
5. Lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock;
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
- void lock():获得锁
- void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
- ReentrantLock的构造方法
- ,ReentrantLock():创建一个ReentrantLock的实例
package Threads;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.lang.Thread.sleep;
public class SellTickets implements Runnable {
private int ticket = 100;
//定义一把锁
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//加锁
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张");
ticket--;
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//放锁
lock.unlock();
}
}
}
为了保证即使程序出现问题也能正常释放锁,通常采用try-finally来释放锁
@Override
public void run() {
while (true) {
try {
//加锁
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "张");
ticket--;
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
//放锁
lock.unlock();
}
}
59.生产者和消费者
1.概述
所谓生产者消费者问题,实际上主要是包含了两类线程:
- 一类是生产者线程用于生产数据
- 一类是消费者线程用于消费数据
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为
为了体现生产和消费过程中的等待和唤醒,Java就提供了几个方法供我们使用,这几个方法在Object类中Object类的等待和唤醒方法:
方法名 | 说明 |
---|---|
void wait() | 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法 |
void notify0 | 唤醒正在等待对象监视器的单个线程 |
void notifyAll( | 唤醒正在等待对象监视器的所有线程 |
2.生产者和消费者案例
生产者消费者案例中包含的类:
- 奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
- 生产者类(Producer):实现Runnable接口,重写run)方法,调用存储牛奶的操作
- 消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶
- 操作测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下
- 创建奶箱对象,这是共享数据区域
- 创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
- 创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
- 创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
- 启动线程
package Threads;
public class Box {
//表示第x瓶奶
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();
}
}
this.milk = milk;
System.out.println("消费者将第"+this.milk+"拿出奶箱");
state = false;
notifyAll();
}
}
package Threads;
public class Producer implements Runnable {
private Box b;
public Producer(Box b) {
this.b=b;
}
@Override
//生产者生产了5瓶奶
public void run() {
for (int i = 1; i <= 5; i++) {
b.put(i);
}
}
}
package Threads;
public class Customer implements Runnable{
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while (true){
b.get();
}
}
}
package Threads;
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();
}
}
网络编程
60. 网络编程入门
1.概述
计算机网络
是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
网络编程
在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
2.网络编程的三要素
- IP地址: 设备标识
- 端口: 应用标识
- 协议: 通信规则
3.IP地址的两大类
- IPV4
给每个网络上的主机分配一个32位的地址(4字节);
表示:二进制或点分十进制 - IPV6
128位地址(16字节)
表示:冒分十六进制
常用命令:
- ipconfig:产看本机IP地址;
- ping IP:检查网络是否畅通;
特殊的ip地址:
- 127.0.0.1 :回送地址 代表本机
4 InetAddress的使用
为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress供我们使用
InetAddress:此类表示Internet协议(IP)地址
方法名 | 说明 |
---|---|
static InetAddress getByName(String host) | 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 |
String getHostName) | 获取此IP地址的主机名 |
String getHostAddress() | 返回文本显示中的IP地址字符串 |
可以在设置中找到自己主机的名字
package IP;
import com.sun.jmx.snmp.InetAddressAcl;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IPDemo {
public static void main(String[] args) throws UnknownHostException {
InetAddress address = InetAddress.getByName("XTZJ-20220709ZX");
String hostName = address.getHostName();
String hostAddress = address.getHostAddress();
System.out.println(hostName);
System.out.println(hostAddress);
}
}
5.端口
- 端口: 设备上应用程序的唯一标识
- 端口号: 用两个字节表示的整数,它的取值范围是0-65535。
其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
6.协议
协议: 计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议
- 用户数据报协议(User Datagram Protocol)
- UDP是无连接通信协议
即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。 - 由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输
- 由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议
TCP协议
- 传输控制协议(Transmission Control Protocol)
- TCP协议是面向连接的通信协议
-即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过
“三次握手”
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
- 第1次握手,客户端向服务器端发出连接请求,等待服务器确认
- 第2次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
- 第3次握手,客户端再次向服务器端发送确认信息,确认连接
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等
61.UDP通信程序
1. UDP发送数据发送数据的步骤
- 创建发送端的Socket对象(DatagramSocket)
- 创建数据,并把数据打包
- 调用DatagramSocket对象的方法发送数据
- 关闭发送端
-
创建发送端的Socket对象(DatagramSocket)
-
创建数据,并把数据打包
DatagramPacket(byte[] buf, int length, InetAddress address, int port) -
调用DatagramSocket对象的方法发送数据
void send(DatagramPacket p) -
关闭发送端 void close()
package UDP;
import java.io.IOException;
import java.net.*;
import java.nio.charset.StandardCharsets;
//udp发送步骤
public class UDPDemo {
public static void main(String[] args) throws IOException {
//创建Socket对象
DatagramSocket ds = new DatagramSocket();
//创建数据并打包
//构建一个数据包 发送长度为length 选择指定主机的指定端口
byte[] bytes = "hello UDP".getBytes(StandardCharsets.UTF_8);
int length = bytes.length;
InetAddress address = InetAddress.getByName("192.168.1.11");
int port = 10086;
DatagramPacket dp = new DatagramPacket(bytes, length, address, port);
//调用DatagrameSocket对象的方法发送数据
ds.send(dp);
//关闭发送端
ds.close();
}
}
2.UDP接收数据接收数据的步骤
- 创建接收端的Socket对象(DatagramSocket)
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法接收数据
- 解析数据包,并把数据在控制台显示
- 关闭接收端
接收数据的步骤
- 创建接收端的Socket对象(DatagramSocket) DatagramSocket(int port)
- 创建一个数据包,用于接收数据DatagramPacket(byte[] buf, int length)
- 调用DatagramSocket对象的方法接收数据
void receive(DatagramPacket p)解析数据包,并把数据在控制台显示
bytel] getData()
int getLength() - 关闭接收端
package UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UDPDemo1 {
public static void main(String[] args) throws IOException {
//创建接收端 DatagramSocket(10086):构造数据报套接字并将其绑定到指定端口
DatagramSocket ds = new DatagramSocket(10086);
//创建数据包,用于接收数据
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//调用对象方法来接受数据
ds.receive(dp);
//解析数据包 并显示
byte[] datas = new byte[1024];
String dataString = new String(datas,0,dp.getLength());
System.out.println(dataString);
//关闭接收端
ds.close();
}
}
3 UDP通信程序练习
需求:
- UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
- UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
package 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;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
public class SentDemo {
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[] bytes = line.getBytes(StandardCharsets.UTF_8);
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.1.11"),12345);
//调用方法发送数据
ds.send(dp);
}
}
}
package UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建接受对象
DatagramSocket ds = new DatagramSocket(12345);
while (true) {
//创建一个数据包用于接受对象
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes, bytes.length);
//调用方法接受数据
ds.receive(dp);
//解析数据并显示
System.out.println("data:" + new String(dp.getData(), 0, dp.getLength()));
}
}
}
62 TCP通信程序
1.TCP通信原理
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信;
Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信Java为客户端提供了Socket类,为服务器端提供了ServerSocket类;
2. TCP发送数据发送数据的步骤
- 创建客户端的Socket对象(Socket)
- 获取输出流,写数据
- 释放资源
package TCP;
//创建客户端发送数据
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket s = new Socket("192.168.1.11", 10000);
//获取输出流写对象
OutputStream os = s.getOutputStream();
os.write("nihao".getBytes(StandardCharsets.UTF_8));
//释放资源
s.close();
}
}
3. TCP接受对象
- 创建服务器端的Socket对象(ServerSocket)
- 获取输入流,读数据,并把数据显示在控制台
- 释放资源
package TCP;
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 {
//- 创建服务器端的Socket对象(ServerSocket)
ServerSocket ss = new ServerSocket(10000);
//倾听要连接此套接字并接受
Socket accept = ss.accept();
//- 获取输入流,读数据,并把数据显示在控制台
InputStream is = accept.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String data = new String(bytes, 0, len);
System.out.println("data:"+data);
//- 释放资源
ss.close();
accept.close();
}
}
4 TCP通信程序练习
练习1
- 客户端:发送数据,接收服务器反馈
- 服务器:接收数据,给出反馈
package TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Client1 {
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket s = new Socket("192.168.1.11",10001);
//写数据
OutputStream os = s.getOutputStream();
os.write("练习1".getBytes(StandardCharsets.UTF_8));
//接受服务器的反馈
InputStream is = s.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String data = new String(bytes, 0, len);
System.out.println("客户端:"+data);
s.close();
}
}
package TCP;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
public class Server1 {
public static void main(String[] args) throws IOException {
//创建服务器对象
ServerSocket ss = new ServerSocket(10001);
//监听客户端连接 返回一个Socket对象
Socket accept = ss.accept();
//获取数据流,读数据并把数据输出到控制tai
InputStream is = accept.getInputStream();
byte[] bytes = new byte[1024];
int len = is.read(bytes);
String data = new String(bytes, 0, len);
System.out.println("服务器:"+data);
//给出反馈
OutputStream os = accept.getOutputStream();
os.write("服务器已收到!".getBytes(StandardCharsets.UTF_8));
ss.close();
}
}
练习2:
- 客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
- 服务器:接收到的数据在控制台输出
package TCP;
import java.io.*;
import java.net.Socket;
public class Client2 {
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket s = new Socket("192.168.1.11",10002);
//写数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//封装输出流对象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line = null;
while ((line=br.readLine())!=null){
if("886".equals(line)){
break;
}
//获取输出流对象
// OutputStream os = s.getOutputStream();
// os.write(line.getBytes(StandardCharsets.UTF_8));
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
}
}
package TCP;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
public class Server2 {
public static void main(String[] args) throws IOException {
//创建服务器接受对象
ServerSocket ss = new ServerSocket(10002);
//监听客户端的链接,返回对应的Socket对象
Socket accept = ss.accept();
//获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
String line;
while ((line=br.readLine())!=null){
System.out.println("服务器收到数据"+line);
}
ss.close();
}
}
练习3
- 客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
- 服务器:接收到的数据写入文本文件
package TCP;
import java.io.*;
import java.net.Socket;
public class Client3 {
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket s = new Socket("192.168.1.11", 12346);
//写数据从键盘录入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//封装数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line= null;
while ((line=br.readLine())!=null){
if("886".equals(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
}
}
package TCP;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server3 {
public static void main(String[] args) throws IOException {
//创建服务器端对象
ServerSocket ss = new ServerSocket(12346);
//监听数据
Socket accept = ss.accept();
//获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
//chuangj文件
BufferedWriter bw = new BufferedWriter(new FileWriter("cw.txt"));
String line;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
ss.close();
bw.close();
}
}
练习4:
- 客户端:数据来自于文本文件
- 服务器:接收到的数据写入文本文件
package TCP;
import java.io.*;
import java.net.Socket;
public class Client4 {
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket s = new Socket("192.168.1.2", 10005);
//封装文本文件数据
BufferedReader br = new BufferedReader(new FileReader("exercise4.txt"));
//封装输出流写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.close();
}
// br.close();
// s.close();
}
}
package TCP;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server4 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10005);
//监听数据
Socket accept = ss.accept();
//获取输入流
BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
//创建文件夹并写入
BufferedWriter bw = new BufferedWriter(new FileWriter("copy4.txt"));
String line;
while ((line= br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
bw.close();
ss.close();
}
}
练习5:
- 客户端:数据来自于文本文件,接收端服务器反馈
- 服务器:接收到的数据写入文本文件,给出反馈
package TCP;
import java.io.*;
import java.net.Socket;
public class Client5 {
public static void main(String[] args) throws IOException {
Socket s = new Socket("192.168.1.2", 10006);
//获取文本文件
BufferedReader br = new BufferedReader(new FileReader("exercise4.txt"));
//封装输出流写文本文件
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line;
while ((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
//自定义接受标记
bw.write("886");
bw.newLine();
bw.flush();
// 接收反馈反馈
BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
String fk = brClient.readLine();
System.out.println("客户端收到fank:"+fk);
s.close();
bw.close();
}
}
package TCP;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class Server5 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10006);
Socket accept = ss.accept();
//接受输入流
BufferedReader br = new BufferedReader(new InputStreamReader(accept.getInputStream()));
//用字符流写入文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("copy5.txt"));
String line;
while ((line=br.readLine())!=null){
if ("886".equals(line)){
break;
}
bw.write(line);
bw.newLine();
bw.flush();
}
//给出反馈
BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bwServer.write("服务器已接受!");
bwServer.newLine();
bwServer.flush();
ss.close();
}
}
系统判断结束标记
- public void shutdownOutput ()
练习6:
- 客户端:数据来自于文本文件,接收端服务器反馈
- 服务器:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程
package TCP;
import java.io.*;
import java.net.Socket;
public class Client6 {
public static void main(String[] args) throws IOException {
Socket s = new Socket("192.168.1.2", 10008);
//读取数据
BufferedReader br = new BufferedReader(new FileReader("exercise4.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();
BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
String data = brClient.readLine();
System.out.println("客户端收到反馈:"+data);
s.close();
}
}
package TCP;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server6 {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10008);
Socket accept = ss.accept();
while (true) {
//为每一个客户开启一个线程
new Thread(new ServerThread(accept)).start();
}
}
}
package TCP;
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()));
BufferedWriter bw = new BufferedWriter(new FileWriter("copyy.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();
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}