TCP练习
练习一:多发多收
需求:
客户端:多次发送数据
服务器:接收多次接收数据,并打印
代码示例:
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1", 10086);
Scanner sc = new Scanner(System.in);
OutputStream os = socket.getOutputStream();
while (true) {
System.out.println("请输入:");
String str = sc.nextLine() + '\n';
if ("886".equals(str)) {
break;
}
os.write(str.getBytes());
}
os.close();
socket.close();
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(10086);
System.out.println("等待连接。。。");
Socket socket = serverSocket.accept();
System.out.println("连接成功。。。");
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int b;
while ((b = isr.read()) != -1) {
System.out.print((char) b);
}
isr.close();
serverSocket.close();
socket.close();
}
}
练习二:接收并反馈
-
案例需求
客户端:发送数据,接受服务器反馈
服务器:收到消息后给出反馈
-
案例分析
- 客户端创建对象,使用输出流输出数据
- 服务端创建对象,使用输入流接受数据
- 服务端使用输出流给出反馈数据
- 客户端使用输入流接受反馈数据
-
代码实现
import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket; public class Client { public static void main(String[] args) throws Exception { Socket socket = new Socket("127.0.0.1", 10086); socket.getOutputStream().write("你好帅".getBytes()); socket.shutdownOutput();//因为服务器read方法需要一个一个结束标记,这里仅仅关闭输出流.并写一个结束标记,对socket没有任何影响 InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); int b; while ((b = isr.read()) != -1) { System.out.print((char) b); } socket.close(); } }
import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(10086); System.out.println("等待连接。。。"); Socket socket = serverSocket.accept(); System.out.println("连接成功。。。"); InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); int b; while ((b = isr.read()) != -1) { System.out.print((char) b); } socket.getOutputStream().write("谢谢,你也很帅".getBytes()); serverSocket.close(); socket.close(); } }
练习三:上传练习(TCP协议)
-
案例需求
客户端:数据来自于本地文件,接收服务器反馈
服务器:接收到的数据写入本地文件,给出反馈
-
案例分析
- 创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用shutdownOutput()方法告知服务端传输结束
- 创建服务器对象,创建输出流对象指向文件,每接受一次数据就使用输出流输出到文件中,传输结束后。使用输出流给客户端反馈信息
- 客户端接受服务端的回馈信息
-
相关方法
方法名 说明 void shutdownInput() 将此套接字的输入流放置在“流的末尾” void shutdownOutput() 禁止用此套接字的输出流 -
代码实现
字节流只能传输图片,字符流传输文字
import java.io.*; import java.net.Socket; public class Client { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1", 10086); BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\Project\\idea-project\\java_xuexi\\Cilent\\图片1.png")); BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); int len; byte[] bytes = new byte[1024]; while ((len = bis.read(bytes)) != -1) { bos.write(bytes, 0, len); } bos.flush(); socket.shutdownOutput(); //接收服务器的回写数据 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String s = br.readLine(); System.out.println(s); socket.close(); } }
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10086); Socket socket = ss.accept(); InputStream is = socket.getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\Project\\idea-project\\java_xuexi\\Server\\图片1.png")); byte[] bytes = new byte[1024]; int len; while ((len = bis.read(bytes)) != -1) { bos.write(bytes, 0, len); } bos.flush(); bos.close(); //回写数据 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bw.write("上传成功"); bw.newLine(); bw.close(); ss.close(); socket.close(); } }
练习四:文件名重复
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10086);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
String fileName = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\Project\\idea-project\\java_xuexi\\Server\\" + fileName + ".png"));
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.flush();
bos.close();
//回写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.close();
ss.close();
socket.close();
}
}
练习五:服务器改写为多线程
服务器只能处理一个客户端请求,接收完一个图片之后,服务器就关闭了。
优化方案一:
使用循环
弊端:
第一个用户正在上传数据,第二个用户就来访问了,此时第二个用户是无法成功上传的。
所以,使用多线程改进
优化方案二:
每来一个用户,就开启多线程处理
import java.io.*;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10086);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\Project\\idea-project\\java_xuexi\\Cilent\\图片1.png"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
int len;
byte[] bytes = new byte[1024];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.flush();
socket.shutdownOutput();
//接收服务器的回写数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = br.readLine();
System.out.println(s);
socket.close();
}
}
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10086);
while (true) {
//等待客户端连接
Socket socket = ss.accept();
//开启一条线程
//一个用户就对应服务端的一条线程
Thread thread = new Thread(new MyRunable(socket));
thread.start();
System.out.println("线程:"+thread.getId());
}
}
}
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class MyRunable implements Runnable {
Socket socket;
public MyRunable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
InputStream is = socket.getInputStream();
String fileName = UUID.randomUUID().toString().replace("-", "");
BufferedInputStream bis = new BufferedInputStream(is);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\Project\\idea-project\\java_xuexi\\Server\\" + fileName + ".png"));
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
bos.flush();
}
bos.close();
//回写数据
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.close();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//释放资源
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
练习六:线程池改进
只需要修改服务端代码,添加一个线程池队列
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(10086);
//创建线程池对象
ThreadPoolExecutor pool = new ThreadPoolExecutor(
3,//线程池中的线程数量
16,//线程池中的最大线程数量
60,//线程没有执行任务的时候,超过60秒就销毁
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(2),//队列
Executors.defaultThreadFactory(),//线程工厂
new ThreadPoolExecutor.AbortPolicy()//拒绝策略
);
while (true) {
//等待客户端连接
Socket socket = ss.accept();
//开启一条线程
//一个用户就对应服务端的一条线程
// Thread thread = new Thread(new MyRunable(socket));
// thread.start();
// System.out.println("线程:" + thread.getId());
pool.submit(new MyRunable(socket));
}
}
}