码迷,mamicode.com
首页 > 编程语言 > 详细

java socket编程

时间:2015-04-03 09:23:52      阅读:153      评论:0      收藏:0      [点我收藏+]

标签:

一,网络编程中两个主要的问题

一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输。

在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机。

而TCP层则提供面向应用的可靠(tcp)的或非可靠(UDP)的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。

目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务器等待客户提出请求并予以响应。客户则在需要服务时向服务器提 出申请。服务器一般作为守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的客户也 能及时得到服务。

二,两类传输协议:TCP;UDP

TCP是Tranfer Control Protocol的 简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建 立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。

UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。

比较:

UDP:1,每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。

            2,UDP传输数据时是大小限制的,每个被传输的数据报必须限定在64KB之内。

           3,UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方

TCP:1,面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接

                时间。

            2,TCP传输数据大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的  

                    数据。

             3,TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。

应用:

1,TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。但是可靠的传输是要付出代价的,对数据内容正确性的检验必然占用计算机的处理时间和网络的带宽,因此TCP传输的效率不如UDP

2,UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

三,基于Socket的java网络编程

1,什么是Socket

网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。

但是,Socket所支持的协议种类也不光TCP/IP一种,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。

2,Socket通讯的过程

Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:

  (1) 创建Socket;

  (2) 打开连接到Socket的输入/出流;

  (3) 按照一定的协议对Socket进行读/写操作;

  (4) 关闭Socket.(在实际应用中,并未使用到显示的close,虽然很多文章都推荐如此,不过在我的程序中,可能因为程序本身比较简单,要求不高,所以并未造成什么影响。)

3,创建Socket

创建Socket

java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。其构造方法如下:

  Socket(InetAddress address, int port);

  Socket(InetAddress address, int port, boolean stream);

  Socket(String host, int prot);

  Socket(String host, int prot, boolean stream);

  Socket(SocketImpl impl)

  Socket(String host, int port, InetAddress localAddr, int localPort)

  Socket(InetAddress address, int port, InetAddress localAddr, int localPort)

  ServerSocket(int port);

  ServerSocket(int port, int backlog);

  ServerSocket(int port, int backlog, InetAddress bindAddr)

  其中address、host和port分别是双向连接中另一方的IP地址、主机名和端 口号,stream指明socket是流socket还是数据报socket,localPort表示本地主机的端口号,localAddr和 bindAddr是本地机器的地址(ServerSocket的主机地址),impl是socket的父类,既可以用来创建serverSocket又可 以用来创建Socket。count则表示服务端所能支持的最大连接数。例如:学习视频网 http://www.xxspw.com

  Socket client = new Socket("127.0.01.", 80);

  ServerSocket server = new ServerSocket(80);

  注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才 能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们在选择端口号时,最好选择一个大于1023的数以防止发生冲突。

  在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。

4,简单的Client/Server程序

1. 客户端程序

  import java.io.*;

  import java.net.*;

  public class TalkClient {

    public static void main(String args[]) {

      try{

        Socket socket=new Socket("127.0.0.1",4700);

        //向本机的4700端口发出客户请求

        BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));

        //由系统标准输入设备构造BufferedReader对象

        PrintWriter os=new PrintWriter(socket.getOutputStream());

        //由Socket对象得到输出流,并构造PrintWriter对象

        BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));

        //由Socket对象得到输入流,并构造相应的BufferedReader对象

        String readline;

        readline=sin.readLine(); //从系统标准输入读入一字符串

        while(!readline.equals("bye")){

        //若从标准输入读入的字符串为 "bye"则停止循环

          os.println(readline);

          //将从系统标准输入读入的字符串输出到Server

          os.flush();

          //刷新输出流,使Server马上收到该字符串

          System.out.println("Client:"+readline);

          //在系统标准输出上打印读入的字符串

          System.out.println("Server:"+is.readLine());

          //从Server读入一字符串,并打印到标准输出上

          readline=sin.readLine(); //从系统标准输入读入一字符串

        } //继续循环

        os.close(); //关闭Socket输出流

        is.close(); //关闭Socket输入流

        socket.close(); //关闭Socket

      }catch(Exception e) {

        System.out.println("Error"+e); //出错,则打印出错信息

      }

  }

}

 2. 服务器端程序

  import java.io.*;

  import java.net.*;

  import java.applet.Applet;

  public class TalkServer{

    public static void main(String args[]) {

      try{

        ServerSocket server=null;

        try{

          server=new ServerSocket(4700);

        //创建一个ServerSocket在端口4700监听客户请求

        }catch(Exception e) {

          System.out.println("can not listen to:"+e);

        //出错,打印出错信息

        }

        Socket socket=null;

        try{

          socket=server.accept();

          //使用accept()阻塞等待客户请求,有客户

          //请求到来则产生一个Socket对象,并继续执行

        }catch(Exception e) {

          System.out.println("Error."+e);

          //出错,打印出错信息

        }

        String line;

        BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));

         //由Socket对象得到输入流,并构造相应的BufferedReader对象

        PrintWriter os=newPrintWriter(socket.getOutputStream());

         //由Socket对象得到输出流,并构造PrintWriter对象

        BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));

         //由系统标准输入设备构造BufferedReader对象

        System.out.println("Client:"+is.readLine());

        //在标准输出上打印从客户端读入的字符串

        line=sin.readLine();

        //从标准输入读入一字符串

        while(!line.equals("bye")){

        //如果该字符串为 "bye",则停止循环

          os.println(line);

          //向客户端输出该字符串

          os.flush();

          //刷新输出流,使Client马上收到该字符串

          System.out.println("Server:"+line);

          //在系统标准输出上打印读入的字符串

          System.out.println("Client:"+is.readLine());

          //从Client读入一字符串,并打印到标准输出上

          line=sin.readLine();

          //从系统标准输入读入一字符串

        }  //继续循环

        os.close(); //关闭Socket输出流

        is.close(); //关闭Socket输入流

        socket.close(); //关闭Socket

        server.close(); //关闭ServerSocket

      }catch(Exception e){

        System.out.println("Error:"+e);

        //出错,打印出错信息

      }

    }

  }

5,支持多客户的client/server程序

前面的Client/Server程序只能实现Server和一个客户的对话。在实际应用 中,往往是在服务器上运行一个永久的程序,它可以接收来自其他多个客户端的请求,提供相应的服务。为了实现在服务器方给多个客户提供服务的功能,需要对上 面的程序进行改造,利用多线程实现多客户机制。服务器总是在指定的端口上监听是否有客户请求,一旦监听到客户请求,服务器就会启动一个专门的服务线程来响 应该客户的请求,而服务器本身在启动完线程之后马上又进入监听状态,等待下一个客户的到来。

 

来源:http://haohaoxuexi.iteye.com/blog/1979837

Java Socket编程

       对于Java Socket编程而言,有两个概念,一个是ServerSocket,一个是Socket。服务端和客户端之间通过Socket建立连接,之后它们就可以进行通信了。首先ServerSocket将在服务端监听某个端口,当发现客户端有Socket来试图连接它时,它会accept该Socket的连接请求,同时在服务端建立一个对应的Socket与之进行通信。这样就有两个Socket了,客户端和服务端各一个。

       对于Socket之间的通信其实很简单,服务端往Socket的输出流里面写东西,客户端就可以通过Socket的输入流读取对应的内容。Socket与Socket之间是双向连通的,所以客户端也可以往对应的Socket输出流里面写东西,然后服务端对应的Socket的输入流就可以读出对应的内容。下面来看一些服务端与客户端通信的例子:

      1、客户端写服务端读

       服务端代码

 

Java代码  

1 public class Server {  

2    

3    public static void main(String args[]) throws IOException {  

4       //为了简单起见,所有的异常信息都往外抛  

5       int port = 8899;  

6       //定义一个ServerSocket监听在端口8899上  

7       ServerSocket server = new ServerSocket(port);  

8       //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的  

9       Socket socket = server.accept();  

10       //跟客户端建立好连接之后,我们就可以获取socket的InputStream,并从中读取客户端发过来的信息了。  

11       Reader reader = new InputStreamReader(socket.getInputStream());  

12       char chars[] = new char[64];  

13       int len;  

14       StringBuilder sb = new StringBuilder();  

15       while ((len=reader.read(chars)) != -1) {  

16          sb.append(new String(chars, 0, len));  

17       }  

18       System.out.println("from client: " + sb);  

19       reader.close();  

20       socket.close();  

21       server.close();  

22    }  

23      

24 }  

        

服务端从Socket的InputStream中读取数据的操作也是阻塞式的,如果从输入流中没有读取到数据程序会一直在那里不动,直到客户端往Socket的输出流中写入了数据,或关闭了Socket的输出流。当然,对于客户端的Socket也是同样如此。在操作完以后,整个程序结束前记得关闭对应的资源,即关闭对应的IO流和Socket。

 

       客户端代码

Java代码  

25 public class Client {  

26    

27    public static void main(String args[]) throws Exception {  

28       //为了简单起见,所有的异常都直接往外抛  

29       String host = "127.0.0.1";  //要连接的服务端IP地址  

30       int port = 8899;   //要连接的服务端对应的监听端口  

31       //与服务端建立连接  

32       Socket client = new Socket(host, port);  

33       //建立连接后就可以往服务端写数据了  

34       Writer writer = new OutputStreamWriter(client.getOutputStream());  

35       writer.write("Hello Server.");  

36       writer.flush();//写完后要记得flush  

37       writer.close();  

38       client.close();  

39    }  

40      

41 }  

       

对于客户端往Socket的输出流里面写数据传递给服务端要注意一点,如果写操作之后程序不是对应着输出流的关闭,而是进行其他阻塞式的操作(比如从输入流里面读数据),记住要flush一下,只有这样服务端才能收到客户端发送的数据,否则可能会引起两边无限的互相等待。在稍后讲到客户端和服务端同时读和写的时候会说到这个问题。 

      2、客户端和服务端同时读和写

       前面已经说了Socket之间是双向通信的,它既可以接收数据,同时也可以发送数据。

       服务端代码

 

Java代码  

42 public class Server {  

43    

44    public static void main(String args[]) throws IOException {  

45       //为了简单起见,所有的异常信息都往外抛  

46       int port = 8899;  

47       //定义一个ServerSocket监听在端口8899上  

48       ServerSocket server = new ServerSocket(port);  

49       //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的  

50       Socket socket = server.accept();  

51       //跟客户端建立好连接之后,我们就可以获取socket的InputStream,并从中读取客户端发过来的信息了。  

52       Reader reader = new InputStreamReader(socket.getInputStream());  

53       char chars[] = new char[64];  

54       int len;  

55       StringBuilder sb = new StringBuilder();  

56       while ((len=reader.read(chars)) != -1) {  

57          sb.append(new String(chars, 0, len));  

58       }  

59       System.out.println("from client: " + sb);  

60       //读完后写一句  

61       Writer writer = new OutputStreamWriter(socket.getOutputStream());  

62       writer.write("Hello Client.");  

63       writer.flush();  

64       writer.close();  

65       reader.close();  

66       socket.close();  

67       server.close();  

68    }  

69      

70 }  

        

在上述代码中首先我们从输入流中读取客户端发送过来的数据,接下来我们再往输出流里面写入数据给客户端,接下来关闭对应的资源文件。而实际上上述代码可能并不会按照我们预先设想的方式运行,因为从输入流中读取数据是一个阻塞式操作,在上述的while循环中当读到数据的时候就会执行循环体,否则就会阻塞,这样后面的写操作就永远都执行不了了。除非客户端对应的Socket关闭了阻塞才会停止,while循环也会跳出。针对这种可能永远无法执行下去的情况的解决方法是while循环需要在里面有条件的跳出来,纵观上述代码,在不断变化的也只有取到的长度len和读到的数据了,len已经是不能用的了,唯一能用的就是读到的数据了。针对这种情况,通常我们都会约定一个结束标记,当客户端发送过来的数据包含某个结束标记时就说明当前的数据已经发送完毕了,这个时候我们就可以进行循环的跳出了。那么改进后的代码会是这个样子:

Java代码  

71 public class Server {  

72    

73    public static void main(String args[]) throws IOException {  

74       //为了简单起见,所有的异常信息都往外抛  

75       int port = 8899;  

76       //定义一个ServerSocket监听在端口8899上  

77       ServerSocket server = new ServerSocket(port);  

78       //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的  

79       Socket socket = server.accept();  

80       //跟客户端建立好连接之后,我们就可以获取socket的InputStream,并从中读取客户端发过来的信息了。  

81       Reader reader = new InputStreamReader(socket.getInputStream());  

82       char chars[] = new char[64];  

83       int len;  

84       StringBuilder sb = new StringBuilder();  

85       String temp;  

86       int index;  

87       while ((len=reader.read(chars)) != -1) {  

88          temp = new String(chars, 0, len);  

89          if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收  

90             sb.append(temp.substring(0, index));  

91             break;  

92          }  

93          sb.append(temp);  

94       }  

95       System.out.println("from client: " + sb);  

96       //读完后写一句  

97       Writer writer = new OutputStreamWriter(socket.getOutputStream());  

98       writer.write("Hello Client.");  

99       writer.flush();  

100       writer.close();  

101       reader.close();  

102       socket.close();  

103       server.close();  

104    }  

105      

106 }  

       

在上述代码中,当服务端读取到客户端发送的结束标记,即“eof”时就会结束数据的接收,终止循环,这样后续的代码又可以继续进行了。 

       客户端代码

Java代码  

107 public class Client {  

108    

109    public static void main(String args[]) throws Exception {  

110       //为了简单起见,所有的异常都直接往外抛  

111      String host = "127.0.0.1";  //要连接的服务端IP地址  

112      int port = 8899;   //要连接的服务端对应的监听端口  

113      //与服务端建立连接  

114      Socket client = new Socket(host, port);  

115       //建立连接后就可以往服务端写数据了  

116      Writer writer = new OutputStreamWriter(client.getOutputStream());  

117       writer.write("Hello Server.");  

118       writer.flush();  

119       //写完以后进行读操作  

120      Reader reader = new InputStreamReader(client.getInputStream());  

121       char chars[] = new char[64];  

122       int len;  

123       StringBuffer sb = new StringBuffer();  

124       while ((len=reader.read(chars)) != -1) {  

125          sb.append(new String(chars, 0, len));  

126       }  

127       System.out.println("from server: " + sb);  

128       writer.close();  

129       reader.close();  

130       client.close();  

131    }  

132      

133 }  

       

在上述代码中我们先是给服务端发送了一段数据,之后读取服务端返回来的数据,跟之前的服务端一样在读的过程中有可能导致程序一直挂在那里,永远跳不出while循环。这段代码配合服务端的第一段代码就正好让我们分析服务端永远在那里接收数据,永远跳不出while循 环,也就没有之后的服务端返回数据给客户端,客户端也就不可能接收到服务端返回的数据。解决方法如服务端第二段代码所示,在客户端发送数据完毕后,往输出 流里面写入结束标记告诉服务端数据已经发送完毕了,同样服务端返回数据完毕后也发一个标记告诉客户端。那么修改后的客户端代码就应该是这个样子:Java代码  

134 public class Client {  

135    

136    public static void main(String args[]) throws Exception {  

137       //为了简单起见,所有的异常都直接往外抛  

138      String host = "127.0.0.1";  //要连接的服务端IP地址  

139      int port = 8899;   //要连接的服务端对应的监听端口  

140      //与服务端建立连接  

141      Socket client = new Socket(host, port);  

142       //建立连接后就可以往服务端写数据了  

143      Writer writer = new OutputStreamWriter(client.getOutputStream());  

144       writer.write("Hello Server.");  

145       writer.write("eof");  

146       writer.flush();  

147       //写完以后进行读操作  

148      Reader reader = new InputStreamReader(client.getInputStream());  

149       char chars[] = new char[64];  

150       int len;  

151       StringBuffer sb = new StringBuffer();  

152       String temp;  

153       int index;  

154       while ((len=reader.read(chars)) != -1) {  

155          temp = new String(chars, 0, len);  

156          if ((index = temp.indexOf("eof")) != -1) {  

157             sb.append(temp.substring(0, index));  

158             break;  

159          }  

160          sb.append(new String(chars, 0, len));  

161       }  

162       System.out.println("from server: " + sb);  

163       writer.close();  

164       reader.close();  

165       client.close();  

166    }  

167      

168 }  

169    

       

我们日常使用的比较多的都是这种客户端发送数据给服务端,服务端接收数据后再返回相应的结果给客户端这种形式。只是客户端和服务端之间不再是这种一对一的关系,而是下面要讲到的多个客户端对应同一个服务端的情况。      3、多个客户端连接同一个服务端

       像前面讲的两个例子都是服务端接收一个客户端的请求之后就结束了,不能再接收其他客户端的请求了,这往往是不能满足我们的要求的。通常我们会这样做:

Java代码  

170 public class Server {  

171    

172    public static void main(String args[]) throws IOException {  

173       //为了简单起见,所有的异常信息都往外抛  

174      int port = 8899;  

175       //定义一个ServerSocket监听在端口8899上  

176      ServerSocket server = new ServerSocket(port);  

177       while (true) {  

178          //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的  

179        Socket socket = server.accept();  

180          //跟客户端建立好连接之后,我们就可以获取socket的InputStream,并从中读取客户端发过来的信息了。  

181        Reader reader = new InputStreamReader(socket.getInputStream());  

182          char chars[] = new char[64];  

183          int len;  

184          StringBuilder sb = new StringBuilder();  

185          String temp;  

186          int index;  

187          while ((len=reader.read(chars)) != -1) {  

188             temp = new String(chars, 0, len);  

189             if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收  

190                 sb.append(temp.substring(0, index));  

191                 break;  

192             }  

193             sb.append(temp);  

194          }  

195          System.out.println("from client: " + sb);  

196          //读完后写一句  

197        Writer writer = new OutputStreamWriter(socket.getOutputStream());  

198          writer.write("Hello Client.");  

199          writer.flush();  

200          writer.close();  

201          reader.close();  

202          socket.close();  

203       }  

204    }  

205      

206 }  

       

在上面代码中我们用了一个死循环,在循环体里面ServerSocket调用其accept方法试图接收来自客户端的连接请求。当没有接收到请求的时候,程序会在这里阻塞直到接收到来自客户端的连接请求,之后会跟当前建立好连接的客户端进行通信,完了后会接着执行循环体再次尝试接收新的连接请求。这样我们的ServerSocket就能接收来自所有客户端的连接请求了,并且与它们进行通信了。这就实现了一个简单的一个服务端与多个客户端进行通信的模式。       上 述例子中虽然实现了一个服务端跟多个客户端进行通信,但是还存在一个问题。在上述例子中,我们的服务端处理客户端的连接请求是同步进行的,每次接收到来自 客户端的连接请求后,都要先跟当前的客户端通信完之后才能再处理下一个连接请求。这在并发比较多的情况下会严重影响程序的性能,为此,我们可以把它改为如 下这种异步处理与客户端通信的方式:

Java代码  

207 public class Server {  

208      

209    public static void main(String args[]) throws IOException {  

210       //为了简单起见,所有的异常信息都往外抛  

211      int port = 8899;  

212       //定义一个ServerSocket监听在端口8899上  

213      ServerSocket server = new ServerSocket(port);  

214       while (true) {  

215          //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的  

216          Socket socket = server.accept();  

217          //每接收到一个Socket就建立一个新的线程来处理它  

218          new Thread(new Task(socket)).start();  

219       }  

220    }  

221      

222    /** 

223     * 用来处理Socket请求的 

224    */  

225    static class Task implements Runnable {  

226    

227       private Socket socket;  

228         

229       public Task(Socket socket) {  

230          this.socket = socket;  

231       }  

232         

233       public void run() {  

234   

235          try {  

236   

237             handleSocket();  

238          } catch (Exception e) {  

239             e.printStackTrace();  

240          }  

241       }  

242         

243       /** 

244        * 跟客户端Socket进行通信 

245        * @throws Exception 

246        */  

247       private void handleSocket() throws Exception {  

248          Reader reader = new InputStreamReader(socket.getInputStream());  

249          char chars[] = new char[64];  

250          int len;  

251          StringBuilder sb = new StringBuilder();  

252          String temp;  

253          int index;  

254          while ((len=reader.read(chars)) != -1) {  

255             temp = new String(chars, 0, len);  

256             if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收  

257              sb.append(temp.substring(0, index));  

258                 break;  

259             }  

260             sb.append(temp);  

261          }  

262          System.out.println("from client: " + sb);  

263          //读完后写一句  

264        Writer writer = new OutputStreamWriter(socket.getOutputStream());  

265          writer.write("Hello Client.");  

266          writer.flush();  

267          writer.close();  

268          reader.close();  

269          socket.close();  

270       }  

271         

272    }  

273      

274 }  

       

在上面代码中,每次ServerSocket接收到一个新的Socket连接请求后都会新起一个线程来跟当前Socket进行通信,这样就达到了异步处理与客户端Socket进行通信的情况。       在从Socket的InputStream中接收数据时,像上面那样一点点的读就太复杂了,有时候我们就会换成使用BufferedReader来一次读一行,如:

Java代码  

275 public class Server {  

276    

277    public static void main(String args[]) throws IOException {  

278       //为了简单起见,所有的异常信息都往外抛  

279      int port = 8899;  

280       //定义一个ServerSocket监听在端口8899上  

281      ServerSocket server = new ServerSocket(port);  

282       while (true) {  

283          //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的  

284          Socket socket = server.accept();  

285          //每接收到一个Socket就建立一个新的线程来处理它  

286          new Thread(new Task(socket)).start();  

287       }  

288    }  

289      

290    /** 

291     * 用来处理Socket请求的 

292    */  

293    static class Task implements Runnable {  

294    

295       private Socket socket;  

296         

297       public Task(Socket socket) {  

298          this.socket = socket;  

299       }  

300         

301       public void run() {  

302          try {  

303             handleSocket();  

304          } catch (Exception e) {  

305             e.printStackTrace();  

306          }  

307       }  

308         

309       /** 

310        * 跟客户端Socket进行通信 

311       * @throws Exception 

312        */  

313       private void handleSocket() throws Exception {  

314          BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));  

315          StringBuilder sb = new StringBuilder();  

316          String temp;  

317          int index;  

318          while ((temp=br.readLine()) != null) {  

319             System.out.println(temp);  

320             if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收  

321              sb.append(temp.substring(0, index));  

322                 break;  

323             }  

324             sb.append(temp);  

325          }  

326          System.out.println("from client: " + sb);  

327          //读完后写一句  

328        Writer writer = new OutputStreamWriter(socket.getOutputStream());  

329          writer.write("Hello Client.");  

330          writer.write("eof\n");  

331          writer.flush();  

332          writer.close();  

333          br.close();  

334          socket.close();  

335       }  

336    }  

337 }  

       

这个时候需要注意的是,BufferedReaderreadLine方法是一次读一行的,这个方法是阻塞的,直到它读到了一行数据为止程序才会继续往下执行,那么readLine什么时候才会读到一行呢?直到程序遇到了换行符或者是对应流的结束符readLine方法才会认为读到了一行,才会结束其阻塞,让程序继续往下执行。所以我们在使用BufferedReader的readLine读取数据的时候一定要记得在对应的输出流里面一定要写入换行符(流结束之后会自动标记为结束,readLine可以识别),写入换行符之后一定记得如果输出流不是马上关闭的情况下记得flush一下,这样数据才会真正的从缓冲区里面写入。对应上面的代码我们的客户端程序应该这样写:Java代码  

338 public class Client {  

339   

340    public static void main(String args[]) throws Exception {  

341       //为了简单起见,所有的异常都直接往外抛  

342      String host = "127.0.0.1";  //要连接的服务端IP地址  

343      int port = 8899;   //要连接的服务端对应的监听端口  

344      //与服务端建立连接  

345      Socket client = new Socket(host, port);  

346       //建立连接后就可以往服务端写数据了  

347      Writer writer = new OutputStreamWriter(client.getOutputStream());  

348       writer.write("Hello Server.");  

349       writer.write("eof\n");  

350       writer.flush();  

351       //写完以后进行读操作  

352      BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));  

353       StringBuffer sb = new StringBuffer();  

354       String temp;  

355       int index;  

356       while ((temp=br.readLine()) != null) {  

357          if ((index = temp.indexOf("eof")) != -1) {  

358             sb.append(temp.substring(0, index));  

359             break;  

360          }  

361          sb.append(temp);  

362       }  

363       System.out.println("from server: " + sb);  

364       writer.close();  

365       br.close();  

366       client.close();  

367    }  

368 }  

  

      4、设置超时时间

       假设有这样一种需求,我们的客户端需要通过Socket从服务端获取到XX信息,然后给用户展示在页面上。我们知道Socket在读数据的时候是阻塞式的,如果没有读到数据程序会一直阻塞在那里。在同步请求的时候我们肯定是不能允许这样的情况发生的,这就需要我们在请求达到一定的时间后控制阻塞的中断,让程序得以继续运行。Socket为我们提供了一个setSoTimeout()方法来设置接收数据的超时时间,单位是毫秒。当设置的超时时间大于0,并且超过了这一时间Socket还没有接收到返回的数据的话,Socket就会抛出一个SocketTimeoutException。

       假设我们需要控制我们的客户端在开始读取数据10秒后还没有读到数据就中断阻塞的话我们可以这样做:

 

Java代码  

369 public class Client {  

370    

371    public static void main(String args[]) throws Exception {  

372       //为了简单起见,所有的异常都直接往外抛  

373      String host = "127.0.0.1";  //要连接的服务端IP地址  

374      int port = 8899;   //要连接的服务端对应的监听端口  

375      //与服务端建立连接  

376      Socket client = new Socket(host, port);  

377       //建立连接后就可以往服务端写数据了  

378      Writer writer = new OutputStreamWriter(client.getOutputStream());  

379       writer.write("Hello Server.");  

380       writer.write("eof\n");  

381       writer.flush();  

382       //写完以后进行读操作  

383      BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream()));  

384       //设置超时间为10秒  

385      client.setSoTimeout(10*1000);  

386       StringBuffer sb = new StringBuffer();  

387       String temp;  

388       int index;  

389       try {  

390          while ((temp=br.readLine()) != null) {  

391             if ((index = temp.indexOf("eof")) != -1) {  

392                 sb.append(temp.substring(0, index));  

393                 break;  

394             }  

395             sb.append(temp);  

396          }  

397       } catch (SocketTimeoutException e) {  

398          System.out.println("数据读取超时。");  

399       }  

400       System.out.println("from server: " + sb);  

401       writer.close();  

402       br.close();  

403       client.close();  

404    }  

405 }  

406   

407    

       5、接收数据乱码

       对于这种服务端或客户端接收中文乱码的情况通常是因为数据发送时使用的编码跟接收时候使用的编码不一致。比如有下面这样一段服务端代码:

Java代码  

408 public class Server {  

409    

410    public static void main(String args[]) throws IOException {  

411       //为了简单起见,所有的异常信息都往外抛  

412       int port = 8899;  

413       //定义一个ServerSocket监听在端口8899上  

414       ServerSocket server = new ServerSocket(port);  

415       while (true) {  

416          //server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的  

417          Socket socket = server.accept();  

418          //每接收到一个Socket就建立一个新的线程来处理它  

419          new Thread(new Task(socket)).start();  

420       }  

421    }  

422      

423    /** 

424     * 用来处理Socket请求的 

425     */  

426    static class Task implements Runnable {  

427    

428       private Socket socket;  

429         

430       public Task(Socket socket) {  

431          this.socket = socket;  

432       }  

433         

434       public void run() {  

435          try {  

436             handleSocket();  

437          } catch (Exception e) {  

438             e.printStackTrace();  

439          }  

440       }  

441         

442       /** 

443        * 跟客户端Socket进行通信 

444       * @throws Exception 

445        */  

446       private void handleSocket() throws Exception {  

447          BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "GBK"));  

448          StringBuilder sb = new StringBuilder();  

449          String temp;  

450          int index;  

451          while ((temp=br.readLine()) != null) {  

452             System.out.println(temp);  

453             if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收  

454              sb.append(temp.substring(0, index));  

455                 break;  

456             }  

457             sb.append(temp);  

458          }  

459          System.out.println("客户端: " + sb);  

460          //读完后写一句  

461        Writer writer = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");  

462          writer.write("你好,客户端。");  

463          writer.write("eof\n");  

464          writer.flush();  

465          writer.close();  

466          br.close();  

467          socket.close();  

468       }  

469    }  

470 }  

       

这里用来测试我就弄的混乱了一点。在上面服务端代码中我们在定义输入流的时候明确定义了使用GBK编码来读取数据,而在定义输出流的时候明确指定了将使用UTF-8编码来发送数据。如果客户端上送数据的时候不以GBK编码来发送的话服务端接收的数据就很有可能会乱码;同样如果客户端接收数据的时候不以服务端发送数据的编码,即UTF-8编码来接收数据的话也极有可能会出现数据乱码的情况。所以,对于上述服务端代码,为使我们的程序能够读取对方发送过来的数据,而不出现乱码情况,我们的客户端应该是这样的:Java代码  

471 public class Client {  

472    

473    public static void main(String args[]) throws Exception {  

474       //为了简单起见,所有的异常都直接往外抛  

475      String host = "127.0.0.1";  //要连接的服务端IP地址  

476      int port = 8899;   //要连接的服务端对应的监听端口  

477      //与服务端建立连接  

478      Socket client = new Socket(host, port);  

479       //建立连接后就可以往服务端写数据了  

480      Writer writer = new OutputStreamWriter(client.getOutputStream(), "GBK");  

481       writer.write("你好,服务端。");  

482       writer.write("eof\n");  

483       writer.flush();  

484       //写完以后进行读操作  

485      BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));  

486       //设置超时间为10秒  

487      client.setSoTimeout(10*1000);  

488       StringBuffer sb = new StringBuffer();  

489       String temp;  

490       int index;  

491       try {  

492          while ((temp=br.readLine()) != null) {  

493             if ((index = temp.indexOf("eof")) != -1) {  

494                 sb.append(temp.substring(0, index));  

495                 break;  

496             }  

497             sb.append(temp);  

498          }  

499       } catch (SocketTimeoutException e) {  

500          System.out.println("数据读取超时。");  

501       }  

502       System.out.println("服务端: " + sb);  

503       writer.close();  

504       br.close();  

505       client.close();  

506    }  

507 }  

 

java socket编程

标签:

原文地址:http://blog.csdn.net/network_boy/article/details/44839779

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!