zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    中移动ipv6-老毛子固件获取ipv6设置
    win7-win10 禁用IPV6临时地址
    辅助调用函数【call,apply,bind】
    Unraid修改docker镜像地址&默认启动
    docker基本入门知识-小白向
    [不止于代码]Unraid基本使用速记
    Dockerfile文件说明
    Git之pull,fetch差别
    代码片段添加智能提示,打造一款人见人爱的ORM框架
    新定义三层,指挥官模式
  • 原文地址:https://www.cnblogs.com/the-wang/p/7109691.html
Copyright © 2011-2022 走看看