🙉专栏推荐:Java入门知识🙉
🙉 内容推荐:Java网络编程(下)🙉
🐹今日诗词: 壮士当唱大风哥, 宵小之徒能几何?🐹
⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏
⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏
目录
TCP字节流套接字编程
回显服务器
服务端(有点问题的)
空白符问题
客户端
代码问题
原因
解决办法一
服务器(没问题的代码)
解决方法二
线程池的服务端代码(没问题的代码)
美图分享
TCP字节流套接字编程
TCP也有两个类用于网络编程
SeverSocket: 用于TCP字节数据进行网络通信, 创建了TCP服务端API, 专门用于服务器的
Socket: 客户端和服务器都能够使用, 客户端使用可以建立和服务器的通信
服务器可使用可以监听客户端的连接请求
值得注意的是: TCP是字节流传输, 传输单位是字节(byte), 不需要向UDP那样专门搞出一个类来用于传输数据报
SeverSocket类
构造方法: SeverSocket(int Port)
SeverSocket函数方法
函数方法
Socket类
构造方法: Socket(String host, int port)
Socket函数方法
回显服务器
服务端(有点问题的)
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class TcpSever { private ServerSocket serverSocket = null; public TcpSever(int port) throws IOException { serverSocket = new ServerSocket(port); } public void start() throws IOException { System.out.println("服务器!启动"); while (true) { Socket clientsocket = serverSocket.accept(); //这里和UDP不一样,UDP是直接接收请求, 而TCP需要先和客户端建立连接, 才能进一步处理请求 processmethod(clientsocket); //客户端请求结束时应该调用close方法,释放资源, 所以在processmethod方法里调用close方法关闭 } } private void processmethod(Socket clientsocket) throws IOException { System.out.printf("[%s:%d]客户端成功连接服务器\n", clientsocket.getInetAddress(), clientsocket.getPort()); //打印日志, 接下来就是响应请求了 try (OutputStream outputStream = clientsocket.getOutputStream(); InputStream inputStream = clientsocket.getInputStream()){ //从clientsocket中获取流对象, 对数据进一步处理, 把他们放到try()里,结束会自动调用close方法 Scanner scanner = new Scanner(inputStream); while (true) { //客户端建立连接后, 可能会发送很多请求, 需要循环处理 //inputStream.read();//读取请求,这样就得到了字符数组,接下来转成字符串, 这样写很麻烦我们可以使用Scanner一步到位 if (!scanner.hasNext()) { //读取前判断有没有数据, 没有数据说明连接断开了 System.out.printf("[%s:%d]客户端断开连接\n", clientsocket.getInetAddress(), clientsocket.getPort()); break; } String request = scanner.next(); //读取请求, next()方法读取数据是读到空白符 String response = process(request); //根据请求计算响应 System.out.printf("[%s:%d] request = %s response = %s", clientsocket.getInetAddress(), clientsocket.getPort(), request, response); //打印日志 outputStream.write(response.getBytes()); //将响应数据写回客户端 } } finally { clientsocket.close(); //客户端断开连接调用close方法释放资源 } } private String process(String request) { //回显服务器,前面next()方法读取数据读到空白符停止,当我们把数据写回客户端是要把空白符加上 return request + "\n"; } public static void main(String[] args) throws IOException { TcpSever tcpSever = new TcpSever(9090); tcpSever.start(); } }
空白符问题
空白符是一类分隔符的统称, 不是一个空格, 常见空白符有很多
TCP字节流传输常见的问题就是空白符问题
发送请求和读取请求时都需要考虑分隔符
因为发送的数据带有空白符,当使用next方法读取数据时, 会忽略空白符, 因此当我们向客户端写回请求时需要把空白符加回去, 保持格式的统一性
客户端
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; public class TcpClient { private Socket socket = null; public TcpClient(String SeverIP, int SeverPort) throws IOException { socket = new Socket(SeverIP, SeverPort); } private void start() throws IOException { System.out.println("客户端, 启动!"); Scanner scanner = new Scanner(System.in); try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) { //获取流对象, 把数据传入流对象之后, 等待服务器响应即可, 这里和UDP不同, UDP是把数据打包发送到服务器 //而TCP客户端已经和服务器建立连接了, 中间通道已经打通, 将数据传入流对象等待响应即可 //流对象扮演的角色就像是中间商, 服务器和客户端的操作都要经过流对象 Scanner scannerinpuStream = new Scanner(inputStream); while (true) { System.out.print("请输入你要发送的请求: "); String request = scanner.next(); request += "\n"; //next方法会忽略\n, 为了保持服务端next方法一致, 手动加上\n outputStream.write(request.getBytes()); //将请求写到流对象中 if(!scannerinpuStream.hasNext()) { //没有数据说明读取完毕了 break; } String response = scannerinpuStream.next(); //从服务器获取响应 System.out.println(response); //将数据打印出来 } } } public static void main(String[] args) throws IOException { TcpClient tcpClient = new TcpClient("127.0.0.1", 9090); tcpClient.start(); } }
运行效果
代码问题
这种写法只能对一个客户端提供服务, 当我们启动多个客户端时
原因
解决办法一
把processmethod方法放到多线程的环境下运行
服务器(没问题的代码)
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; public class TcpSever { private ServerSocket serverSocket = null; public TcpSever(int port) throws IOException { serverSocket = new ServerSocket(port); } public void start() throws IOException { System.out.println("服务器!启动"); while (true) { Socket clientsocket = serverSocket.accept(); //这里和UDP不一样,UDP是直接接收请求, 而TCP需要先和客户端建立连接, 才能进一步处理请求 Thread thread = new Thread(() -> { try { processmethod(clientsocket); //客户端请求结束时应该调用close方法,释放资源, 所以在processmethod方法里调用close方法关闭 } catch (IOException e) { throw new RuntimeException(e); } }); thread.start(); } } private void processmethod(Socket clientsocket) throws IOException { System.out.printf("[%s:%d]客户端成功连接服务器\n", clientsocket.getInetAddress(), clientsocket.getPort()); //打印日志, 接下来就是响应请求了 try (OutputStream outputStream = clientsocket.getOutputStream(); InputStream inputStream = clientsocket.getInputStream()){ //从clientsocket中获取流对象, 对数据进一步处理, 把他们放到try()里,结束会自动调用close方法 Scanner scanner = new Scanner(inputStream); while (true) { //客户端建立连接后, 可能会发送很多请求, 需要循环处理 //inputStream.read();//读取请求,这样就得到了字符数组,接下来转成字符串, 这样写很麻烦我们可以使用Scanner一步到位 if (!scanner.hasNext()) { //读取前判断有没有数据, 没有数据说明连接断开了 System.out.printf("[%s:%d]客户端断开连接\n", clientsocket.getInetAddress(), clientsocket.getPort()); break; } String request = scanner.next(); //读取请求, next()方法读取数据是读到空白符 String response = process(request); //根据请求计算响应 System.out.printf("[%s:%d] request = %s response = %s", clientsocket.getInetAddress(), clientsocket.getPort(), request, response); //打印日志 outputStream.write(response.getBytes()); //将响应数据写回客户端 } } finally { clientsocket.close(); //客户端断开连接调用close方法释放资源 } } private String process(String request) { //回显服务器,前面next()方法读取数据读到空白符停止,当我们把数据写回客户端是要把空白符加上 return request + "\n"; } public static void main(String[] args) throws IOException { TcpSever tcpSever = new TcpSever(9090); tcpSever.start(); } }
解决方法二
把代码放到线程池中, 这种方法更合适, 不仅解决了只能连接一个客户端, 又能减少线程创建销毁的开销
线程池的服务端代码(没问题的代码)
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class TcpSever { private ServerSocket serverSocket = null; public TcpSever(int port) throws IOException { serverSocket = new ServerSocket(port); } public void start() throws IOException { System.out.println("服务器!启动"); ExecutorService pool = Executors.newCachedThreadPool(); //这个线程池可以自动扩容 while (true) { Socket clientsocket = serverSocket.accept(); //这里和UDP不一样,UDP是直接接收请求, 而TCP需要先和客户端建立连接, 才能进一步处理请求 pool.submit(new Runnable() { @Override public void run() { try { processmethod(clientsocket); //客户端请求结束时应该调用close方法,释放资源, 所以在processmethod方法里调用close方法关闭 } catch (IOException e) { throw new RuntimeException(e); } } }); } } private void processmethod(Socket clientsocket) throws IOException { System.out.printf("[%s:%d]客户端成功连接服务器\n", clientsocket.getInetAddress(), clientsocket.getPort()); //打印日志, 接下来就是响应请求了 try (OutputStream outputStream = clientsocket.getOutputStream(); InputStream inputStream = clientsocket.getInputStream()){ //从clientsocket中获取流对象, 对数据进一步处理, 把他们放到try()里,结束会自动调用close方法 Scanner scanner = new Scanner(inputStream); while (true) { //客户端建立连接后, 可能会发送很多请求, 需要循环处理 //inputStream.read();//读取请求,这样就得到了字符数组,接下来转成字符串, 这样写很麻烦我们可以使用Scanner一步到位 if (!scanner.hasNext()) { //读取前判断有没有数据, 没有数据说明连接断开了 System.out.printf("[%s:%d]客户端断开连接\n", clientsocket.getInetAddress(), clientsocket.getPort()); break; } String request = scanner.next(); //读取请求, next()方法读取数据是读到空白符 String response = process(request); //根据请求计算响应 System.out.printf("[%s:%d] request = %s response = %s", clientsocket.getInetAddress(), clientsocket.getPort(), request, response); //打印日志 outputStream.write(response.getBytes()); //将响应数据写回客户端 } } finally { clientsocket.close(); //客户端断开连接调用close方法释放资源 } } private String process(String request) { //回显服务器,前面next()方法读取数据读到空白符停止,当我们把数据写回客户端是要把空白符加上 return request + "\n"; } public static void main(String[] args) throws IOException { TcpSever tcpSever = new TcpSever(9090); tcpSever.start(); } }
美图分享
✨🎆谢谢你的阅读和耐心!祝愿你在编程的道路上取得更多的成功与喜悦!"🎆✨🎄
⭐️点赞收藏加关注,学习知识不迷路⭐️
🎉✔️💪🎉✔️💪🎉✔️💪🎉✔️💪🎉
👍😏⛳️点赞☀️收藏⭐️关注😏👍
👍😏⛳️点赞☀️收藏⭐️关注😏👍
👍😏⛳️点赞☀️收藏⭐️关注😏👍
🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️🙆♂️