作者:greg travis
<让我们通过一个java程序的例子,学习建立一个简单的tcp/ip服务器。大部分代码都放在一个类中,这样就很容易入门->
本文介绍了快速实现一个简单的tcp/ip服务器的技巧。核心代码都在单个类server.java中,所以很容易建立和使用。
server.java的结构很简单。它监听一个端口,对于每一个进来的连接,生成处理该连接的线程。实际处理该连接的子程序并没有实现,所以你必须自己扩展server.java,写个子程序来实现。
(这样,从技术上讲,这就不是“仅用一个类的服务器”,因为你还需要用另外一个类,来完成某件事情。如果你真的坚持打算只用一个类,只需把子类中的代码放到server.java中。)
下面的echoserver.java的代码体现了该程序的简单性。echoserver 完成如下的简单功能:把客户发来的信息再发回去。
-----------------------------------------------
the one class server
-----------------------------------------------
echoserver.java
-----------------------------------------------
-----------------------------------------------
import java.io.*;
import java.io.*;
public class echoserver extends server
{
// a single argument: the port on which to listen
public echoserver( int port ) {
super( port );
}
// this function is called for each new connection; it
// implements whatever functionality is needed from the server.
// in this case, it simply sends back to the client all data
// that the client sends it. if it receives a ´q´, the
// whole server is shut down.
public void process( inputstream in, outputstream out ) {
try {
while (true) {
int c = in.read();
if (c==´q´) {
close();
} else {
out.write( c );
}
}
} catch( ioexception ie ) { system.out.println( ie ); }
}
// command-line: "java echoserver <port>"
static public void main( string args[] ) throws exception {
int port = new integer( args[0] ).intvalue();
new echoserver( port );
}
}
server类的实现是很有趣的,因为它对于所有线程使用了一个server类的实例。
listener 线程
创建的第一个线程是“listener”线程。该线程中的代码对一个端口进行监听,等待进入的连接。一旦一个连接进入时,一个新的“connection”线程被创建,用于处理该连接。
listener线程必须把用某种方法把新连接的socket对象传送给新线程。因为java在创建线程时,不具有传递参数的功能,所以使用了另外一个技术:哈希表。
一个哈希表对象可以把线程对象映射为socket对象。listener 线程创建了一个新的线程,然后把该新线程和新的socket放到哈希表中。当新线程开始执行时,它从哈希表基于自己的线程对象中,通过调用thread.currentthread(),读入socket。
对于listener线程来说,因为它不需要其他线程“传递”什么信息,此时的哈希表中就没有socket对象,这样,程序就可以判断这是一个listener 线程而不是一个connection线程。
下面为listener线程的代码。现在的这个实现很简单,因为它省略了一些处理。尤其是发生例外时的处理,我没有编写这些代码。因为这需要做更多的工作,而且依赖于具体的应用。
-----------------------------------------------
the one class server
-----------------------------------------------
the listener thread
-----------------------------------------------
-----------------------------------------------
import java.io.*;
import java.net.*;
import java.util.*;
abstract public class server implements runnable
{
// the port the server will listen on
private int port;
// used to "pass" a socket to the new thread that will process it
private hashtable handoff = new hashtable();
// the first thread -- we store it here so we can kill it
// first when closing.
private thread listener;
// a list of the threads that have been started
private vector threads = new vector();
// a list of the sockets that have connected to us
private vector sockets = new vector();
// the listen socket
private serversocket ss;
public server( int port ) {
this.port = port;
// start the listener thread. because we haven´t passed a socket
// object to this thread in the handoff table, it will know
// that it is to be the listener thread.
listener = new thread( this );
listener.start();
}
synchronized public void close() {
// first, make sure there aren´t any incoming connections
listener.stop();
// now, close all the sockets
for (enumeration e = sockets.elements(); e.hasmoreelements();){
socket s = (socket)e.nextelement();
try {
s.close();
} catch( ioexception ie ) { system.out.println( ie );}
}
// and stop all the threads
for (enumeration e = threads.elements(); e.hasmoreelements();){
thread t = (thread)e.nextelement();
// but let´s not stop *ourselves* yet!
if (t != thread.currentthread())
t.stop();
}
system.out.println( "shutting down!" );
// now we can stop ourselves.
thread.currentthread().stop();
}
// this routine does the actual work of the server. it´s not
// implemented, so you have to extend this class to actually get
// something done.
abstract public void process( inputstream in, outputstream out );
// this routine processes all the connections. all the threads
// started by this class run this same routine of the same instance
// of server.
public void run() {
// get the socket that is being "passed" to us by the listener
// thread. if there is no socket here for us, then we are the
// listener thread, or at least we are about to be.
socket s = (socket)handoff.get( thread.currentthread() );
if (s==null) {
// aha -- we are the very first thread, the listener thread.
// start listening.
try {
// set up the listen socket.
ss = new serversocket( port );
system.out.println( "listening on "+port );
while (true) {
// accept a new connection
s = ss.accept();
synchronized( this ) {
system.out.println( "connection from "+s.getinetaddress() );
// make a new thread to handle this connection
thread t = new thread( this );
// store the thread and socket in the lists
sockets.addelement( s );
threads.addelement( t );
// the socket object is "passed" to the new thread by
// getting stuffed here. when the new thread is started,
// it will pull the socket object out of here based on
// its own thread object.
handoff.put( t, s );
// all set! start the thread!
t.start();
}
}
} catch( ioexception ie ) {}
} else {
// we are a processing socket.
try {
inputstream in = s.getinputstream();
outputstream out = s.getoutputstream();
// call the actually-do-something routine in the subclass of
// this object, so that something can actually get done.
process( in, out );
} catch( ioexception ie ) { system.out.println( ie ); }
}
}
}
此处,我们也没有对socket,线程列表以及对handoff哈希表进行整理,大家可以修改这个类,以便能够完善这些工作,我把它们的代码编写工作作为练习,留给读者。
闽公网安备 35060202000074号