Java中的TCP通信
构造方法
| 方法名 | 说明 |
|---|---|
| Socket(InetAddress address,int port) | 创建流套接字并将其连接到指定IP指定端口号 |
| Socket(String host, int port) | 创建流套接字并将其连接到指定主机上的指定端口号 |
相关方法
| 方法名 | 说明 |
|---|---|
| InputStream getInputStream() | 返回此套接字的输入流 |
| OutputStream getOutputStream() | 返回此套接字的输出流 |
示例代码
xxxxxxxxxxpublic class ClientDemo { public static void main(String[] args) throws IOException { //创建客户端的Socket对象(Socket) //Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号 Socket s = new Socket("127.0.0.1",10000); //获取输出流,写数据 //OutputStream getOutputStream() 返回此套接字的输出流 OutputStream os = s.getOutputStream(); os.write("hello,tcp,我来了".getBytes()); //释放资源 s.close(); }}构造方法
| 方法名 | 说明 |
|---|---|
| ServletSocket(int port) | 创建绑定到指定端口的服务器套接字 |
相关方法
| 方法名 | 说明 |
|---|---|
| Socket accept() | 监听要连接到此的套接字并接受它 |
注意事项
三次握手和四次挥手
三次握手

四次挥手

示例代码
xxxxxxxxxxpublic class ServerDemo { public static void main(String[] args) throws IOException { //创建服务器端的Socket对象(ServerSocket) //ServerSocket(int port) 创建绑定到指定端口的服务器套接字 ServerSocket ss = new ServerSocket(10000); //Socket accept() 侦听要连接到此套接字并接受它 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(); }}案例需求
客户端:发送数据,接受服务器反馈
服务器:收到消息后给出反馈
案例分析
代码实现
xxxxxxxxxx// 客户端public class ClientDemo { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",10000); OutputStream os = socket.getOutputStream(); os.write("hello".getBytes()); // os.close();如果在这里关流,会导致整个socket都无法使用 socket.shutdownOutput();//仅仅关闭输出流.并写一个结束标记,对socket没有任何影响 BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while((line = br.readLine())!=null){ System.out.println(line); } br.close(); os.close(); socket.close(); }}// 服务器public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); Socket accept = ss.accept(); InputStream is = accept.getInputStream(); int b; while((b = is.read())!=-1){ System.out.println((char) b); } System.out.println("看看我执行了吗?"); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("你谁啊?"); bw.newLine(); bw.flush(); bw.close(); is.close(); accept.close(); ss.close(); }}案例需求
客户端:数据来自于本地文件,接收服务器反馈
服务器:接收到的数据写入本地文件,给出反馈
案例分析
相关方法
| 方法名 | 说明 |
|---|---|
| void shutdownInput() | 将此套接字的输入流放置在“流的末尾” |
| void shutdownOutput() | 禁止用此套接字的输出流 |
代码实现
xxxxxxxxxx// 客户端public class ClientDemo { public static void main(String[] args) throws IOException { Socket socket = new Socket("127.0.0.1",10000); //是本地的流,用来读取本地文件的. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("socketmodule\\ClientDir\\1.jpg")); //写到服务器 --- 网络中的流 OutputStream os = socket.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(os); int b; while((b = bis.read())!=-1){ bos.write(b);//通过网络写到服务器中 } bos.flush(); //给服务器一个结束标记,告诉服务器文件已经传输完毕 socket.shutdownOutput(); BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while((line = br.readLine()) !=null){ System.out.println(line); } bis.close(); socket.close(); }}// 服务器public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); Socket accept = ss.accept(); //网络中的流,从客户端读取数据的 BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); //本地的IO流,把数据写到本地中,实现永久化存储 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("socketmodule\\ServerDir\\copy.jpg")); int b; while((b = bis.read()) !=-1){ bos.write(b); } BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("上传成功"); bw.newLine(); bw.flush(); bos.close(); accept.close(); ss.close(); }}优化方案一
需求
服务器只能处理一个客户端请求,接收完一个图片之后,服务器就关闭了。
解决方案
使用循环
代码实现
xxxxxxxxxx// 服务器代码如下,客户端代码同上个案例,此处不再给出public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); while (true) { Socket accept = ss.accept(); //网络中的流,从客户端读取数据的 BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); //本地的IO流,把数据写到本地中,实现永久化存储 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("optimizeserver\\ServerDir\\copy.jpg")); int b; while((b = bis.read()) !=-1){ bos.write(b); } BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("上传成功"); bw.newLine(); bw.flush(); bos.close(); accept.close(); } //ss.close(); }}优化方案二
需求
第二次上传文件的时候,会把第一次的文件给覆盖。
解决方案
UUID. randomUUID()方法生成随机的文件名
代码实现
xxxxxxxxxx// 服务器代码如下,客户端代码同上个案例,此处不再给出public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); while (true) { Socket accept = ss.accept(); //网络中的流,从客户端读取数据的 BufferedInputStream bis = new BufferedInputStream(accept.getInputStream()); //本地的IO流,把数据写到本地中,实现永久化存储 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("optimizeserver\\ServerDir\\" + UUID.randomUUID().toString() + ".jpg")); int b; while((b = bis.read()) !=-1){ bos.write(b); } BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())); bw.write("上传成功"); bw.newLine(); bw.flush(); bos.close(); accept.close(); } //ss.close(); }}优化方案三
需求
使用循环虽然可以让服务器处理多个客户端请求。但是还是无法同时跟多个客户端进行通信。
解决方案
开启多线程处理
代码实现
xxxxxxxxxx// 线程任务类public class ThreadSocket implements Runnable { private Socket acceptSocket; public ThreadSocket(Socket accept) { this.acceptSocket = accept; } public void run() { BufferedOutputStream bos = null; try { //网络中的流,从客户端读取数据的 BufferedInputStream bis = new BufferedInputStream(acceptSocket.getInputStream()); //本地的IO流,把数据写到本地中,实现永久化存储 bos = new BufferedOutputStream(new FileOutputStream("optimizeserver\\ServerDir\\" + UUID.randomUUID().toString() + ".jpg")); int b; while((b = bis.read()) !=-1){ bos.write(b); } BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(acceptSocket.getOutputStream())); bw.write("上传成功"); bw.newLine(); bw.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if (acceptSocket != null){ try { acceptSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }}// 服务器代码public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); while (true) { Socket accept = ss.accept(); ThreadSocket ts = new ThreadSocket(accept); new Thread(ts).start(); } //ss.close(); }}优化方案四
需求
使用多线程虽然可以让服务器同时处理多个客户端请求。但是资源消耗太大。
解决方案
加入线程池
代码实现
xxxxxxxxxx// 服务器代码如下,线程任务类代码同上,此处不再给出public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(10000); ThreadPoolExecutor pool = new ThreadPoolExecutor( 3,//核心线程数量 10, //线程池的总数量 60, //临时线程空闲时间 TimeUnit.SECONDS, //临时线程空闲时间的单位 new ArrayBlockingQueue<>(5),//阻塞队列 Executors.defaultThreadFactory(),//创建线程的方式 new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略 ); while (true) { Socket accept = ss.accept(); ThreadSocket ts = new ThreadSocket(accept); //new Thread(ts).start(); pool.submit(ts); } //ss.close(); }}