【Java】ServerSocket的学习笔记
公司有本《Java网络编程》一直闲置在书架上,反正我对Socket方面不太懂,今天跟着书学习一番。
> 参考的优秀书籍
《Java网络编程》 --中国电力出版社
> 最简单的服务器端
当客户端连接进来,向客户端发送“welcome”以表咋程序员的亲切感~~

import java.io.IOException; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class SimpleServerSocket { public static void main(String[] args) { ServerSocket ss = null; Socket s = null; OutputStreamWriter osw = null; try { ss = new ServerSocket(9000); s = ss.accept(); osw = new OutputStreamWriter(s.getOutputStream()); osw.write("welcome..." + System.getProperty("line.separator")); osw.flush(); System.out.println("outputed."); } catch (IOException e) { System.out.println("socket exception."); // TODO Auto-generated catch block e.printStackTrace(); } finally { CloseableCloser.close(osw); CloseableCloser.close(s); CloseableCloser.close(ss); } } }
有许多资源需要关闭,那就写一个小的工具类来关闭吧

import java.io.Closeable; import java.io.IOException; public class CloseableCloser { public static void close(Closeable c) { if (c == null) { return; } try { c.close(); } catch (IOException e) { // TODO Auto-generated catch block System.out.println("exception when closing."); e.printStackTrace(); } } }
通过Linux telnet一下,看到反馈了。
当然用Windows telnet也一样的,只是Windows telnet跳了一个页面,不方便截图而已。
注意:如果服务端输出语句时没有加换行符,我在Linux、Windows测试时都没看到打印welcome哦。

telnet xx.xx.xx.xx 9000 Trying xx.xx.xx.xx... Connected to xx.xx.xx.xx. Escape character is '^]'. welcome... Connection closed by foreign host.
当然也可通过Java Socket编写客户端去连接

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; public class SimpleSocket { public static void main(String[] args) { Socket s = null; BufferedReader br = null; String line = null; try { s = new Socket("127.0.0.1", 9000); br = new BufferedReader(new InputStreamReader(s.getInputStream())); while ((line = br.readLine()) != null) { System.out.println(line); } } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { CloseableCloser.close(s); } } }
> 提供持续的服务,同时处理好不同的异常
看上面的服务端程序,可以发现它只能处理一个任务,而服务端一般来说是提供持续的服务的嘛,那么我们加一个while true呗。
另外,与客户端交互的Socket的异常和ServerSocket的异常是不是需要分开处理一下呢?试想,你一定不想某一个业务出现异常了,导致整个服务端的服务都中止的。

import java.io.IOException; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class SimpleServerSocket { public static void main(String[] args) { ServerSocket ss = null; Socket s = null; OutputStreamWriter osw = null; try { ss = new ServerSocket(9000); while (true) { try { s = ss.accept(); osw = new OutputStreamWriter(s.getOutputStream()); osw.write("welcome..." + System.getProperty("line.separator")); osw.flush(); System.out.println("outputed."); } catch (IOException e) { System.out.println("socket exception."); // TODO Auto-generated catch block e.printStackTrace(); } finally { CloseableCloser.close(osw); CloseableCloser.close(s); } } } catch (IOException e) { System.out.println("server socket exception."); // TODO Auto-generated catch block e.printStackTrace(); } finally { if (ss != null) { CloseableCloser.close(ss); } } } }
这样,你用多个客户端不断地连接,它都给你响应了。
> 并发地处理任务
上述的服务端代码有一段线程睡眠的代码用于模拟业务处理所需的时间的,我们把它的注解解开,然后用不同客户端连接,可以发现,程序在同一时间只能处理一个任务嘛。
而且,由于服务端同时只能处理一个请求,其他请求就堵塞了,操作系统会将请求同一端口的请求存储在一个先进先出的队列中,然而这个队列有长度限制。当然,这个限制各个操作系统不同。
不信,那么我们用Java多线程发送150个请求:

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; public class MultipleSocket extends Thread { public static void main(String[] args) { for (int i = 0; i < 150; i++) { new MultipleSocket().start(); } } public void run() { Socket s = null; BufferedReader br = null; String line = null; try { System.out.println(this.getName() + " is started."); s = new Socket("127.0.0.1", 9000); br = new BufferedReader(new InputStreamReader(s.getInputStream())); while ((line = br.readLine()) != null) { System.out.println(line); } } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { CloseableCloser.close(br); CloseableCloser.close(s); } } }
不出意外,将报以下异常:

java.net.ConnectException: Connection refused: connect at java.net.DualStackPlainSocketImpl.connect0(Native Method) at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
那么我们就转为线程处理呗!
这里换为多线程处理,同时限制同时最多处理2个线程(需要限制几个线程数,自己设置哦)。
为什么要限制线程数量呢?如果同时许多了客户端连接,超过一定数量,最直接的结果就是内存耗尽了。
关于如何限制线程数量,可以参考以前的博文:【多线程】并发执行指定数量的线程

import java.io.IOException; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SimpleServerSocket { public static void main(String[] args) { ServerSocket ss = null; Socket s = null; OutputStreamWriter osw = null; ExecutorService es = Executors.newFixedThreadPool(2); try { ss = new ServerSocket(9000); while (true) { s = ss.accept(); es.execute(new BusinessThread(s)); } } catch (IOException e) { System.out.println("server socket exception."); // TODO Auto-generated catch block e.printStackTrace(); } finally { if (ss != null) { CloseableCloser.close(ss); } } } }

import java.io.IOException; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.concurrent.TimeUnit; public class BusinessThread implements Runnable { Socket s = null; public BusinessThread(Socket s) { super(); this.s = s; } public void run() { OutputStreamWriter osw = null; try { osw = new OutputStreamWriter(s.getOutputStream()); osw.write("welcome..." + System.getProperty("line.separator")); osw.flush(); System.out.println("outputed."); // 模拟这里的业务进行得很慢 try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (IOException e) { System.out.println("socket exception."); // TODO Auto-generated catch block e.printStackTrace(); } finally { CloseableCloser.close(osw); CloseableCloser.close(s); } } }
本博客为学习、笔记之用,以笔记形式记录学习的知识与感悟。学习过程中可能参考各种资料,如觉文中表述过分引用,请务必告知,以便迅速处理。如有错漏,不吝赐教。
如果本文对您有用,点赞或评论哦;如果您喜欢我的文章,请点击关注我哦~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用