zoukankan      html  css  js  c++  java
  • 由一个项目看java TCP/IP Socket编程

    前一段时间刚做了个java程序和网络上多台机器的c程序通讯的项目,遵循的是TCP/IP协议,用到了java的Socket编程。网络通讯是java的强项,用TCP/IP协议可以方便的和网络上的其他程序互通消息。 

    先来介绍下网络协议: 
        TCP/IP 
            Transmission Control Protocol 传输控制协议 
            Internet Protocol 互联网协议 
        UDP 
            User Datagram Protocol 用户数据协议 

    连接协议: 
        分为: 
        面向连接协议: Connection Oriented Protocol 
        非连接协议: Connectionless Protocol 

        1).面向连接协议是指两台电脑在传输数据前,先会建立一个专属的连接。就如电信局的交换机会为打电话双方提供专属连接一样。 
        Internet上的面向连接协议就是TCP/IP 
        特点:确认回应;分组序号;流量控制。 
        TCP/IP属于可靠性传输,适合不容许有传输错误的网络程序设计使用 

        2).非连接协议:无专属连接,无分组,容错,距离短,可同时对多台电脑进行数据传输 
        Internet上的非连接协议就是UDP 

        TCP在网络通信上有极强的生命力,例如远程连接(Telnet)和文件传输(FTP)都需要不定长度的数据被可靠地传输。相比之下UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。 


    Socket 是程序与网络间的一种接口,大部分网络应用程序都是点对点的,所谓点就是服务器端和客户端所执行的程序。Socket是用来接收和传送分组的一个端点。 

    java的Socket编程要用到java.net包,最常用的是net包下的6个类:InetAddress(互联网协议 (IP) 地址)类,Socket(套接字)类,ServerSocket(套接字服务器)类,DatagramSocket(发送和接收数据报包的套接字)类,DatagramPacket(数据报包)类,MulticastSocket(多播数据报套接字类用于发送和接收 IP 多播包)类,其中InetAddress、Socket、ServerSocket类是属于TCP面向连接协议,DatagramSocket、DatagramPacket和MulticastSocket类则属于UDP非连接协议的传送类。 

    本项目因为使用TCP/IP协议,主要用到Socket和ServerSocket类 

    项目代码如下 

    Java代码  收藏代码
    1. package com.sse.monitor.serv;  
    2.   
    3. import java.io.DataInputStream;  
    4. import java.io.IOException;  
    5. import java.io.InputStream;  
    6. import java.io.OutputStream;  
    7. import java.io.BufferedOutputStream;  
    8.   
    9. import java.net.Socket;  
    10. import java.net.UnknownHostException;  
    11. import java.util.ArrayList;  
    12.   
    13. import com.sse.monitor.bean.Message;  
    14. import com.sse.monitor.bean.MessageHead;  
    15. import com.sse.monitor.bean.ResponseMessage;  
    16. import com.sse.monitor.form.ListenerInvoke;  
    17. import com.sse.monitor.form.MainForm;  
    18. import com.sse.monitor.util.SwingUtils;  
    19.   
    20. /** 
    21.  * Socket套接字工厂,对外接口是静态方法 SocketFactory.request(String, String, String, int)  
    22.  * Copyright: Copyright (c) 2008  
    23.  * Company: conserv 
    24.  * @author cuishen 
    25.  * @version 1.3 
    26.  */  
    27. public class SocketFactory {  
    28.     private Socket socket = null;  
    29.     private String targetIpAddress = null;  
    30.     private int targetPort = 0;  
    31.     private static SocketFactory sf = new SocketFactory();  
    32.   
    33.     public SocketFactory() {  
    34.     }  
    35.   
    36.     /** 
    37.      * 建立一条TCP/IP连接 
    38.      * @param targetIpAddress String 目标ip地址 
    39.      * @param targetPort String 目标端口 
    40.      * @throws IOException 
    41.      */  
    42.     private void connect(String targetIpAddress, int targetPort) throws IOException {  
    43.         setTargetIpAddress(targetIpAddress);  
    44.         setTargetPort(targetPort);  
    45.         if(socket == null)  
    46.             socket = new Socket(targetIpAddress, targetPort);  
    47.     }  
    48.   
    49.     /** 
    50.      * 这是对外接口。发送命令,接收反馈和接收message放两个线程, 
    51.      * 发送命令并接收反馈是短连接,所以每次执行成功后,将销毁socket并终止线程, 
    52.      * 接收message是长连接,所以可能会new出n个线程,建议对接收message的线程做缓存 
    53.      * @param commandType String 命令类型 
    54.      * @param commandContent String 命令内容 
    55.      * @param targetIP String 目标ip 
    56.      * @param targetPort int 目标端口 
    57.      */  
    58.     public static void request(String commandType, String commandContent, String targetIP, int targetPort) {  
    59.         if (commandType.equalsIgnoreCase(MessageFactory.SCAN_COMMAND)) {  
    60.             sf.new GetMessageSocketThread(commandType, commandContent, targetIP, targetPort);  
    61.         } else {  
    62.             sf.new RequestSocketThread(commandType, commandContent, targetIP, targetPort);  
    63.         }  
    64.     }  
    65.   
    66.     /** 
    67.      * 发送请求 
    68.      * @param commandType String 命令类型 
    69.      * @param commandContent String 命令内容 
    70.      * @param targetIp String 目标ip 
    71.      */  
    72.     private void sendRequest(String commandType, String commandContent, String targetIp) {  
    73.         OutputStream os = null;  
    74.         BufferedOutputStream bs = null;  
    75.         try {  
    76.             os = socket.getOutputStream();  
    77.             bs = new BufferedOutputStream(os);  
    78.             char[] message = MessageFactory.makeRequestMessage(targetIp, commandType, commandContent, MessageFactory.COMMAND_TRADE_CODE, MessageFactory.RIGHT_COMMAND, MessageFactory.MESSAGE_END_FLAG);  
    79.             for (int i = 0; i < message.length; i++)  
    80.                 bs.write(new String(message).getBytes(), i, 1);  
    81.             bs.flush();  
    82.             SwingUtils.appendLog(MainForm.jTextArea, "发送请求:'" + commandType + "' '" + commandContent + "' '" + targetIp + "'", ReadConfig.commandStateShowLineCount);  
    83.         } catch (IOException e) {  
    84.             SwingUtils.appendLog(MainForm.jTextArea, "Error!!! 发送请求:'" + commandType + "' '" + commandContent + "' '" + targetIp + "'失败!! " + e.getMessage(), ReadConfig.commandStateShowLineCount);  
    85.             e.printStackTrace();  
    86.         } catch (Exception e) {  
    87.             e.printStackTrace();  
    88.         } finally {  
    89.         }  
    90.     }  
    91.   
    92.     /** 
    93.      * 获得反馈 
    94.      *  
    95.      * @return 如果成功获得反馈,则返回true;否则返回false 
    96.      */  
    97.     private boolean getResponse() {  
    98.         InputStream is = null;  
    99.         DataInputStream di = null;  
    100.         boolean returnFlag = false;  
    101.         try {  
    102.             is = socket.getInputStream();  
    103.             di = new DataInputStream(is);  
    104.             byte[] temp = new byte[1];  
    105.             int flag = 0;  
    106.             ArrayList tempByteList = new ArrayList();  
    107.             int i = 0;  
    108.             while (flag != -1) {  
    109.                 i++;  
    110.                 flag = di.read(temp = new byte[1]);  
    111.                 if (flag != -1)  
    112.                     tempByteList.add(temp);  
    113.                 if (i == 38)  
    114.                     break;  
    115.             }  
    116.             if (i == 1) {  
    117.                 SwingUtils.Error("未收到response!!!");  
    118.                 return false;  
    119.             }  
    120.             MessageHead messageHead = MessageFactory.readHead(tempByteList);  
    121.   
    122.             SwingUtils.appendLog(MainForm.jTextArea, "收到 response", ReadConfig.commandStateShowLineCount);  
    123.   
    124.             tempByteList = new ArrayList();  
    125.             i = 0;  
    126.             while (flag != -1) {  
    127.                 i++;  
    128.                 flag = di.read(temp = new byte[1]);  
    129.                 if (flag != -1)  
    130.                     tempByteList.add(temp);  
    131.                 if (i == 26)  
    132.                     break;  
    133.             }  
    134.             byte[] length = new byte[4];  
    135.             di.read(length);  
    136.             int len = Integer.parseInt(new String(length, MessageFactory.DEFAULT_CHAR_SET).trim());  
    137.             flag = 0;  
    138.             for (int j = 0; j < (len + 37); j++) {  
    139.                 flag = di.read(temp = new byte[1]);  
    140.                 if (flag == -1)  
    141.                     break;  
    142.                 tempByteList.add(temp);  
    143.             }  
    144.   
    145.             ResponseMessage rm = MessageFactory.readResponseMessage(tempByteList, len);  
    146.   
    147.             if (messageHead.getErrorCode().equals(MessageFactory.SUCCESS))  
    148.                 returnFlag = true;  
    149.             else  
    150.                 SwingUtils.Error("errorCode: " + messageHead.getErrorCode() + "; content: " + rm.getCommandContent());  
    151.         } catch (IOException e) {  
    152.             e.printStackTrace();  
    153.         } finally {  
    154.         }  
    155.         return returnFlag;  
    156.     }  
    157.   
    158.     /** 
    159.      * 分发消息的方法,将消息按进程名发送到对应的消息缓存  
    160.      * 
    161.      * 消息缓存ListenerInvoke.messageMap,key = machineName + 
    162.      * '|' + programName + '|' + processId, value = messageList 
    163.      * 存放messageMap里面的键名的List -- ListenerInvoke.messageMapKeyList 
    164.      * 进程状态缓存ListenerInvoke.processStateMap, key = machineName + '|' + 
    165.      * programName + '|' + processId, value = String 
    166.      *  
    167.      * @param message Message 
    168.      */  
    169.     private void distributeMess(Message message) {  
    170.         String machineName = message.getMachineName();  
    171.         String programName = message.getProgramName();  
    172.         String processId = message.getProcessId();  
    173.         String content = message.getContent();  
    174.         String key = machineName + '|' + programName + '|' + processId;  
    175.         key = key.trim();  
    176.         ArrayList messageList = null;  
    177.         if (ListenerInvoke.messageMap.get(key) == null) {  
    178.             synchronized (ListenerInvoke.messageMap) {  
    179.                 if (ListenerInvoke.messageMap.get(key) == null) {  
    180.                     messageList = new ArrayList();  
    181.                     messageList.add(content);             
    182.                     ListenerInvoke.messageMap.put(key, messageList);  
    183.                 }  
    184.             }  
    185.         } else {  
    186.             messageList = (ArrayList) ListenerInvoke.messageMap.get(key);  
    187.             synchronized (messageList) {  
    188.                 if (ListenerInvoke.messageMap.get(key) != null) {  
    189.                     messageList.add(content);  
    190.                     if (!ReadConfig.threadDeleteMessCacheOrFIFO  
    191.                             && messageList.size() >= ReadConfig.messageCacheSizeLimit)  
    192.                         messageList.remove(0);  
    193.                 }  
    194.             }  
    195.         }  
    196.         if (!ListenerInvoke.messageMapKeyList.contains(key)) {  
    197.             synchronized (ListenerInvoke.messageMapKeyList) {  
    198.                 if (!ListenerInvoke.messageMapKeyList.contains(key))  
    199.                     ListenerInvoke.messageMapKeyList.add(key);  
    200.             }  
    201.         }  
    202.     }  
    203.   
    204.     /** 
    205.      * 接收message 
    206.      * @return Message 
    207.      */  
    208.     private boolean getMessage() {  
    209.         InputStream is = null;  
    210.         DataInputStream di = null;  
    211.         Message message = null;  
    212.         try {  
    213.             if (this.socket == null)  return false;  
    214.             is = this.socket.getInputStream();  
    215.             if (is == null)  return false;  
    216.             di = new DataInputStream(is);  
    217.             byte[] temp = new byte[1];  
    218.             int flag = 0;  
    219.             ArrayList tempByteList = new ArrayList();  
    220.             int i = 0;  
    221.             while (flag != -1) {  
    222.                 i++;  
    223.                 flag = di.read(temp = new byte[1]);  
    224.                 if (flag != -1)  
    225.                     tempByteList.add(temp);  
    226.                 if (i == 38)  
    227.                     break;  
    228.             }  
    229.             if (i == 1)  return false;  
    230.   
    231.             tempByteList = new ArrayList();  
    232.             i = 0;  
    233.             while (flag != -1) {  
    234.                 i++;  
    235.                 flag = di.read(temp = new byte[1]);  
    236.                 if (flag != -1)  
    237.                     tempByteList.add(temp);  
    238.                 if (i == 74)  
    239.                     break;  
    240.             }  
    241.             byte[] length = new byte[4];  
    242.             di.read(length);  
    243.             int len = Integer.parseInt(new String(length,  
    244.                     MessageFactory.DEFAULT_CHAR_SET).trim());  
    245.             flag = 0;  
    246.             for (int j = 0; j < len; j++) {  
    247.                 flag = di.read(temp = new byte[1]);  
    248.                 if (flag == -1)  
    249.                     break;  
    250.                 tempByteList.add(temp);  
    251.             }  
    252.             message = MessageFactory.readMessage(tempByteList, len);  
    253.             SwingUtils.appendLog(MainForm.jTextArea, "收到新 Message",  
    254.                     ReadConfig.commandStateShowLineCount);  
    255.             distributeMess(message);// 分发message  
    256.         } catch (IOException e) {  
    257.             e.printStackTrace();  
    258.         } finally {  
    259.         }  
    260.         return true;  
    261.     }  
    262.   
    263.     /** 
    264.      * 负责发送请求接收反馈的内部线程类,每new一个RequestSocketThread线程, 
    265.      * 就new一个socket,建立一条专属连接,成功接收反馈后将销毁socket,终止线程。 
    266.      * 将发送请求,接收反馈放进内部线程处理,是为了防止套接字阻塞造成主线程挂死。 
    267.      * @author cuishen 
    268.      * @version 1.2 
    269.      */  
    270.     class RequestSocketThread implements Runnable {  
    271.         private SocketFactory socketFactory;  
    272.         private String commandType = null;  
    273.         private String commandContent = null;  
    274.         private String targetIP = null;  
    275.         Thread t;  
    276.   
    277.         public RequestSocketThread(String commandType, String commandContent, String targetIP, int targetPort) {  
    278.             this.socketFactory = new SocketFactory();  
    279.             try {  
    280.                 this.socketFactory.connect(ReadConfig.targetIpAddress, ReadConfig.targetPort);  
    281.             } catch (UnknownHostException e) {  
    282.                 SwingUtils.Error("主机 IP 地址无法确定,无法建立连接! targetIP=" + ReadConfig.targetIpAddress + ", targetPort=" + ReadConfig.targetPort);  
    283.                 e.printStackTrace();  
    284.             } catch (IOException e) {  
    285.                 SwingUtils.Error("访问被拒绝,无法建立连接,请检查网络! targetIP=" + ReadConfig.targetIpAddress + ", targetPort=" + ReadConfig.targetPort);  
    286.                 e.printStackTrace();  
    287.             }  
    288.             this.commandType = commandType;  
    289.             this.commandContent = commandContent;  
    290.             this.targetIP = targetIP;  
    291.             t = new Thread(this);  
    292.             t.start();  
    293.         }  
    294.   
    295.         public void run() {  
    296.             this.socketFactory.sendRequest(commandType, commandContent, targetIP);  
    297.             this.socketFactory.getResponse();  
    298.             stopThread();  
    299.         }  
    300.   
    301.         public void stopThread() {  
    302.             try {  
    303.                 this.commandType = null;  
    304.                 this.commandContent = null;  
    305.                 this.targetIP = null;  
    306.                 socketFactory.closeSocket();  
    307.                 socketFactory = null;  
    308.                 this.t.join(100);  
    309.             } catch (InterruptedException e) {  
    310.                 e.printStackTrace();  
    311.             } finally {  
    312.                 t = null;  
    313.             }  
    314.         }  
    315.     }  
    316.   
    317.     /** 
    318.      * 负责接收message的内部线程类,每new一个GetMessageSocketThread线程, 
    319.      * 就new一个socket,建立一条专属TCP/IP连接,getMessage是长连接,所以建议 
    320.      * 将该线程放入缓存方便管理 
    321.      * @author cuishen 
    322.      * @version 1.2 
    323.      */  
    324.     class GetMessageSocketThread implements Runnable {  
    325.         private SocketFactory socketFactory;  
    326.         private String commandType = null;  
    327.         private String commandContent = null;  
    328.         private String targetIP = null;  
    329.         Thread t;  
    330.         private boolean flag = false;  
    331.         private boolean ifGetResponse = true;  
    332.         private boolean ifGetMessage = true;  
    333.         private boolean ifSendRequest = true;  
    334.         private boolean ifCycle = true;  
    335.   
    336.         public GetMessageSocketThread(String commandType, String commandContent, String targetIP, int targetPort) {  
    337.               
    338.             this.socketFactory = new SocketFactory();  
    339.             try {  
    340.                 this.socketFactory.connect(ReadConfig.targetIpAddress, ReadConfig.targetPort);  
    341.             } catch (UnknownHostException e) {  
    342.                 SwingUtils.Error("主机 IP 地址无法确定,无法建立连接! targetIP="  
    343.                         + ReadConfig.targetIpAddress + ", targetPort="  
    344.                         + ReadConfig.targetPort);  
    345.                 e.printStackTrace();  
    346.             } catch (IOException e) {  
    347.                 SwingUtils.Error("访问被拒绝,无法建立连接,请检查网络! targetIP="  
    348.                         + ReadConfig.targetIpAddress + ", targetPort="  
    349.                         + ReadConfig.targetPort);  
    350.                 e.printStackTrace();  
    351.             }  
    352.             this.commandType = commandType;  
    353.             this.commandContent = commandContent;  
    354.             this.targetIP = targetIP;  
    355.             t = new Thread(this);  
    356.             t.start();  
    357.         }  
    358.   
    359.         public void run() {  
    360.             while (ifCycle) {  
    361.                 if (ifSendRequest) {  
    362.                     this.socketFactory.sendRequest(commandType, commandContent, targetIP);  
    363.                     ifSendRequest = false;  
    364.                 }  
    365.                 if (ifGetResponse) {  
    366.                     flag = socketFactory.getResponse();  
    367.                     ifGetResponse = false;  
    368.                 }  
    369.                 if (flag && ifGetMessage && socketFactory.socket != null) {  
    370.                     if (!socketFactory.getMessage()) {  
    371.                         try {  
    372.                             Thread.sleep(ReadConfig.getMessageThreadSleep);  
    373.                         } catch (InterruptedException e) {  
    374.                             e.printStackTrace();  
    375.                         }  
    376.                     }  
    377.                 }  
    378.             }  
    379.         }  
    380.   
    381.         public void stopThread() {  
    382.             try {  
    383.                 this.commandType = null;  
    384.                 this.commandContent = null;  
    385.                 this.targetIP = null;  
    386.                 ifGetMessage = false;  
    387.                 ifCycle = false;  
    388.                 socketFactory.closeSocket();  
    389.                 socketFactory = null;  
    390.                 this.t.join(100);  
    391.             } catch (InterruptedException e) {  
    392.                 e.printStackTrace();  
    393.             } finally {  
    394.                 t = null;  
    395.             }  
    396.         }  
    397.     }  
    398.   
    399.     /** 
    400.      * 关闭套接字 
    401.      */  
    402.     private void closeSocket() {  
    403.         try {  
    404.             if (!socket.isClosed())  
    405.                 socket.close();  
    406.             socket = null;  
    407.         } catch (IOException e) {  
    408.             e.printStackTrace();  
    409.         }  
    410.     }  
    411.   
    412.     /** 
    413.      * @return the targetIpAddress 
    414.      */  
    415.     public String getTargetIpAddress() {  
    416.         return targetIpAddress;  
    417.     }  
    418.   
    419.     /** 
    420.      * @param targetIpAddress 
    421.      *            the targetIpAddress to set 
    422.      */  
    423.     public void setTargetIpAddress(String targetIpAddress) {  
    424.         this.targetIpAddress = targetIpAddress;  
    425.     }  
    426.   
    427.     /** 
    428.      * @return the targetPort 
    429.      */  
    430.     public int getTargetPort() {  
    431.         return targetPort;  
    432.     }  
    433.   
    434.     /** 
    435.      * @param targetPort 
    436.      *            the targetPort to set 
    437.      */  
    438.     public void setTargetPort(int targetPort) {  
    439.         this.targetPort = targetPort;  
    440.     }  
    441.   
    442. }  



    以上是Socket编程,ServerSocket在项目里没有用到,但是我也写了个包装类供参考 

    Java代码  收藏代码
    1. package com.sse.monitor.serv;  
    2.   
    3. import java.io.IOException;  
    4. import java.net.ServerSocket;  
    5. import java.net.Socket;  
    6.   
    7. /** 
    8.  * 服务器套接字工厂 
    9.  * Copyright: Copyright (c) 2008 
    10.  * @author cuishen 
    11.  * @version 1.0 
    12.  */  
    13. public class ServerSocketFactory {  
    14.     private static ServerSocket server;  
    15.     private static Socket client;  
    16.     private boolean ifRunServer = true;  
    17.       
    18.     public void runServer(int port) throws IOException {  
    19.         //本地建立一个套接字服务器,等待其他机器访问  
    20.         server = new ServerSocket(port);  
    21.         System.out.println("Socket Server Start...");  
    22.         new ServerThread();  
    23.     }  
    24.       
    25.   
    26.     class ServerThread implements Runnable {  
    27.         Thread t;  
    28.           
    29.         public ServerThread() {  
    30.             t = new Thread(this);  
    31.             t.start();  
    32.         }  
    33.   
    34.         public void run() {  
    35.             try {  
    36.                 while(ifRunServer) {  
    37.                     if(client == null) client = server.accept();  
    38.                     if(client != null) //getMessage();  
    39.                     Thread.sleep(ReadConfig.serverThreadSleep);  
    40.                 }  
    41.             } catch (InterruptedException e) {  
    42.                 e.printStackTrace();  
    43.             }   
    44.             catch (IOException e) {  
    45.                 e.printStackTrace();  
    46.             }  
    47.         }  
    48.         public void stopThread() {  
    49.             try {  
    50.                 ifRunServer = false;  
    51.                 this.t.join(100);  
    52.             } catch (InterruptedException ex) {  
    53.                 System.out.println("socket服务器线程终止异常!!!");  
    54.             } finally {  
    55.                 t = null;  
    56.             }  
    57.         }  
    58.     }  
    59. }  



    Socket编程就是运用Socket或者ServerSocket类搭配线程来使用(由于TCP/IP属于可靠性传输,不会丢包)。可能会因为在发送请求或者接受消息时Socket阻塞而导致主线程挂死,因此发送请求、接收消息的方法要放进子线程里处理;对于同一目标ip和端口,在同一个子线程里只能new一个Socket,也就是说,要对同一地址建立多条连接,就要开启多个线程。而且注意连接可能会因作用不同分长连接和短连接,要分别处理,本项目中发送请求和接受message就分别属于短连接和长连接,因此分别开发了RequestSocketThread和GetMessageSocketThread两个子线程区分对待。可以同时开发个Message类来封装打包和解包消息的方法(项目中开发MessageFactory.java),方便调用

  • 相关阅读:
    Android 开发笔记___复选框__checkbox
    Android 开发笔记___FrameLayout
    Android 开发笔记___RelativeLayout
    Android 开发笔记___初级控件之实战__计算器
    Android 开发笔记___shape
    Android 开发笔记___DateUtil——Time
    改良版 导航栏自动跟随
    简洁 js排序算法
    简单使用rem方案适配移动设备
    导航栏监听页面滚动跟随 简单封装
  • 原文地址:https://www.cnblogs.com/smilesmile/p/3844612.html
Copyright © 2011-2022 走看看