Java网络编程基础 1 建立一个提供当前时间字符串的ServerSocket

一个在本机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();
      }

    }
  }
}


 

注意点:(不知道有没有理解错误的地方)

 

在这里这个ServerSocket是放在一个后台线程里运行的,所以当主线程(main所在的那个线程)退出时,后台线程也自动中断退出了。这里 通过在main里用Thread.currentThread().sleep30*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文档中BufferdReaderreadLine()方法并没有提到是不是阻塞的。

因为readLine()方法的实质只是一个不断的调用read()方法的过程。所以我们再参考下read()方法。(看下面说明),在里面也没有提到阻塞这个概念!

因为BufferedReaderread()的方法是覆盖父类的read()方法,所以也有必要参考下父类Readerread()方法。在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.~~~@_@

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值