导语

仅仅只有Socket类是不足以编写服务器的。要创建一个Socket,你需要知道希望连接哪个Internet主机。编写服务器程序时,无法预先了解哪个主机会联系你,即使确实知道,你也不清楚那个主机希望何时与你联系。对于服务器端的Socket,java提供了一个ServerSocket类表示服务器Socket。从技术上讲,服务器Socket在服务器上运行,监听入站TCP连接,每个服务器Socket监听服务器机器上的一个特定端口。当有个客户端尝试连接这个端口时,服务器就被唤醒,协商建立客户端和服务器之间的连接,并返回一个常规的Socket对象,表示两台主机之间的Socket。换句话说,服务器等待连接,客户端发起连接,一旦ServerSocket建立了连接,服务器会使用一个常规的Socket对象向客户端发送数据。

使用ServerSocket

ServerSocket类包含了服务器端Socket所需的全部内容。其中包括构造ServerSocket对象,指定端口监听连接的方法,配置各个服务器Socket选项的方法,以及其它一些常见的方法。下面是服务器程序的基本生命周期:

  • 使用一个ServerSocket()构造函数在一个特定端口创建一个新的ServerSocket
  • ServerSocket使用其accept()方法监听这个端口的入站连接。accept()会一直阻塞,直到一个客户端尝试建立连接,此时accept()将会返回一个连接客户端和服务器的Socket对象
  • 根据服务器的类型,会调用Socket的getInputSteam()方法或getOutputSteam()方法,或者这两个方法都调用,以获得与客户端通信的输入和输出流
  • 服务器和客户端根据已协商的协议交互
  • 服务器或客户端关闭连接
  • 服务器返回到步骤2,等待下一次连接

简单的daytime服务器

public static void main(String[] args) {

		try (ServerSocket server = new ServerSocket(13)) {
while (true) {
try (Socket conn = server.accept()) {
Writer out = new OutputStreamWriter(conn.getOutputStream());
out.write(new Date().toString());
out.flush();
out.close();
conn.close();
} catch (IOException e) {}
}
} catch (IOException e) {
e.printStackTrace();
}
}

多线程服务器

操作系统会把指向某个特定端口的入站连接请求存储在一个先进先出的队列中。默认地,Java会将这个队列的长度设置为50,但不同的操作系统会有所不同。有些操作系统有一个最大队列长度,例如,在FreeBSD上,默认的最大队列长度为128。在构建ServerSocket时可以指定这个值,但是这个值不能超过操作系统设置的值。当队列中的连接达到最大容量时,主机会拒绝这个端口上额外的连接,直到队列腾出新的位置为止。为了尽快能够处理队列中的连接,可以采用多线程的方式来处理每个socket。

package com.dy.xidian;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class PooledDaytimeServer { private final static int PORT = 13; public static void main(String[] args) {
ExecutorService pool = Executors.newFixedThreadPool(50);
try (ServerSocket server = new ServerSocket(PORT)) {
while (true) {
try {
Socket conn = server.accept();
pool.execute(new DaytimeTask(conn));
} catch (IOException e) {}
}
} catch (IOException e) {
e.printStackTrace();
}
}
} class DaytimeTask implements Runnable { public Socket conn; public DaytimeTask(Socket _conn) {
conn = _conn;
} @Override
public void run() {
try {
Writer out = new OutputStreamWriter(conn.getOutputStream());
out.write(new Date().toString());
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (conn != null)
try {
conn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

从客户端读取数据

echo协议是最简单的交互式TCP服务之一。客户端打开指向echo服务器端口7的socket,并发送数据。服务器将数据发回。这个过程一直继续,直到客户端关闭连接为止。echo协议很有用,可以测试网络,确保数据没有被有问题的路由器或防火墙所破坏。下面的代码中使用NIO中的技术,如果对NIO不了解请移步

package com.dy.xidian;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set; public class PooledDaytimeServer { private final static int PORT = 7; public static void main(String[] args) {
ServerSocketChannel serverChannel;
Selector selector; try {
serverChannel = ServerSocketChannel.open();
ServerSocket server = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(PORT);
server.bind(address);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
return;
} while (true) {
try {
selector.select();
} catch (IOException e) {
e.printStackTrace();
break;
} Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator(); while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
SelectionKey clientKey = client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(100);
clientKey.attach(buffer);
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
client.read(output);
}
if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
output.flip();
client.write(output);
output.compact();
}
} catch (IOException e) {
key.cancel();
try {
key.channel().close();
} catch (IOException e1) {}
}
}
}
}
}

关闭服务器Socket

使用完一个服务器Socket,就应该将其关闭。但不要把关闭ServerSocket与关闭Socket混淆。关闭ServerSocket会释放本地主机的一个端口,允许另一个服务可以绑定这个端口,并且它还会中断该ServerSocket已经接受的目前处于打开状态的所有Socket。关闭ServerSocket基本使用下面的流程:

ServerSocket server = null;
try {
server = new ServerSocket(port);
} finally {
if (server != null) {
try {
server.close();
} catch (IOException e) {}
}
}

构造服务器Socket

创建一个Socket监听80端口

ServerSocket httpd = new ServerSocket(80);

如果主机上有多个IP地址,我们只想监听特定的IP地址上请求(50表示入栈连接的最大个数)

InetAddress local = InetAddress.getByName("192.168.0.100");
ServerSocket httpd = new ServerSocket(80, 50, local);

ServerSocket的介绍的更多相关文章

  1. TCP 通信

    一.TCP与UDP的区别 二.ServerSocket与Socket 1 ServerSocket 以上介绍的几个构造方法中,第二个构造方法最常用. 2.Socket import java.io.* ...

  2. Java ServerSocket的服务端代码介绍

    转自:http://developer.51cto.com/art/201003/190007.htm 所谓Java ServerSocket通常也称作"套接字",有不少的时候需要 ...

  3. python select网络编程详细介绍

    刚看了反应堆模式的原理,特意复习了socket编程,本文主要介绍python的基本socket使用和select使用,主要用于了解socket通信过程 一.socket模块 socket - Low- ...

  4. SSL介绍与Java实例

    有关SSL的原理和介绍在网上已经有不少,对于Java下使用keytool生成证书,配置SSL通信的教程也非常多.但如果我们不能够亲自动手做一个SSL Sever和SSL Client,可能就永远也不能 ...

  5. PhoneGap 在 Android 上的插件开发方法介绍

    移动应用开发已经成为软件开发的一个重要方向,但是移动开发面临的一个重要问题就是跨平台的问题.PhoneGap 作为一个多平台的软件开发框架,提供了一次编写多个平台的运行.目前已经支持多达 6 个移动平 ...

  6. 【转】Android bluetooth介绍(二): android blueZ蓝牙代码架构及其uart 到rfcomm流程

    原文网址:http://blog.sina.com.cn/s/blog_602c72c50102uzoj.html 关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP ...

  7. Java 语言中 Enum 类型的使用介绍

    Enum 类型的介绍 枚举类型(Enumerated Type) 很早就出现在编程语言中,它被用来将一组类似的值包含到一种类型当中.而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常 ...

  8. Java[4] Jetty工作原理介绍(转)

    转自:https://www.ibm.com/developerworks/cn/java/j-lo-jetty/ Jetty 的工作原理以及与 Tomcat 的比较 Jetty 应该是目前最活跃也是 ...

  9. Socket介绍

    一 socket协议 Socket协议的形象描述 socket的英文原义是“孔”或“插座”.在这里作为4BDS UNIX的进程通信机制,取后一种意思.socket非常类似于电话插座.以一个国家级电话网 ...

随机推荐

  1. 列表组件抽象(2)-listViewBase说明

    这是我写的关于列表组件的第2篇博客.前面的相关文章有: 1. 列表组件抽象(1)-概述 listViewBase是列表组件所有文件中最核心的一个,它抽象了所有列表的公共逻辑,将来如果有必要添加其它公共 ...

  2. 《高性能javascript》 领悟随笔之-------DOM编程篇(二)

    <高性能javascript> 领悟随笔之-------DOM编程篇二 序:在javaSctipt中,ECMASCRIPT规定了它的语法,BOM实现了页面与浏览器的交互,而DOM则承载着整 ...

  3. CLR和.Net对象生存周期

    标签:GC .Net C# CLR 前言 1. 基础概念明晰 * 1.1 公告语言运行时 * 1.2 托管模块 * 1.3 对象和类型 * 1.4 垃圾回收器 2. 垃圾回收模型 * 2.1 为什么需 ...

  4. Android Studio同时打开多个项目

    Android Studio的默认设置是打开第二个项目时,第一个项目就被自动关闭了,如果要同时打开多个项目,可以点击File->Settings,对Project Opening进行下面的设置: ...

  5. Python时间戳和日期的相互转换

    Python时间戳和日期的相互转换 (2014-03-17 11:24:35) 转载▼   分类: Python 当前时间戳:time.time() 当前日期:time.ctime() 1.Pytho ...

  6. Python 历遍目录

    Automate the Boring Stuff 学习笔记 01 使用 os 模块的 walk() 函数可以实现历遍目录的操作,该函数接收一个绝对路径字符串作为必选参数,返回三个参数: 当前目录—— ...

  7. 记录一次bug解决过程:velocity中获取url中的参数

    一.总结 在Webx的Velocity中获取url中参数:$rundata.getRequest().getParameter('userId') 在Webx项目中,防止CSRF攻击(Cross-si ...

  8. FunDA(0)- Functional Data Access accessible to all

    大数据.多核CPU驱动了函数式编程模式的兴起.因为函数式编程更适合多线程.复杂.安全的大型软件编程.但是,对许多有应用软件开发经验的编程者来说,函数式编程模式是一种全新的.甚至抽象的概念,可能需要很长 ...

  9. 如何判断一个DOM元素正在动画,一个CSS“阻塞”JS的例子

    一般情况下CSS不会直接影响JS的程序逻辑,但是以CSS实现动画的话,这个便不太确定了,这个故事发生在与UED迁移全局样式的过程. 曾经我有一段实现弹出层隐藏动画的代码是这个样子的: if (this ...

  10. 一步步实现ABAP后台导入EXCEL到数据库【1】

    在SAP的应用当中,导入.导出EXCEL文件的情况是一个常见的需求,有时候用户需要将大量数据定期导入到SAP的数据库中.这种情况下,使用导入程序在前台导入可能要花费不少的时间,如果能安排导入程序为后台 ...