一、知识准备
二、TCP编程
1.TCP客户端步骤
package day23.tcp1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws IOException {
//1.
//这行代码执行成功,说明客户端创建成功
//说明客户端和服务器端连接成功,实际上就是这个socket对象
//Socket中既有字输入流,也有字节输出流
Socket socket = new Socket(InetAddress.getByName("10.20.152.10"), 11111);
//2.
//向服务器端发送数据,也就是使用字节输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,你好".getBytes());
//3.
//接收数据
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[50];
int length = inputStream.read(bytes);
System.out.println(new String(bytes,0,length));
//4.
//关闭socket
socket.close();
}
}
2.TCP服务器端步骤
服务器端向和哪个客户通信,需要得到客户端的Socket对象
package day23.tcp1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
//1.
//创建ServerSocket对象的同时,监听端口
ServerSocket serverSocket = new ServerSocket(11111);
//2.
//服务器端需要得到一个客户端是Socket对象,从而保证和客户端使用相同的通道(Socket流)
Socket socket = serverSocket.accept();
System.out.print(InetAddress.getByName("10.20.152.10")+ ":");
//3.
//接收客户端发送的数据
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[50];
int length = inputStream.read(bytes);
System.out.println(new String(bytes,0,length));
//4.
//给客户端发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write("我已经收到信息!谢谢".getBytes());
socket.close();
}
}
3.运行结果:
4.总结
4.练习
(1)大小写转换
客户端
public class ChangeClient {
/*
实现一个大写转换服务器:客户端读取键盘输入的小写字符串,发送给服务器端
服务器端读取客户端发送的小写字符串,转成大写,再发给客户端
客户端:
1.读取键盘输入的小写字符串
2.把小写字符串发送给服务器端
3.接收服务器端发送过来的大写字符串
*/
public static void main(String[] args) throws IOException {
System.out.println("客户端启动了......");
//创建客户端对象
Socket socket=new Socket(InetAddress.getByName("10.20.152.61"),17878);
//创建读取键盘输入的小写字符串的字符缓冲读取流
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
//创建把小写字符串发送给服务器端的字符输出流
OutputStream out = socket.getOutputStream();
PrintWriter prinWriter=new PrintWriter(out,true);
//创建接收服务器端发送过来的大写字符串的字符输入流
InputStream in = socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(in));
//反复读取键盘输入的数据,发送给服务端
String line=null;
while ((line=bufferedReader.readLine())!=null)
{
if("over".equals(line))
break;
//把数据发送给服务端
prinWriter.println(line);
//读取服务器返回的大写数据
System.out.println(br.readLine());
}
bufferedReader.close();
socket.close();
}
}
服务端
public class ChangeServer {
/*
实现一个大写转换服务器:客户端读取键盘输入的小写字符串,发送给服务器端
服务器端读取客户端发送的小写字符串,转成大写,再发给客户端
服务端:
1.读取客户端发送的小写字符串
2:把大写字符串发送给客户端
*/
public static void main(String[] args) throws IOException {
System.out.println("服务端启动了。。。。");
//创建服务端对象
ServerSocket serverSocket=new ServerSocket(17878);
//得到客户端对象
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress().getHostAddress()+":");
//读取客户端发送的小写字符串
InputStream in = socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(in));
//把大写字符串发送给客户端
OutputStream out = socket.getOutputStream();
PrintWriter pw=new PrintWriter(out,true);
//反复读取客户端发送过来的数据
String line=null;
while((line=br.readLine())!=null){
pw.println(line.toUpperCase());
}
//socket.close();
}
}
(2)文本文件的上传
客户端
public class UploadClient {
//实现文本文件的上传:把文件从客户端上传到服务端,上传完成时,服务端返回"上传成功",客户端显示 “上传成功”
/*
1。客户端读取本地文件
2:把读取的本地文件的内容发送到服务端
3.读取服务端返回的 上传成功
*/
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket socket=new Socket(InetAddress.getByName("10.20.152.61"),18989);
//创建读取本地文件的字符读取流
BufferedReader bufferedReader=new BufferedReader(new FileReader("files\\temp.java"));
//把读取的本地文件的内容发送到服务端,创建字符输出流
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter=new PrintWriter(outputStream,true);
//读取服务端返回的 上传成功,创建字符输入流
InputStream inputStream = socket.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(inputStream));
//循环读取本地文件,发送到服务端
String line=null;
while((line=bufferedReader.readLine())!=null)
{
printWriter.println(line);
}
socket.shutdownOutput();//给服务端一个结束标记
bufferedReader.close();
//读取服务端返回的 上传成功
String result = br.readLine();
System.out.println(result);
socket.close();
}
}
服务端
public class UploadServer {
//实现文本文件的上传:把文件从客户端上传到服务端,上传完成时,服务端返回"上传成功",客户端显示 “上传成功”
/*
1;接收客户端发送的数据
2:把数据写到服务端的某个文件中
3:给客户端发送 "上传成功"
*/
public static void main(String[] args) throws IOException {
//创建服务端对象
ServerSocket serverSocket=new ServerSocket(18989);
//得到客户端对象
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress().getHostAddress()+":");
//创建接收客户端发送的数据的字符读取流
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
//把数据写到服务端的某个文件中,创建文件输出流
BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter("files\\temp_copy.java"));
//给客户端发送 "上传成功"
OutputStream outputStream = socket.getOutputStream();
PrintWriter printWriter=new PrintWriter(outputStream,true);
//反复读取客户端发送的数据,写入到服务端文件
String line=null;
while((line=bufferedReader.readLine())!=null)//循环读取的是客户端,所以读不到null
{
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
bufferedWriter.close();
printWriter.println("上传成功");//循环没结束,所以没能发送"上传成功"
socket.close();
serverSocket.close();
}
}
注意这题客户端应当加入结束标记socket.shutdownOutput(),因为流的发送不可能发送null过去,服务端不能接收到null。
1.在客户端或者服务端通过socket.shutdownOutput()都是单向关闭的,即关闭客户端的输出流并不会关闭服务端的输出流,所以是一种单方向的关闭流;
2.通过socket.shutdownOutput()关闭输出流,但socket仍然是连接状态,连接并未关闭
3.如果直接关闭输入或者输出流,即:in.close()或者out.close(),会直接关闭socket
(3)图片文件上传
客户端
public class UploadPicClient {
public static void main(String[] args) throws IOException {
Socket socket=new Socket(InetAddress.getByName("10.20.152.61"),19999);
//创建读取本地图片的字节输入流
FileInputStream fileInputStream=new FileInputStream("images\\33.jpg");
//创建向服务端发送数据的字节输出流
OutputStream outputStream = socket.getOutputStream();
//创建读取服务端返回数据的字节输入流
InputStream inputStream = socket.getInputStream();
//反复读取本地文件,发送到服务端
byte[] arr=new byte[1024];
int num=0;
while((num=fileInputStream.read(arr))!=-1)
{
outputStream.write(arr,0,num);
}
socket.shutdownOutput();//写入结束标记
fileInputStream.close();
byte[] b=new byte[20];
num = inputStream.read(b);
System.out.println(new String(b,0,num));
socket.close();
}
}
服务端
public class UploadPicServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(19999);
Socket socket = serverSocket.accept();
System.out.println(socket.getInetAddress().getHostAddress()+":");
//创建读取客户端数据的输入流
InputStream inputStream = socket.getInputStream();
//创建写入到本地文件的字节输出流
FileOutputStream fileOutputStream=new FileOutputStream("images\\33_copy.jpg");
//创建发送数据字节输出流
OutputStream outputStream = socket.getOutputStream();
//反复读取客户端,写入到本地文件
byte[] arr=new byte[1024];
int num=0;
while((num=inputStream.read(arr))!=-1)
{
fileOutputStream.write(arr,0,num);
}
fileOutputStream.close();
outputStream.write("上传成功".getBytes());
socket.close();
serverSocket.close();
}
}
(4)多线程上传图片
客户端
public class UploadPicClient {
public static void main(String[] args) throws IOException {
Socket socket=new Socket(InetAddress.getByName("10.20.152.61"),19999);
//创建读取本地图片的字节输入流
FileInputStream fileInputStream=new FileInputStream("images\\33.jpg");
//创建向服务端发送数据的字节输出流
OutputStream outputStream = socket.getOutputStream();
//创建读取服务端返回数据的字节输入流
InputStream inputStream = socket.getInputStream();
//反复读取本地文件,发送到服务端
byte[] arr=new byte[1024];
int num=0;
while((num=fileInputStream.read(arr))!=-1)
{
outputStream.write(arr,0,num);
}
socket.shutdownOutput();//写入结束标记
fileInputStream.close();
byte[] b=new byte[20];
num = inputStream.read(b);
System.out.println(new String(b,0,num));
socket.close();
}
}
服务端
服务端分为两个文件,多线程接收文件
public class UploadPicture implements Runnable {
private Socket socket;
UploadPicture(Socket socket)
{
this.socket=socket;
}
@Override
public void run() {
String ip = socket.getInetAddress().getHostAddress();
System.out.println(ip+":");
//上传的文件统一放在一个目录下
File dir=new File("f:\\files");
if(!dir.exists())
dir.mkdir();
int num=0;
File file=new File(dir,ip+"("+num+")"+".jpg");
while(file.exists()){
file=new File(dir,ip+"("+(++num)+")"+".jpg");
}
try {
InputStream inputStream = socket.getInputStream();//读取客户端数据的输入流
FileOutputStream fileOutputStream=new FileOutputStream(file);
OutputStream outputStream = socket.getOutputStream();
byte[] arr=new byte[1024];
int len=0;
while((len=inputStream.read(arr))!=-1)
{
fileOutputStream.write(arr,0,len);
}
fileOutputStream.close();
outputStream.write("上传成功".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Program {
public static void main(String[] args) throws IOException {
//创建服务端对象
ServerSocket serverSocket=new ServerSocket(19999);
while(true) {
Socket socket = serverSocket.accept();
new Thread(new UploadPicture(socket)).start();
}
}
}
三、Stream类
集合的流式编程: jdk1.8 出现的,多数需要结合着lambda使用。目的是简化代码
-
Collection
-
Stream<E> stream()
-
数组
-
Stream<T> stream(T[] array)
- JDK8加入 了 java.util.stream包,实现了集合的流式操作,流式操作包括集合的过滤,排序,映射等功能。根据流的操作性,又可以分为串行流 和 并行流。
- 根据操作返回的结果不同,流式操作又分为中间操作和最终操作。大大方便了我们对于集合的操作。
- Stream 不是集合元素,也不是数据结构,它相当于一个高级版本的 Iterator,不可以重复遍历里面的数据,像水一样,流过了就一去不复返。它和普通的 Iterator 不同的是,它可以并行遍历,
- 普通的 Iterator 只能是串行,在一个线程中执行。
1.最终操作
public class Demo1 {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
Collections.addAll(list,10,30,20,50,40);
//获取流的方式,流中是集合中的数据,通过流来方便操作数据,集合中的数据本身是没有改变的
Stream<Integer> stream = list.stream();//得到的是串行流
Stream<Integer> integerStream = list.parallelStream();//得到的是并行流
int[] arr={34,45,56};
IntStream stream1 = Arrays.stream(arr);
//对流的操作分为最终操作和中间操作
//执行了最终操作,流就关闭了,执行的是中间操作,还会返回该流对象
//最终操作
//collect():将流中的数据收集到一起,对这些数据进行一些处理,返回一个新的集合
List<Integer> lists = list.stream().collect(Collectors.toList());//把流中的数据存储到Collectors.toList()返回的集合对象中
System.out.println(lists);
Set<Integer> sets = list.stream().collect(Collectors.toSet());
System.out.println(sets);
//需要指定键生成的规则,值生成的规则
Map<Integer, Integer> map = list.stream().collect(Collectors.toMap(e -> e / 10, e -> e));
System.out.println(map);
//reduce():将流的元素, 逐一带入到这个方法中,进行运算,最终的运算结果,得到的其实是一个Optional类型,需要使用get() 获取到里面的数据
Integer integer = list.stream().reduce((e1, e2) -> e1 + e2).get();//reduce方法的返回值是Optional类型的,需要调用get方法得到结果值
System.out.println(integer);
//count():统计流中的元素数量。
long count = list.stream().count();
System.out.println(count);
//forEach():遍历每个元素。
list.stream().forEach(System.out::println);
//min(): 找到最小值,需要使用get() 获取到里面的数据
Integer integer1 = list.stream().min((e1, e2) -> e2 - e1).get();
System.out.println(integer1);
//max():找到最大值,需要使用get() 获取到里面的数据
Integer integer2 = list.stream().max((e1, e2) -> e1 - e2).get();
System.out.println(integer2);
//allMatch:只有当流中所有的元素,都匹配指定的规则,才会返回true
boolean b = list.stream().allMatch(e -> e > 5);
System.out.println(b);
// anyMatch:只要流中有任意的数据,满足指定的规则,都会返回true
boolean b1 = list.stream().anyMatch(e -> e % 2 == 0);
System.out.println(b1);
//noneMatch:只有当流中的所有的元素,都不满足指定的规则,才会返回true
boolean b2 = list.stream().noneMatch(e -> e > 60);
System.out.println(b2);
//findFirst:从流中获取一个元素(一般情况下,是获取的开头的元素),需要使用 get() 获取到里面的数据
Integer integer3 = list.stream().findFirst().get();
integer3 =list.parallelStream().findFirst().get();
System.out.println(integer3);
//findAny:从流中获取-一个元素(一般情况下,是获取的开头的元素)p,需要使用get() 获取到里面的数据
Integer integer4 = list.stream().findAny().get();
integer4=list.parallelStream().findAny().get();
System.out.println(integer4);
//在多线程的环境下,findAny 和findFirst返回的结果可能不一样。
/*
Stream<Integer> stream2 = list.stream();
System.out.println(stream2.count());//执行完该最终操作,流会关闭
stream2.forEach(System.out::println);//再次使用流时,会异常 java.lang.IllegalStateException
*/
}
}
2.中间操作
public class Demo2 {
public static void main(String[] args) {
ArrayList<Integer> list=new ArrayList<>();
Collections.addAll(list,10,20,30,30,60,15);
//中间操作:可以连续操作,每一个操作的返回值都是一个Stream对象,可以继续进行其他的操作。直到最终操做
//filter(): 对元素进行过滤
//list.stream().filter(e->e>20).forEach(System.out::println);
//distinct():去除重复的元素,去重的规则与HashSet相同。
//list.stream().distinct().forEach(System.out::println);
//sorted():对元素排序
//list.stream().sorted().forEach(System.out::println);//流中的数据所属的类必须实现Comparable接口
//list.stream().sorted((e1,e2) ->e2-e1).forEach(System.out::println);
//limit(): 截取流中指定数量的元素
//list.stream().limit(3).forEach(System.out::println);
//skip(): 跳过流中的指定数量的元素
//list.stream().skip(3).forEach(System.out::println);
//list.stream().skip(4).limit(1).forEach(System.out::println);
//map():对流中的数据进行映射,用新的数据替换旧的数据。
list.stream().map(e->e==10?true:false).forEach(System.out::println);
System.out.println(list);
//flatmap(): 扁平化映射,常用于map直接映射完成后,流中的数据是一个个的容器,而我们需要对容器中的元素进行处
String[] arr={"h,e,l,l,o","w,o,r,l,d"};
Arrays.stream(arr).map(e->e.toCharArray()).forEach(e->System.out.println(Arrays.toString(e)));
//map映射之前是几个数据,映射之后还是几个,flatMap 是合并成一个
Arrays.stream(arr).map(e->e.split(",")).flatMap(e->Arrays.stream(e)).distinct().forEach(System.out::println);
//mapToInt():将流中的数据替换成int类型
String[] b={"234","567"};
double asDouble = Arrays.stream(b).mapToInt(e -> Integer.parseInt(e)).average().getAsDouble();
System.out.println(asDouble);
}
}