Java网络编程024.TCP网络通信编程
- 基本介绍
- 基于客户端–服务端的网络通信
- 底层使用的是TCP/IP协议
- 应用场景举例:客户端发送数据,服务端接收并显示控制台
- 基于Scoket的TCP编程
4.1应用案例1:(使用字节流)
- 编写一个服务器端,和一个客户端
- 服务器端在9999端口监听
- 客户端连接到服务器端,发送“hello,server”,然后退出
- 服务器端接收到客户端发送的信息,输出,并结束
客户端思路:
1.连接服务端(ip,端口)
2.连接上后,生成socket,通过socket.getOutputStream()
3.通过输出流,写入数据到数据通道
package li.network.socket;import java.io.IOException;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;//发送"hello,server"给服务端public class SocketTCP01Client { public static void main(String[] args) throws IOException { //1.连接服务端(ip,端口) Socket socket = new Socket(InetAddress.getLocalHost(), 9999);//连接服务端的主机的端口,在这里是本机 System.out.println("客户端 socket返回=" + socket.getClass()); //2.连接上后,生成socket,通过socket.getOutputStream() // 得到和 socket 对象关联的输出流对象 OutputStream outputStream = socket.getOutputStream(); //3.通过输出流,写入数据到数据通道 outputStream.write("hello,server".getBytes()); //4. 关闭流对象和socket(必须关闭) outputStream.close(); socket.close(); System.out.println("客户端退出"); }}
服务器端思路:
1.在本机的9999端口监听,等待连接
2.当没有客户端连接9999端口时,程序将会阻塞,等待连接
3.通过socket.getInputStream()读取客户端写入到数据通道的数据,显示
package li.network.socket;import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class SocketTCP01Server { public static void main(String[] args) throws IOException { // 1.在本机的9999端口监听,等待连接 // 要求在本机没有其他服务在监听9999 // ServerSocket 可以通过 accept() 方法返回多个 Socket[多个客户端连接服务器的并发] ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务端在9999端口监听,等待连接..."); // 2.当没有客户端连接9999端口时,程序会阻塞,等待连接 // 如果有客户端连接则会返回一个socket对象,程序继续 Socket socket = serverSocket.accept(); System.out.println("服务器端 socket=" + socket.getClass()); // 3.通过socket.getInputStream()读取客户端写入到数据通道的数据,显示 InputStream inputStream = socket.getInputStream(); // 4.IO读取 byte[] buf = new byte[1024]; int readLen = 0; while ((readLen = inputStream.read(buf)) != -1) { System.out.println(new String(buf, 0, readLen));//根据实际读取到的长度显示内容 } // 5. 关闭流对象和socket(必须关闭) inputStream.close(); socket.close(); serverSocket.close();//关闭serverSocket }}
- 要先运行服务端,这里显示正在等待连接客户端:
- 再运行客户端程序,得到一个socket,通过输出流将数据写入到数通道里面,然后退出:
- 服务器端会通过输入流不停地读取,把数据输出到控制台上,然后关闭退出:
4.2应用案例2:(使用字节流)
- 编写一个服务器端,和一个客户端
- 服务器端在9999端口监听
- 客户端连接到服务器端,发送“hello,server”,并接收服务器端回发的“hello,client”,再退出
- 服务器端接收到客户端发送的信息然后输出,并发送“hello,client”,再退出
注意设置写入结束标记socket.shutdownOutput();
否则程序会认为还在写入数据。
服务端Server:
package li.network.socket;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;@SuppressWarnings("all")public class SocketTCP02Server { public static void main(String[] args) throws IOException { // 1.在本机的9999端口监听,等待连接 // 要求在本机没有其他服务在监听9999 // ServerSocket 可以通过 accept() 方法返回多个 Socket[多个客户端连接服务器的并发] ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务端在9999端口监听,等待连接..."); // 2.当没有客户端连接9999端口时,程序会阻塞,等待连接 // 如果有客户端连接则会返回一个socket对象,程序继续 Socket socket = serverSocket.accept(); System.out.println("服务器端 socket=" + socket.getClass()); // 3.获取数据,通过socket.getInputStream()读取客户端写入数据通道的数据,并显示 InputStream inputStream = socket.getInputStream(); //IO读取 byte[] buf = new byte[1024]; int readLen = 0; while ((readLen = inputStream.read(buf)) != -1) { System.out.println(new String(buf, 0, readLen));//根据实际读取到的字符串长度显示内容 } // 4. 回话,写入数据 // 获取socket相关的输出流 OutputStream outputStream = socket.getOutputStream(); outputStream.write("hello,client".getBytes()); //设置写入结束标记 socket.shutdownOutput(); // 5. 关闭流对象和socket(必须关闭) inputStream.close(); outputStream.close(); socket.close(); serverSocket.close();//关闭serverSocket }}
客户端Client:
package li.network.socket;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.Socket;@SuppressWarnings("all")//发送"hello,server"给服务端public class SocketTCP02Client { public static void main(String[] args) throws IOException { //1.连接服务端(ip,端口) Socket socket = new Socket(InetAddress.getLocalHost(), 9999);//连接服务端的主机的端口,在这里是本机 System.out.println("客户端 socket返回=" + socket.getClass()); //2.连接上后,生成socket,通过socket.getOutputStream() // 得到和 socket 对象关联的输出流对象 OutputStream outputStream = socket.getOutputStream(); //3.通过输出流,写入数据到数据通道 outputStream.write("hello,server".getBytes()); //设置写入结束标记 socket.shutdownOutput(); //4. 获取数据 // 获取和socket相关联的输入流,并显示 InputStream inputStream = socket.getInputStream(); byte[] buf = new byte[1024]; int readLen = 0; while ((readLen = inputStream.read(buf)) != -1) { System.out.println(new String(buf, 0, readLen)); } //5. 关闭流对象和socket(必须关闭) outputStream.close(); inputStream.close(); socket.close(); System.out.println("客户端退出"); }}
- 服务端等待客户端连接:
- 客户端往数据通道写入数据“hello,server,并接受到服务端发送的数据“hello,client”
- 服务端接收到数据:
4.3应用案例3:(使用字符流)
- 编写一个服务端,一个客户端
- 服务端在9999端口监听
- 客户端连接到服务端,发送“hello,server”,并接收到服务端会发的“hello,client”,然后退出
- 服务端接收到 客户端发送的信息,输出,并发送“hello,client”,再退出
思路答题与应用案例2一致,这里要求使用字符流,那么在拿到输入/输出字符流之后,使用转换流,将其转换为字符流即可。
PS:设置写入结束标记:除了使用socket.shutdownOutput(); 还可以使用writer.newLine(),如果使用这种结束标志的话,需要使用readLine()的方式读取
注意点:如果使用的是字符流,需要我们手动刷新xx.flush();
,否则数据不会写入数据通道
服务端Server:
package li.network.socket;import java.io.*;import java.net.ServerSocket;import java.net.Socket;//使用字符流@SuppressWarnings("all")public class SocketTCP03Server { public static void main(String[] args) throws IOException { // 1.在本机的9999端口监听,等待连接 // 要求在本机没有其他服务在监听9999 // ServerSocket 可以通过 accept() 方法返回多个 Socket[多个客户端连接服务器的并发] ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务端在9999端口监听,等待连接..."); // 2.当没有客户端连接9999端口时,程序会阻塞,等待连接 // 如果有客户端连接则会返回一个socket对象,程序继续 Socket socket = serverSocket.accept(); System.out.println("服务器端 socket=" + socket.getClass()); // 3.获取数据,通过socket.getInputStream()读取客户端写入数据通道的数据,并显示 InputStream inputStream = socket.getInputStream(); //IO读取---使用字符流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine(); System.out.println(s);//输出 // 4. 回话,写入数据 // 获取socket相关的输出流 OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("hello,client 字符流"); bufferedWriter.newLine();//插入一个换行符,表示回复内容的结束 bufferedWriter.flush();//手动刷新 // 5. 关闭流对象和socket(必须关闭) bufferedWriter.close();//关闭外层流 bufferedReader.close(); socket.close(); serverSocket.close();//关闭serverSocket }}
客户端Client:
package li.network.socket;import java.io.*;import java.net.InetAddress;import java.net.Socket;@SuppressWarnings("all")//发送"hello,server"给服务端,使用字符流public class SocketTCP03Client { public static void main(String[] args) throws IOException { //1.连接服务端(ip,端口) Socket socket = new Socket(InetAddress.getLocalHost(), 9999);//连接服务端的主机的端口,在这里是本机 System.out.println("客户端 socket返回=" + socket.getClass()); //2.连接上后,生成socket,通过socket.getOutputStream() // 得到和 socket 对象关联的输出流对象 OutputStream outputStream = socket.getOutputStream(); //3.通过输出流,写入数据到数据通道----使用字符流 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("hello,server 字符流"); bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束,注意:要求对方使用readLine()!!!! //如果使用的是字符流,需要我们手动刷新,否则数据不会写入数据通道 bufferedWriter.flush(); //4. 获取数据 // 获取和socket相关联的输入流,并显示 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine(); System.out.println(s); //5. 关闭流对象和socket(必须关闭) bufferedReader.close();//关闭外层流 bufferedWriter.close(); socket.close(); System.out.println("客户端退出"); }}
4.4应用案例4:
- 编写一个服务端和一个客户端
- 服务器端在端口8888监听
- 客户端连接到服务端,并发送一张图片
- 服务器端接收到客户端发送的图片,保存到src目录下,然后发送“收到图片”,再退出
- 客户端接收到服务端的回话:“收到图片“ 之后,再退出
- 该程序要求使用StreamUtils.java
说明:使用BufferedInputStreasm和BufferedOutputStream字节流
思路如下:
客户端这边先将数据传输到客户端程序中,然后通过socket获取数据通道,将文件数据发送给服务端;
服务端这边,先是得到数据通道上的数据(传输到服务端程序中),然后使用将程序中的文件数据输出到某个目录下,之后在将“收到图片”的消息输出到数据通道上
客户端通过socket,从数据通道上获取回复消息,并打印在控制台上
工具类StreamUtils:
package li.network.upload;import java.io.BufferedReader;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;/** * 此类用于演示关于流的读写方法 * */public class StreamUtils { /** * 功能:将输入流转换成byte[],即可以把文件的内容写入到byte[] * @param is * @return * @throws Exception */ public static byte[] streamToByteArray(InputStream is) throws Exception{ ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象 byte[] b = new byte[1024];//字节数组 int len; while((len=is.read(b))!=-1){//循环读取 bos.write(b, 0, len);//把读取到的数据写入到 bos流中 } byte[] array = bos.toByteArray();//然后将bos 转成为一个字节数组 bos.close(); return array; } /** * 功能:将InputStream转换成String * @param is * @return * @throws Exception */ public static String streamToString(InputStream is) throws Exception{ BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder builder= new StringBuilder(); String line; while((line=reader.readLine())!=null){ //当读取到 null时,就表示结束 builder.append(line+"\r\n"); } return builder.toString(); }}
服务端Server:
package li.network.upload;import java.io.*;import java.net.ServerSocket;import java.net.Socket;//文件上传的服务端public class TCPFileUploadServer { public static void main(String[] args) throws Exception { // 1.服务端在监听8888端口(这里是在本机进行) ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务端在8888端口监听..."); // 2.等待连接 Socket socket = serverSocket.accept(); //3.读取数据通道的数据 //通过socket获得输入流 //创建对象 BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); //写入到程序中 byte[] bytes = StreamUtils.streamToByteArray(bis);//streamToByteArray方法:将输入流转换成byte[] //4.将得到的bytes数组(文件数据)写入到指定的路径,就得到一个文件 String destFilePath = "src\\copy.png"; //创建对象 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath)); //写入数据到目录中 bos.write(bytes); bos.close(); //5.收到图片后,向客户端回复“收到图片” //通过socket获得输出流 //创建对象 BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); bw.write("收到图片"); bw.flush();//把内容刷新到数据通道 socket.shutdownOutput();//设置写入结束标记 //6.关闭其他资源 bw.close(); bis.close(); socket.close(); serverSocket.close(); }}
客户端Client:
package li.network.upload;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.FileInputStream;import java.io.InputStream;import java.net.InetAddress;import java.net.Socket;//文件上传的客户端public class TCPFileUploadClient { public static void main(String[] args) throws Exception { //1.客户端连接服务端,得到一个socket对象 Socket socket = new Socket(InetAddress.getLocalHost(), 8888); //2.创建读取磁盘文件的输入流 String filePath = "d:\\ggmm.jpg"; //创建对象 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath)); //这里的 bytes 就是 文件filePath 对应的字节数组 //读取数据到程序中 byte[] bytes = StreamUtils.streamToByteArray(bis); //3.通过socket获取输出流,将bytes数据发送到服务端 //创建对象 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); //写入数据通道 bos.write(bytes);//将文件对应的字节数组写入到数据通道 bis.close(); socket.shutdownOutput();//设置写入数据的结束标志 //4.获取服务端回复的消息 InputStream inputStream = socket.getInputStream(); String s = StreamUtils.streamToString(inputStream);//streamToString方法:把一个输入流的数据直接转成字符串 System.out.println(s); //关闭相关的流 inputStream.close(); bos.close(); socket.close(); }}