`
javahigh1
  • 浏览: 1227362 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

TCP/IP服务器与客户端套接字

 
阅读更多
套接字(Socket)是由伯克利大学首创的。它允许程序把网络连接当成一个流,可以向这个流写字节,也可以从这个流读取字节。套接字为程序员屏蔽了网络的底层细节,例如媒体类型、信息包的大小、网络地址、信息的重发等。 Socket是网络上运行的两个程序间双向通信的一端,它既可以接受请求,也可以发送请求,利用它可以较为方便地编写网络上数据的传递。在Java中,有专门的Socket类处理用户的请求和响应。利用Socket类的方法,就可以实现两台计算机之间的通信。 套接字能执行7种基本操作: 连接到远程机器。 绑定到端口。 接收从远程机器来的绑定端口上的连接。 监听到达的数据。 发送数据。 接收数据。 关闭连接。 java.net.Socket类是Java的基础类,用于执行客户端的TCP操作。套接字有两种:一种套接字在服务器端创建,叫做服务器套接字(ServerSocket);还有一种在客户端被创建,就是客户端套接字。 1 客户端套接字 1.1 构造函数 1.public Socket(String host ,int port) throws unknownHostException IOException 这个方法建立一个到主机host、端口号为port的套接字,连接到远程主机。 示范代码如下: try { Socket soc=new Socket ("www.sdfd.net" , 80); //发送数据 } catch(unknownHostException uex) { } catch(IOException e) { } 2.public Socket (InetAddress host ,int port ) throws IOException 建立一个套接字,与前一个不同的是它用InetAddress对象指定套接字。如果出错则抛出IOException异常。 1.2 常用的方法 1.public InetAddress getInetAddress ( ) 调用Socket对象的getInetAddress ( )方法返回连接到远程主机的地址,如果连接失败,则返回以前连接的主机。实例如下: try { Socket socket = new Socket ("www.snm.com" ,80); InetAddress host =socket.getInetAddress ( ); //操作 } catch( IOException e) { //异常处理 } catch (unknownHostException e) { //异常处理 } 2.public int getPort ( ) 返回Socket连接到远程主机的端口号。示范代码如下: try { Socket socket =new Socket ("www.snm.com",80); int port =socket.getPort ( ); //操作 } catch( IOException e) { //异常处理 } catch (unknownHostException e) { //异常处理 } 3.public int getLocalPort ( ) 一个Socket连接两个终端,方法getLocalPort( )返回本地连接终端的端口号。示范代码如下: try { Socket socket =new Socket ("www.snm.com" ,80); int port =socket.getLocalPort ( ); //操作 } catch( IOException e) { //异常处理 } catch (unknownHostException e) { //异常处理 } 4.public InetAddress getLocalAddress ( ) 此方法告诉用户套接字绑定到哪个网络接口。用户通常在多目录的主机或带有多目录的网络接口上使用这个方法。 5.public InputStream getInputStream( ) throws IOException 这个方法返回一个输入流,利用这个流就可以从套接字读取数据。通常链接这个流到一个BufferedInputStream或者BufferedReader。 6.public OutputStream getOutputStream ( ) throws IOException 返回一个原始的OutputStream,可以从应用程序写数据到套接字的另一端。通常将它链接到DataOutputStream或者OutputStreamWriter等更方便的类,还可以利用缓冲。示范代码如下: OutputStreamWriter out; try { Socket socket = new Socket ("www.sdhn.net",80 ); OutputStream outs=socket.getOutputStream ( ); BufferedOutputStream buffer=new BufferedOutputStream(outs); out =new OutputStreamWriter (buffer , "ASCII"); out.write("the java networking"); } catch (Exception e) { } finally { try { out.close( ); } catch (Exceptin e) { } } 7.public synchronized void close( ) throws IOException 虽然套接字会在两个数据流之一被关闭、程序结束时会被自动地关闭,但是我们应该用close()方法断开连接,特别是要运行无限长时间的程序时。关闭套接字的示范代码如下: Socket socket=null; try { socket=new Socket("www.sdhj.com",80 ); //套接字操作 } catch( IOException e) { //异常处理 } catch (unknownHostException e) { //异常处理 } finally { if (socket!=null) socket.close ( ); } 2 服务器套接字 每个服务器套接字运行在服务器上特定的端口,监听在这个端口的TCP连接。当远程客户端的Socket试图与服务器指定端口建立连接时,服务器被激活,判定客户程序的连接,并打开两个主机之间固有的连接。一旦客户端与服务器建立了连接,则两者之间就可以传送数据,而数据是通过这个固有的套接字传递的。 在ServerSocket类中包含了创建ServerSocket对象的构造方法、在指定端口监听的方法、建立连接后发送和接收数据的方法。 ServerSocket的工作过程如下: (1)用ServerSocket()方法在指定端口创建一个新的ServerSocket对象。 (2)ServerSocket对象调用accept( )方法在指定的端口监听到来的连接。accept( )一直处于阻塞状态,直到有客户端试图建立连接。这时accept( )方法返回连接客户端与服务器的Socket对象。 (3)调用getInputStream( )方法或者getOutputStream( )方法或者两者全调用建立与客户端交互的输入流和输出流。具体情况要看服务器的类型而定。 (4)服务器与客户端根据一定的协议交互,直到关闭连接。 (5)服务器、客户机或者两者都关闭连接。 (6)服务器回到第2步,继续监听下一次的连接。 下面我们介绍一下与客户端套接字不同的服务器套接字的方法。 构造方法如下: public ServerSocket(int port ) throws IOException BindException public ServerSocket(int port ,int queuelength) throws IOException BindException public ServerSocket(int port ,int queuelength,InetAddress bindaddress) throws IOException,BindException 这些构造方法允许指定端口,用来保存到来连接请求队列的长度,绑定本地网络的地址。如果想在端口5300创建一个服务器端口,同时使队列中所能存储的到来的请求数为100,则示范代码如下: try { ServerSocket socket=new ServerSocket(5300 ,100); } catch(IOException e) { //异常处理 } 常用方法如下: public Socket accept ( ) throws IOException 服务器建立并准备接收连接时,调用ServerSocket的 accept( )方法。这个方法是阻塞的:它停止执行流等待下一个客户端的连接。当客户端请求连接时,accept( )方法返回一个Socket对象。然后就用这个Socket对象的getInputStream( )和getOutputStream( )方法返回的流与客户端交互。示范代码如下: ServerSocket server=new ServerSocket( ); While (true) { Socket connection =server.accept ( ); OutputStream out=new OutputStream (connection.getOutputStream( )); out.write("the java networking" ); connection.close( ); } public void close ( ) throws IOException 实现了服务器套接字后要关闭它。 public InetAddress getInetAddress( ) public int getLocalPort ( ) 3 实例:C/S环境下的套接字应用程序 下面我们介绍客户/服务器环境下的套接字应用程序。 客户/服务器在分布处理过程中,使用基于连接的网络通信模式。该通信模式首先在客户机和服务器之间定义一套通信协议,并创建一个 Socket类,利用这个类建立一条可靠的连接;然后,客户/服务器再在这条连接上可靠地传输数据。客户机发出请求,服务器监听来自客户机的请求,并为客户机提供响应服务。这就是典型的"请求-应答"模式。下面是客户/服务器的一个典型运作过程: (1)服务器监听相应端口的输入。 (2)客户机发出一个请求。 (3)服务器接收到此请求。 (4)服务器处理这个请求,并把结果返回给客户机。 (5)重复上述过程,直至完成一次会话过程。 按照以上过程,我们使用Java语言编写一个分别针对服务器和客户机的应用程序(Application)。服务器程序负责监听客户机请求,为每个客户机请求建立Socket连接,从而为客户机提供服务。本程序提供的服务为:读取来自客户机的命令,根据客户机的命令,决定服务器要发给客户机的信息,并发送给客户机,见示例12-5。 【程序源代码】 1 // ==================== Program Description ======================== 2 // 程序名称:示例12-5: KKMultiServerThread.java 3 // 程序目的:创建server端线程类 4 //============================================================= 5 import java.net.*; 6 import java.io.*; 7 8 public class KKMultiServerThread extends Thread 9 { 10 private Socket socket = null; 11 12 public KKMultiServerThread(Socket socket) { 13 super("KKMultiServerThread"); 14 this.socket = socket; 15 } 16 17 public void run() 18 { 19 try { 20 PrintWriter out = new PrintWriter(socket.getOutputStream(), true); 21 BufferedReader in = new BufferedReader( new InputStreamReader( 22 socket.getInputStream())); 23 String inputLine, outputLine; 24 KnockKnockProtocol kkp = new KnockKnockProtocol(); 25 outputLine = kkp.processInput(null); 26 out.println(outputLine); 27 28 while ((inputLine = in.readLine()) != null) 29 { 30 outputLine = kkp.processInput(inputLine); 31 out.println(outputLine); 32 if (outputLine.equals("Bye")) 33 break; 34 } 35 out.close(); 36 in.close(); 37 socket.close(); 38 } catch (IOException e) { 39 e.printStackTrace(); 40 } 41 } 42 } 【程序注解】 类KKMultiServerThread继承自线程类,有一个Socket成员变量socket,在构造函数中初始化了socket变量。下面看看如何实现线程的run()方法。 首先定义了PrintWriter out和BufferedReader in,使输入输出流与socket相关联。然后声明了KnockKnockProtocol对象 kkp,读取客户端的命令,根据kkp中定义的协议,决定要发送给客户的信息,并发送出去。最后调用out.close()、in.close()和 socket.close()关闭输入输出流和套接字。 示例12-6是多线程服务器程序。 【程序源代码】 1 // ==================== Program Description ===================== 2 // 程序名称:示例12-6: KKMultiServer.java 3 // 程序目的:多线程服务器 4 //========================================================= 5 import java.net.*; 6 import java.io.*; 7 8 public class KKMultiServer 9 { 10 public static void main(String[] args) throws IOException 11 { 12 ServerSocket serverSocket = null; 13 boolean listening = true; 14 15 try { 16 serverSocket = new ServerSocket(4444); 17 } catch (IOException e) { 18 System.err.println("Could not listen on port: 4444."); 19 System.exit(-1); 20 } 21 22 while (listening) 23 new KKMultiServerThread(serverSocket.accept()).start(); 24 25 serverSocket.close(); 26 } 27 } 【程序注解】 第12行声明了一个ServerSocket对象serverSocket。第16行实例化了 serverSocket,监听端口为4444。然后就是监听客户端的连接(使用serverSocket.accept()方法)。如果有客户端的连接,则serverSocket.accept()就返回一个Socket套接字,以这个套接字实例化一个KKMultiServerThread对象,实际处理客户端的数据。 下面的程序示例12-7实现了服务器端与客户端交互的协议。 【程序源代码】 1 // ==================== Program Description ===================== 2 // 程序名称:示例12-7: KnockKnockProtocol.java 3 // 程序目的:服务端与客户端相互交互的协议 4 //============================================================= 5 import java.net.*; 6 import java.io.*; 7 8 public class KnockKnockProtocol 9 { 10 private static final int WAITING = 0; 11 private static final int SENTKNOCKKNOCK = 1; 12 private static final int SENTCLUE = 2; 13 private static final int ANOTHER = 3; 14 15 private static final int NUMJOKES = 5; 16 17 private int state = WAITING; 18 private int currentJoke = 0; 19 20 private String[] clues = { "Turnip", "Little Old Lady", "Atch", "Who", "Who" }; 21 private String[] answers = { "Turnip the heat, it's cold in here!", 22 "I didn't know you could yodel!", 23 "Bless you!", 24 "Is there an owl in here?", 25 "Is there an echo in here?" }; 26 27 public String processInput(String theInput) 28 { 29 String theOutput = null; 30 31 if (state == WAITING) { 32 theOutput = "Knock! Knock!"; 33 state = SENTKNOCKKNOCK; 34 } 35 else if (state == SENTKNOCKKNOCK) { 36 if (theInput.equalsIgnoreCase("Who's there?")) { 37 theOutput = clues[currentJoke]; 38 state = SENTCLUE; 39 } 40 else { 41 theOutput = "You're supposed to say "Who's there?"! " + 42 "Try again. Knock! Knock!"; 43 } 44 } 45 else if (state == SENTCLUE) { 46 if (theInput.equalsIgnoreCase(clues[currentJoke] + " who?")) { 47 theOutput = answers[currentJoke] + " Want another? (y/n)"; 48 state = ANOTHER; 49 } else { 50 theOutput = "You're supposed to say "" + clues[currentJoke] + " who?"" + 51 "! Try again. Knock! Knock!"; 52 state = SENTKNOCKKNOCK; 53 } 54 } 55 else if (state == ANOTHER) { 56 if (theInput.equalsIgnoreCase("y")) { 57 theOutput = "Knock! Knock!"; 58 if (currentJoke == (NUMJOKES - 1)) 59 currentJoke = 0; 60 else 61 currentJoke++; 62 state = SENTKNOCKKNOCK; 63 } 64 else { 65 theOutput = "Bye."; 66 state = WAITING; 67 } 68 } 69 return theOutput; 70 } 71 } 【程序注解】 KnockKnockProtocol类具体定义了客户端与服务器交互的协议。定义了两个字符串数组clues = { "Turnip", "Little Old Lady", "Atch", "Who", "Who" }; answers = { "Turnip the heat, it's cold in here!", "I didn't know you could yodel!", "Bless you!", "Is there an owl in here?", "Is there an echo in here?" }。在processInput(String theInput)方法中根据这两个字符串和客户端的命令String theInput 决定要发送给客户端的字符串。程序假定了客户端应该发送的字符串,如果发现接收的字符串不符合,则返回提醒的字符串,交由socket发送给客户端。 示例12-8是客户端源程序。 【程序源代码】 1 // ==================== Program Description ===================== 2 // 程序名称:示例12-8: KnockKnockClient.java 3 // 程序目的:客户端源程序 4 //========================================================= 5 import java.io.*; 6 import java.net.*; 7 8 public class KnockKnockClient 9 { 10 public static void main(String[] args) throws IOException 11 { 12 Socket kkSocket = null; 13 PrintWriter out = null; 14 BufferedReader in = null; 15 16 try { 17 kkSocket = new Socket("hostname", 4444); 18 out = new PrintWriter(kkSocket.getOutputStream(), true); 19 in = new BufferedReader(new InputStreamReader(kkSocket.getInputStream())); 20 } 21 catch (UnknownHostException e) { 22 System.err.println("Don't know about host: taranis."); 23 System.exit(1); 24 } 25 catch (IOException e) { 26 System.err.println("Couldn't get I/O for the connection to: biao."); 27 System.exit(1); 28 } 29 30 BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in)); 31 String fromServer; 32 String fromUser; 33 34 while ((fromServer = in.readLine()) != null) 35 { 36 System.out.println("Server: " + fromServer); 37 if (fromServer.equals("Bye.")) 38 break; 39 40 fromUser = stdIn.readLine(); 41 if (fromUser != null) { 42 System.out.println("Client: " + fromUser); 43 out.println(fromUser); 44 } 45 } 46 47 out.close(); 48 in.close(); 49 stdIn.close(); 50 kkSocket.close(); 51 } 52 } 【程序输出结果】如下所示。 Server: Knock! Knock! Who's there? Client: Who's there? Server: Turnip Turnip who? Client: Turnip who? Server: Turnip the heat, it's cold in here! Want another? (y/n) 【程序注解】 在客户端程序中,第17行建立了一个套接字与服务器保持联系,然后用一个PrintWriter和一个BufferedReader与套接字相关联,以便能更好地与服务器相交互。而BufferedReader stdIn与客户端的标准输入System.in相关联。在while循环中,处理从服务器读取的数据,并把从stdIn中读取的数据发给服务器。最后调用out.close() 、in.close()、stdIn.close() 和kkSocket. close( )关闭套接字和输入输出流。 客户端假定按先后顺序输入为"who's there ?"和"Turnip who?",如果你不是这样输入的话,服务器端不认识你输入的命令,会提示你该输入的命令。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics