TCP客户服务端

创建TCP服务端
1.创建一个ServerSocket对象。
2.调用accept()方法接收客户端请求。
3.从Socket中获取I/O流。
4.对I/O流进行读写操作,完成与客户端的交互。
5.关闭I/O流和Socket

 1 import java.io.InputStream;
 2 import java.io.OutputStream;
 3 import java.net.ServerSocket;
 4 import java.net.Socket;
 5 
 6 public class Server {
 7   public static void main(String[] args) throws Exception {
 8     // 监听指定的端口
 9     int port = 55533;
10     ServerSocket server = new ServerSocket(port);
11     
12     // server将一直等待连接的到来
13     System.out.println("server将一直等待连接的到来");
14     Socket socket = server.accept();
15     // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
16     InputStream inputStream = socket.getInputStream();
17     byte[] bytes = new byte[1024];
18     int len;
19     inputStream.read(bytes);
20 //    StringBuilder s = new StringBuilder();
21     String s=null;
22     //只有当客户端关闭它的输出流的时候,服务端才能取得结尾的-1
23     while ((len = inputStream.read(bytes)) != -1) {
24       // 注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
25 //      s.append(new String(bytes, 0, len, "UTF-8"));
26       s= new String(bytes, 0, len,"utf-8");
27     }
28     System.out.println("server : I get your message   : " + s);
29 
30     OutputStream outputStream = socket.getOutputStream();
31     outputStream.write("this is server  ".getBytes("UTF-8"));
32 
33     inputStream.close();
34     outputStream.close();
35     socket.close();
36     server.close();
37   }
38 }

 


 创建TCP客户端

1.创建一个Socket对象。

2.从Socket中获取I/O流。

3.对I/O流进行读写操作,完成与服务端的交互。

4.关闭I/O流和Socket

 1 import java.io.InputStream;
 2 import java.io.OutputStream;
 3 import java.net.Socket;
 4 
 5 public class ClientTest {
 6   public static void main(String args[]) throws Exception {
 7     // 要连接的服务端IP地址和端口
 8     String host = "127.0.0.1";
 9     int port = 55533;
10     // 与服务端建立连接
11     Socket socket = new Socket(host, port);
12     // 建立连接后获得输出流
13     OutputStream outputStream = socket.getOutputStream();
14     String message = "client:   hello this is client";
15     socket.getOutputStream().write(message.getBytes("UTF-8"));
16     //通过shutdownOutput告诉服务器已经发送完数据,后续只能接受数据
17     socket.shutdownOutput();
18     
19     InputStream inputStream = socket.getInputStream();
20     byte[] bytes = new byte[1024];
21     int len;
22     StringBuilder sb = new StringBuilder();
23     while ((len = inputStream.read(bytes)) != -1) {
24       //注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
25       sb.append(new String(bytes, 0, len,"UTF-8"));
26     }
27     System.out.println("client : I get your message   " + sb);
28     
29     inputStream.close();
30     outputStream.close();
31     socket.close();
32   }
33 }

  在传输过程中,客户端需要给服务端发送消息告知自己发送完成,否则服务端会一直等待,直到超时。

1、 此时需要调用方法告诉服务端,自己发送完成。

socket.shutdownOutput(); 而不是 outputStream.close();

     如果关闭输出流那模相应的Socket也关闭,相当于  socket.close();

   调用shutdownOutput() ,底层会告知服务端我这边已经写完,服务端知道消息已经读取完,如果服务端有要发送的消息,会发送,如果没有直接关闭socket。

    这样会使得,不能再次发送消息,如果发送需要再次建立连接。在访问频率较高时,将急需优化。

2、通过约定符号

  双方约定一个短语或字符来当做发送完成的标识,比如约定  end 

1 Socket socket = server.accept();
2 // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
3 BufferedReader read=new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
4 String line;
5 StringBuilder sb = new StringBuilder();
6 while ((line = read.readLine()) != null && "end".equals(line)) {
7   //注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
8   sb.append(line);
9 }

  优点:不需要关闭流,当发送完一条消息可以再次发送

  缺点:额外的结束标志占带宽,容易误判误被结束,

3、指定长度

  现指定命令长度,然后读取指定长度的内容

  现在首要的问题就是用几个字节指定长度呢,我们可以算一算:

  • 1个字节:最大256,表示256B
  • 2个字节:最大65536,表示64K
  • 3个字节:最大16777216,表示16M
  • 4个字节:最大4294967296,表示4G

   当然我们没必要使用最大长度,而使用边长方式来表示长度:

  • 第一个字节首位为0:即0XXXXXXX,表示长度就一个字节,最大128,表示128B
  • 第一个字节首位为110,那么附带后面一个字节表示长度:即110XXXXX 10XXXXXX,最大2048,表示2K
  • 第一个字节首位为1110,那么附带后面二个字节表示长度:即110XXXXX 10XXXXXX 10XXXXXX,最大131072,表示128K
  • 依次类推

 

  • 上面提到的这种用法适合高富帅的程序员使用,一般呢,如果用作命名发送,两个字节就够了,如果还不放心4个字节基本就能满足你的所有要求

服务端程序:

 1 import java.io.InputStream;
 2 import java.net.ServerSocket;
 3 import java.net.Socket;
 4 
 5 public class SocketServer {
 6   public static void main(String[] args) throws Exception {
 7     // 监听指定的端口
 8     int port = 55533;
 9     ServerSocket server = new ServerSocket(port);
10 
11     // server将一直等待连接的到来
12     System.out.println("server将一直等待连接的到来");
13     Socket socket = server.accept();
14     // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
15     InputStream inputStream = socket.getInputStream();
16     byte[] bytes;
17     // 因为可以复用Socket且能判断长度,所以可以一个Socket用到底
18     while (true) {
19       // 首先读取两个字节表示的长度
20       int first = inputStream.read();
21       //如果读取的值为-1 说明到了流的末尾,Socket已经被关闭了,此时将不能再去读取
22       if(first==-1){
23         break;
24       }
25       int second = inputStream.read();
26       int length = (first << 8) + second;
27       // 然后构造一个指定长的byte数组
28       bytes = new byte[length];
29       // 然后读取指定长度的消息即可
30       inputStream.read(bytes);
31       System.out.println("get message from client: " + new String(bytes, "UTF-8"));
32     }
33     inputStream.close();
34     socket.close();
35     server.close();
36   }
37 }

先读取两个字节的长度,然后读取消息,客户端程序:

 1 import java.io.OutputStream;
 2 import java.net.Socket;
 3 
 4 public class SocketClient {
 5   public static void main(String args[]) throws Exception {
 6     // 要连接的服务端IP地址和端口
 7     String host = "127.0.0.1";
 8     int port = 55533;
 9     // 与服务端建立连接
10     Socket socket = new Socket(host, port);
11     // 建立连接后获得输出流
12     OutputStream outputStream = socket.getOutputStream();
13     String message = "the first message!";
14     //首先需要计算得知消息的长度
15     byte[] sendBytes = message.getBytes("UTF-8");
16     //然后将消息的长度优先发送出去
17     outputStream.write(sendBytes.length >>8);
18     outputStream.write(sendBytes.length);
19     //然后将消息再次发送出去
20     outputStream.write(sendBytes);
21     outputStream.flush();
22     //==========此处重复发送一次,实际项目中为多个命名,此处只为展示用法
23     message = "the second message!";
24     sendBytes = message.getBytes("UTF-8");
25     outputStream.write(sendBytes.length >>8);
26     outputStream.write(sendBytes.length);
27     outputStream.write(sendBytes);
28     outputStream.flush();
29     //==========此处重复发送一次,实际项目中为多个命名,此处只为展示用法
30     message = "the third message!";
31     sendBytes = message.getBytes("UTF-8");
32     outputStream.write(sendBytes.length >>8);
33     outputStream.write(sendBytes.length);
34     outputStream.write(sendBytes);    
35     
36     outputStream.close();
37     socket.close();
38   }
39 }

 

运用线程池处理并发:

    服务端:

 1 import java.io.InputStream;
 2 import java.net.ServerSocket;
 3 import java.net.Socket;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 
 7 public class SocketServer {
 8   public static void main(String args[]) throws Exception {
 9     // 监听指定的端口
10     int port = 55533;
11     //建立服务端
12     ServerSocket server = new ServerSocket(port);
13     // server将一直等待连接的到来
14     System.out.println("server将一直等待连接的到来");
15 
16     //如果使用多线程,那就需要线程池,防止并发过高时创建过多线程耗尽资源
17     ExecutorService threadPool = Executors.newFixedThreadPool(100);
18     
19     while (true) {
20         //建立会话
21       Socket socket = server.accept();
22       
23 //      Runnable runnable=new Runnable() {    
24 //        @Override
25 //        public void run() {
26 //        }
27 //    };  
28     // java8   lambda  用于简化匿名类
29       Runnable runnable=()->{
30         try {
31           // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
32           InputStream inputStream = socket.getInputStream();
33           byte[] bytes = new byte[1024];
34           int len;
35           StringBuilder sb = new StringBuilder();
36           while ((len = inputStream.read(bytes)) != -1) {
37             // 注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
38             sb.append(new String(bytes, 0, len, "UTF-8"));
39           }
40           System.out.println("get message from client: " + sb);
41           inputStream.close();
42           socket.close();
43         } catch (Exception e) {
44           e.printStackTrace();
45         }
46       };
47       threadPool.submit(runnable);
48     }
49 
50   }
51 }

 

posted @ 2017-07-03 09:50  千彧  阅读(266)  评论(0编辑  收藏  举报