一个在本机7777端口提供字符串时间的Socket服务,程序运行后,可以在命令行直接telnet localhost 7777进行测试。
程序虽然简单,但包含了很多相当有用的知识。在程序后面列举了几个我觉得需要注意的地方。(当然还有很多可以讨论地方。)
复杂的东西都是一点一滴建立在简单的模型或者框架上的。所以要想写出很漂亮和健壮的Socket程序,还得从头开始吧。
下面一篇文章我会写一个简单的客户端Socket程序用以和这个ServerSocket交互。
1 只能同时处理一个客户端,想想为什么? @_@
| import java.util.Date; import java.net.Socket; import java.net.ServerSocket; import java.io.IOException; import java.io.PrintWriter; import java.io.BufferedWriter; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class ClientServer extends Thread { private int port; private ServerSocket serverSocket; public ClientServer(int port) throws IOException { this.port = port; this.init(); } private void init() throws IOException { this.serverSocket = new ServerSocket(this.port); this.setDaemon(true); this.start(); } public void run() { Socket clientSock = null; while (true) { try { System.out.println("---------------DateServerTcpServer run----------------------"); clientSock = this.serverSocket.accept(); //见注意点2 boolean validConnection = true; //do sth to check clientSock if (validConnection) { System.out.println("Connection accepted : " + clientSock.toString()); //send a message to client PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSock.getOutputStream())));//见注意点4 String info = "a message from server!"; Date now = new Date(); writer.println(info+now.toString()); writer.flush(); //receive a message from client System.out.println("[wait a response]"); try{ Thread.currentThread().sleep(15000); }catch(Exception e){ } BufferedReader reader = new BufferedReader(new InputStreamReader(clientSock.getInputStream())); System.out.println(reader.readLine());//见注意点2 writer.close(); reader.close(); clientSock.close();//见注意点3 } } catch (Exception e) { e.printStackTrace(); try { if (clientSock != null) { clientSock.close(); } } catch (Exception ee) { clientSock = null; } } } } public int getPort() { return port; } public static void main(String[] args) { try { int port = 7777; ClientServer server = new ClientServer(port); } catch (IOException ex) { ex.printStackTrace(); } while (true) { try { //见注意点1 Thread.currentThread().sleep(30*1000);//server run for 30 seconds! } catch (InterruptedException e) { e.printStackTrace(); } } } } |
注意点:(不知道有没有理解错误的地方)
1 在这里这个ServerSocket是放在一个后台线程里运行的,所以当主线程(main所在的那个线程)退出时,后台线程也自动中断退出了。这里 通过在main里用Thread.currentThread().sleep(30*1000);让主线程休息30秒来让ServerSocket达到运行30秒的效果,如果要让程序运行1个小时,把sleep里的参数改成(60*60*1000)(单位是毫秒)。(后台线程指无法单独运行的线程)
2 clientSock = this.serverSocket.accept(); 和 System.out.println(reader.readLine());都是阻塞语句 (不知道用这个词语恰不恰当),,正常情况下,只有当条件满足,程序才会继续执行下去。clientSock = this.serverSocket.accept()表示只有当客户端尝试去连接这个ServerSocket的(程序里是本机的7777端口)。程序才会返回一个新Socket用于跟客户端通信。(当然这是正常情况,非正常情况程序也有其他途径返回的,比如抛出异常)。
Accept()应该是ServerSocket最重要的方法了,所以这里列出JDK中的资料供参考
| accept public Socket accept() throws IOException 侦听并接受到此套接字的连接。此方法在进行连接之前一直阻塞。 创建新套接字 s,如果存在安全管理器,则使用 s.getInetAddress().getHostAddress() 和 s.getPort() 作为参数调用安全管理器的 checkAccept 方法,以确保允许该操作。这可能会导致 SecurityException 异常。 返回: 新套接字 抛出: IOException - 如果等待连接时发生 I/O 错误。 SecurityException - 如果安全管理器存在并且其 checkListen 方法不允许进行该操作。 SocketTimeoutException - 如果以前使用 setSoTimeout 设置了超时并且已到达超时值。 IllegalBlockingModeException - 如果此套接字具有相关联的通道、通道处于非阻塞模式并且不存在准备接受的连接 另请参见: SecurityManager.checkAccept(java.lang.String, int) |
在这里ServerSocket主要是提供时间服务的,所以并不需要客户端响应。这里提供System.out.println(reader.readLine())的语句,只是为了表明服务端跟客户端的
交互是双向的。readLine()也是阻塞语句,正常情况下只有在流中读到行才会正常返回。在JDK文档中BufferdReader的readLine()方法并没有提到是不是阻塞的。
因为readLine()方法的实质只是一个不断的调用read()方法的过程。所以我们再参考下read()方法。(看下面说明),在里面也没有提到阻塞这个概念!
因为BufferedReader的read()的方法是覆盖父类的read()方法,所以也有必要参考下父类Reader的read()方法。在JDK中是这么说明的
读取单个字符。在有可用字符、发生 I/O 错误或者已到达流的末尾前,此方法一直阻塞。
| BufferedReader类 readLine public String readLine() throws IOException 读取一个文本行。通过下列字符之一即可认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。 返回: 包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null 抛出: IOException - 如果发生 I/O 错误 read public int read() throws IOException 读取单个字符。 覆盖: 类 Reader 中的 read 返回: 作为范围 0 到 65535 (0x00-0xffff) 的整数读入的字符,如果已到达流末尾,则返回 -1 抛出: IOException - 如果发生 I/O 错误 |
3 因为Socket是有限的系统资源,所以我们在使用完以后应该使用clientSock.close();关闭以便还给系统。
4 PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSock.getOutputStream())));
在这里,其实不用以这么麻烦的形式去往流里仅仅输入字符串形式的时间的,但是以这么优美的语句去书写想要表达的概念确实
一个非常痛快的事情。用最直接,最简洁的语句表达出想要做的事情, 是程序员最喜欢的形式。~~~(本来就是因为有好用的接口,好用的标准库,
直观简洁的代码才喜欢Java.~~~@_@)
本文来源:http://blog.csdn.net/snailjava/archive/2007/08/04/1726645.aspx