socket之事例

1.使用ServerSocket和Socket实现服务器端和客户端的 Socket通信:

2.面试题目总结;

1.面试题目1总结:

面试题目一:编写一个网络应用程序,有客户端与服务器端,客户端向服务器发送一个字符串,服务器收到该字符串后将其打印到命令行上,然后向客户端返回该字符串的长度,最后,客户端输出服务器端返回的该字符串的长度

//客户端代码
public class Client {
public static void main(String[] args) {

Socket socket = new Socket("127.0.0.1", 65000);
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
os.write(new String("hello world").getBytes());
int ch = 0;
byte[] buff = new byte[1024];
ch = is.read(buff);
String content = new String(buff, 0, ch);
System.out.println(content);
is.close();
os.close();
socket.close();}

}

// 服务端代码
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(65000);
while (true) {
  Socket socket = serverSocket.accept();
  new ServerThread(socket).start();}
}

class ServerThread extends Thread {
private Socket socket;
public ServerThread(Socket socket) {
  this.socket = socket;
}

public void run() {
try {

InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
int ch = 0;
byte[] buff = new byte[1024];
ch = is.read(buff);
String content = new String(buff, 0, ch);
System.out.println(content);
os.write(String.valueOf(content.length()).getBytes());
is.close();
os.close();
socket.close();

}}}
}

面试题二:用UDP通信方式完成上述问题?

public class UDPClient { // 客户端代码
public static void main(String[] args) throws Exception {

DatagramSocket socket = new DatagramSocket(); // 客户端发数据报给服务端
byte[] buf = "Hello World".getBytes();    // 要发送给服务端的数据
InetAddress address = InetAddress.getByName("127.0.0.1"); // 将IP地址封装成InetAddress对象
// 将要发送给服务端数据封装成DatagramPacket对象 需要填写上ip地址与端口号
DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 65001);
socket.send(packet); // 发送数据给服务端

byte[] data = new byte[100]; // 客户端接受服务端发送过来的数据报
DatagramPacket receivedPacket = new DatagramPacket(data, data.length); // 创建DtagramPacket对象用来存储服务端发送过来的数据
socket.receive(receivedPacket); // 将接受到的数据存储到DatagramPacket对象中
String content = new String(receivedPacket.getData(), 0, receivedPacket.getLength());
System.out.println(content);}

}

public class UDPServer { // 服务端代码
public static void main(String[] args) {

// 服务端接受客户端发送的数据报
DatagramSocket socket = new DatagramSocket(65001);
byte[] buff = new byte[100];   //存储从客户端接受到的内容
DatagramPacket pocket = new DatagramPacket(buff, buff.length);
socket.receive(pocket);    //接受客户端发送过来的内容,并将内容封装进DatagramPacket中
byte[] data = pocket.getData();   //从DatagramPacket对象中获取到真正存储的数据
String content = new String(data, 0, pocket.getLength());   //将数据从二进制转换成字符串形式
System.out.println(content);

byte[] sendedContent = String.valueOf(content.length()).getBytes();  // 将要发送给客户端的数据转换成二进制
// 服务端给客户端发送数据报,从DatagramPacket对象中获取到数据的来源地址与端口号
DatagramPacket packetToClient = new DatagramPacket(sendedContent, sendedContent.length, pocket.getAddress(), pocket.getPort());
socket.send(packetToClient);  //发送数据给客户端 } 

}

案例14. 使用ServerSocket和Socket实现服务器端和客户端的 Socket通信:

总结:
1) 建立Socket连接
  2) 获得输入/输出流  
3)读/写数据  
4) 关闭输入/输出流
  5) 关闭Socket
1. 编写网络应用程序,有客户端与服务器端,客户端向服务器端发送一个字符串,服务器收到该字符串后将其打印到命令行上,
 然后向客户端返回该字符串的长度,最后,客户端输出服务器端返回的该字符串的长度。

public class ClientTest{
public static void main(String[] args) {

Socket socket = new Socket("localhost",9999);
OutputStream os = socket.getOutputStream();
String content = "This comes from client";
os.write(content.getBytes());
byte[] b = new byte[100];

InputStream is = socket.getInputStream();
int length = is.read(b);
String str = new String(b,0,length);
System.out.println("string's length:" + str);
is.close();
os.close();
socket.close(); }

}

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

ServerSocket ss = new ServerSocket(9999);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
byte[] buffer = new byte[100];
int length = is.read(buffer);
String content = new String(buffer,0,length);
System.out.println("read from client:" + content);
String str = String.valueOf(content.length());

OutputStream os = socket.getOutputStream();
os.write(str.getBytes());
is.close();
os.close();
socket.close();}

}

案例13:用socket编写一个服务端与客户端的聊天程序

//客户端代码
public class Client {
public static void main(String[] args) throws Exception {
  Socket socket = new Socket("127.0.0.1", 65000);
  new MyThread(socket).start(); //开启一个线程用于处理已连接上的socket
}

//服务器端代码: 监听端口,每成功连接一个客户机就开启一个线程进行读写处理
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(65000);
while (true) {
   Socket socket = ss.accept();
   new MyThread(socket).start(); //开启一个线程用于处理已连接上的socket}}
}
//开启两个线程分别用来处理socket中的输入输出流
class MyThread extends Thread {
private Socket socket;
public MyThread(Socket socket) {
    this.socket = socket;
}
public void run() {
try {
//开启两个线程分别来处理服务端的读写
   new OutputThread(socket.getOutputStream()).start();
   new InputThread(socket.getInputStream()).start();
} catch (IOException e) {}}
}

//处理socket的输出流
class OutputThread extends Thread {
private OutputStream os;
public OutputThread(OutputStream os) {
   this.os = os;
}
public void run() {
  BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
  String line = null;
try {
 while ((line = br.readLine()) != null) {
   os.write((line).getBytes());
   os.flush();
}
} }
}
//处理socket的输入流
class InputThread extends Thread {
  private InputStream is;
  public InputThread(InputStream is) {
  this.is = is;
}
public void run() {
int ch = 0;
byte[] buff = new byte[1024];
try {
while ((ch = is.read(buff)) != -1) {
  String content = new String(buff, 0, ch);
  System.out.println(content);}
} }}

案例12. Java socket聊天室例子:
server 服务器端,处理数据连接,并转发消息
* 聊天室服务端 public class Server { * 运行在服务端的ServerSocket主要负责: * 1. 向系统申请服务端口客户端就是通过这个端口与之建立连接的 * 2. 监听申请的服务端口,当一个客户端通过该端口尝试建立连接时,ServerSocket会在 * 服务端创建一个Socket与客户端建立连接。 private ServerSocket server; * 保存所有客户端输出流的集合 private Map<String,PrintWriter> allOut; * 用来初始化服务端 public Server() throws IOException{ * 初始化的同时申请服务端口 server = new ServerSocket(8088); allOut = new HashMap<String,PrintWriter>(); } * 入口方法 public static void main(String[] args) { try { Server server = new Server(); server.start(); }catch(Exception e) { e.printStackTrace(); System.out.println("服务端启动失败"); } } * 将给定的输出流添加到共享集合 private synchronized void addOut(String nickName,PrintWriter out) { allOut.put(nickName, out); } * 将给定的输出流从共享集合中删除 private synchronized void removeOut(String nickName) { allOut.remove(nickName); } * 将给定的消息发送给所有客户端 private void sendMessage(String message) { Set<Map.Entry<String, PrintWriter>> entryset = allOut.entrySet(); for(Map.Entry<String, PrintWriter> e: entryset) { e.getValue().println(message); } } * 将给定消息发送给特定上线用户 private void sendOneMessage(String whoName,String message) { String keyName = message.substring(1,message.indexOf(":")); PrintWriter out = allOut.get(keyName); System.out.println("out is "+out+keyName); if(out != null) { out.println(whoName+"对你说:"+message.substring(message.indexOf(":"))); }else { System.out.println("发送失败"); } } * 服务端开始工作的方法 public void start() { try { * ServerSocket的accept方法,是一个堵塞方法,作用是监听服务端口, * 直到一个客户端连接并创建一个Socket,使用该Socket即可与刚连接的客户端进行交互 while(true) { System.out.println("等待客户端连接····"); Socket socket = server.accept(); System.out.println("一个客户端连接了"); ClientHandler handler = new ClientHandler(socket); Thread t = new Thread(handler); t.start(); } } } * 该线程处理客户端的Socket class ClientHandler implements Runnable{ private Socket socket; private String nickName; private String host; * 客户端的地址信息 public ClientHandler(Socket socket) { this.socket = socket; * 通过Socket可以获取远端计算机的地址信息。 InetAddress address = socket.getInetAddress(); * 获取ip地址 host = address.getHostAddress(); } public void run() { * Socket提供的方法 * InputStream getInputStream() * 该方法可以获取一个输入流,从该流读取的数据就是从远端计算机发送过来的 PrintWriter pw = null; try { InputStream input = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(input,"UTF-8"); BufferedReader br = new BufferedReader(isr); OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8"); pw = new PrintWriter(osw,true); * br.readLine()在读取客户端发送过来的消息时,由于客户端断线,而其操作系统的不同, * 这里读取的结果不同: * 当windows的客户端断开时,br.readLine会抛出异常 * 当linux的客户端断开时,br.readLine会返回null nickName = br.readLine(); System.out.println(host+" "+nickName+" 上线了"); * 将该客户端的输出流存入到共享集合中 addOut(nickName,pw); System.out.println(allOut); System.out.println("等待接收消息"); while(true) { String message = br.readLine(); if(message == null) { break; } System.out.println(host+" "+nickName+": "+message); if(message.startsWith("@")) { sendOneMessage(nickName,message); }else { sendMessage(host+nickName+": "+message); } } } catch (IOException e) { e.printStackTrace(); } finally { try { socket.close(); removeOut(nickName); System.out.println(nickName+"断开连接"); } catch (IOException e) { e.printStackTrace(); } } } } }

Client 客户端,监听发送事件,启动一个线程专门处理接收数据

 * 聊天室客户端
public class Client {
     * 套接字,java.net.Socket
     * 封装了TCP协议,使用它就可以基于TCP协议进行网络通讯。
     * Socket是运行在客户端的
    private Socket socket;
     * 构造方法,用来初始化客户端
    public Client() throws Exception {
         * 实例化Socket的时候需要传入两个参数:
         * 1:服务器地址,通过IP地址可以找到服务器的计算机
         * 2:服务器端口,通过端口可以找到服务器计算机上服务端应用程序
         * 实例化Socket的过程就是连接的过程,若远程计算机没有响应会抛出异常。
        System.out.println("正在连接服务端····");
        socket = new Socket("localhost",8088);
        System.out.println("已与服务端建立连接");
    }

     * 入口方法
    public static void main(String[] args) {
        try {
            Client client = new Client();
            client.start();
        }catch (Exception e) {
            e.printStackTrace();
            System.out.println("客户端启动失败!");
        }
    }

     * 启动客户端的方法
    public void start() {
        try {
             * Socket提供的方法:
             * OutputStream getOutputStream
             * 获取一个字节输出流,通过该流写出的数据会被发送至远端计算机。
            OutputStream out = socket.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");
            PrintWriter pw = new PrintWriter(osw,true);
             * 启动读取服务端发送过来消息的线程
            ServerHandler handler = new ServerHandler();
            Thread t = new Thread(handler);
            t.start();
             * 将字符串发送至服务端,,,--漏写发送nick昵称了,
            Scanner scan = new Scanner(System.in);
            while(true) {
                System.out.print("请输入昵称:");
                String nick = scan.nextLine();
                if(nick.length()==0) {
                    System.out.println("输入有误,请重新输入!");
                }else {
                    break;
                }
            }
            while(true) {
                System.out.print("说点什么吧:");
                String str = scan.nextLine();
                if("".equals(str)) {
                    break;
                }
                pw.println(str);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                socket.close();
                System.out.println("连接结束");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

     * 该线程用来读取服务端发送过来的消息,并输出到客户端控制台显示。
    class ServerHandler implements Runnable{
        public void run() {
            try {
                InputStream input = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(input,"UTF-8");
                BufferedReader br = new BufferedReader(isr);
                while(true) {
                    String str = br.readLine();
                    System.out.println("服务端回复:"+str);
                }
            }
        }
    }
}

 整个程序很简单,要十分注意一点,就是缓冲流要设置自动刷新写入,要不然客户端就收不到消息了
代码链接:链接:https://pan.baidu.com/s/1LyJHx6ZQiUTqtdQJpJUZUg 密码:ahux 

实例11:多人聊天,用Swing和JFrame做的聊天窗口,例子

与上面的实现是完全不同的方式写的,但基本是一样的。可以实现多人聊天,一对一聊天没写。

发送数据的方式都是经过包装的,发送文字时,首先发送一个字节的数据类型,再发送文字长度,再发送文字内容;

发送文件时,开始的部分是跟发送文字一样的,在发送文字内容时改为四个字节的文件名称长度,再写入文件名称,后面加上文件内容,取出数据时依据文件类型来特殊取出。

// 服务器server端,转发数据给所有上线的客户端
public class Server { * Socket列表,用于存放所有上线的用户Socket private static List<Socket> sockets; * 循环监听8888端口,一有连接就新建线程去处理,再继续监听端口 public static void main(String[] args) { ServerSocket serverSocket; sockets = new ArrayList<Socket>(); try { serverSocket = new ServerSocket(8888); System.out.println("服务器已启动,等待客户端连接"); while(true) { Socket socket = serverSocket.accept(); sockets.add(socket); offlineNotice(socket, "上线了"); new MyThread(socket, sockets).start(); } } catch (IOException e) { e.printStackTrace(); } } * 对所有上线的客户端消息通知 public static void offlineNotice(Socket socket, String message) { String descClient = "[" + socket.getRemoteSocketAddress() + "]"; byte[] userByte = getUsersByte(); for(Socket s : sockets) { Utils.write(descClient + message, s, Utils.STRINGTYPE); Utils.write(userByte, s, Utils.USERSTYPE); } } * 将Sockets取出其中的IP地址和端口,转成字符串添加到List中,再序列化成byte[]返回 public static byte[] getUsersByte() { byte[] userByte = null; try { List<String> users = new ArrayList<String>(); for(Socket s : sockets) { users.add("[" + s.getRemoteSocketAddress() + "]"); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(users); oos.close(); baos.close(); userByte = baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return userByte; } } * Server对每个客户端连接都启动一个线程负责处理,转发数据 class MyThread extends Thread{ private Socket socket; private List<Socket> sockets; public MyThread(Socket socket, List<Socket> sockets) { this.socket = socket; this.sockets = sockets; } * 循环处理转发数据 @Override public void run() { String descClient = "[" + socket.getRemoteSocketAddress() + "]"; try { System.out.println(descClient + "上线了"); while(true) { System.out.println("上线主机列表:" + sockets); Map<Byte, byte[]> map = Utils.read(socket); Byte type = Utils.getMapKey(map); byte[] data = Utils.getMapValue(map); System.out.println(socket.getInetAddress() + ": 数据类型--" + type + ", 数据--" + new String(data)); * 群发消息,排除发出消息的客户端的群发 for(Socket s : sockets) { if(s == socket) { continue; } if(type == Utils.STRINGTYPE) { Utils.write(descClient + ":" + new String(data), s, type); }else if(type == Utils.FILETYPE){ Utils.write(descClient + "发送文件", s, Utils.STRINGTYPE); Utils.write(data, s, type); } } if("exit".getBytes().equals(data) || data == null) { break; } } * 客户端正常下线通知 sockets.remove(socket); socket.close(); Server.offlineNotice(socket, "下线了"); System.out.println(descClient + ":下线了"); } catch (Exception e) { * 客户端异常正常下线通知 e.printStackTrace(); sockets.remove(socket); Server.offlineNotice(socket, "异常下线了"); System.out.println(descClient + "异常下线了"); } } }

ChatUI界面,整个界面布局,对按钮的事件处理,调用客户端处理连接和显示数据

public class ChatUI extends JFrame implements ActionListener{
    private static final long serialVersionUID = 7517634945840465934L;
    public JTextArea chatArea;
    private JButton sendNewsButton;
    private JButton sendFileButton;
    public JTextArea friendsArea;
    private JTextField newsField;
    private Client client = null;
    public static void main(String[] args) {
        ChatUI chatui = new ChatUI();
    }
    public ChatUI() {
        init();
    }
    
     * 初始化聊天窗口
    public void init() {
        this.setTitle("聊天窗口");
        this.setBounds(270, 80, 800, 600);
        this.setLayout(null);
        
        Font font = new Font("宋体", Font.BOLD, 22);   
         * 聊天对话界面
        chatArea = new JTextArea();
        JScrollPane chatPane = new JScrollPane(chatArea);
        chatPane.setBounds(30, 20, 450, 400);
        chatArea.setBounds(30, 20, 430, 380);
        chatArea.setFont(font);
        this.add(chatPane);

         * 填写发送消息的输入框
        newsField = new JTextField();
        newsField.setBounds(30, 460, 380, 40);
        newsField.setFont(font);
        this.add(newsField);
        
         * 发送按钮
        sendNewsButton = new JButton("发送");
        sendNewsButton.setBounds(450, 460, 60, 40);
        sendNewsButton.addActionListener(this);
        this.add(sendNewsButton);
        
         * 发送文件按钮
        sendFileButton = new JButton("发送文件");
        sendFileButton.setBounds(540, 460, 100, 40);
        sendFileButton.addActionListener(this);
        this.add(sendFileButton);
        
         * 所有上线客户端列表
        friendsArea = new JTextArea();
        friendsArea.setFont(new Font("宋体", Font.BOLD, 18));
        JScrollPane friendsPane = new JScrollPane(friendsArea);
        friendsPane.setBounds(550, 20, 200, 400);
        this.add(friendsPane);
        
         * 添加窗口事件处理程序,使用适配器,监听JFame退出事件
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.out.println("关闭窗口");
                System.exit(-1);
            }
        });
        this.setVisible(true);
         * 实例化一个负责通信的 Client
        client = new Client(this);
    }
    
     * 对两个按钮事件进行处理
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == sendNewsButton) {  * 发送按钮
            String msg = newsField.getText().trim();
            if(msg.length() != 0) {
                boolean status = Utils.write(msg, client.getSocket(), Utils.STRINGTYPE);
                if(!status) {
                    msg = "连接出错";
                    client.close();
                }
                 * 设置聊天窗口的数据
                String old;
                if("\r\n".equals(chatArea.getText())) {
                     old = "";
                }else {
                    old = chatArea.getText();
                }
                chatArea.setText(old + "我:" + msg + "\r\n");
                newsField.setText("");
                
            }
        }else if(e.getSource() == sendFileButton) { * 发送文件按钮
             * 对于发送文件的处理,需要重构发送的数据格式,定义文件头
            try {
                FileDialog dialog = new FileDialog(this, "选择文件", FileDialog.LOAD);
                dialog.setVisible(true);
                String path = dialog.getDirectory() + dialog.getFile();
                System.out.println(path);
                Utils.write(path, new FileInputStream(path), client.getSocket());
                String old;
                if("\r\n".equals(chatArea.getText())) {
                     old = "";
                }else {
                    old = chatArea.getText();
                }
                chatArea.setText(old + "我发送文件:" + path + "\r\n");
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            }
        }
    }
}

Client,ChatUI界面调用Client中的方法来与server交互

 * 负责ChatUI后台与服务器Server进行通信
public class Client {
    private Socket socket = null;
    
    public Client(ChatUI chatui) {
        getSocket();
        System.out.println("等待读取数据");
         * 启动一个守护线程,用于循环读取数据
        Thread t = new Thread() {
            public void run() {
            try {
                while(true) {
                    Map<Byte, byte[]> map = Utils.read(socket, chatui);
                    Byte type = Utils.getMapKey(map);
                    byte[] buffer = Utils.getMapValue(map);
                     * 检测获得的数据是否是字符串类型
                    if(type == Utils.STRINGTYPE) {
                         * 检测是否满足退出条件
                        if("exit".equals(new String(buffer)) || buffer == null) {
                            System.out.println("连接关闭");
                            socket.close();
                            chatui.chatArea.setText(chatui.chatArea.getText() + "连接关闭" + "\r\n");
                            break;
                        }
                         * 将字符串直接写入到聊天窗口中
                        System.out.println("STRINGTYPE = " + new String(buffer));
                        chatui.chatArea.setText(chatui.chatArea.getText() + new String(buffer) + "\r\n");
                    }else if(type == Utils.USERSTYPE){  * 检测是否是用户列表类型
                         * 用户列表数据是经过序列化成byte的数据,所以要先转换为对象
                        ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
                        ObjectInputStream ois = new ObjectInputStream(bais);
                        List<String> usersList =  (List<String>) ois.readObject();
                        String d = "";
                        for(String str : usersList) {
                            d = d + str + "\r\n";
                        }
                         * 读取所有的用户数据,构造好字符串,填入好友列表框中
                        chatui.friendsArea.setText(d);
                        ois.close();
                        bais.close();
                        
                    }else if(type == Utils.FILETYPE) {  * 检查是否是文件类型
                         * 是文件类型,需要反重构,将文件名称和文件数据分别取出来
                         * 前四个字节是文件名称的长度,接下来是文件名称,最后就是文件数据了
                        System.out.println("接收到文件了:" + new String(buffer));
                         * 文件名称长度
                        byte[] lenByte = new byte[4];
                        System.arraycopy(buffer, 0, lenByte, 0, 4);
                         * 将byte[]转成int类型
                        int lenName = Utils.byte2Int(lenByte);
                        System.out.println("lenName = " + lenName);
                        
                         * 构造一个特定长度的byte[],取出文件名称
                        byte[] nameByte = new byte[lenName];
                        System.arraycopy(buffer, 4, nameByte, 0, lenName);
                         * 构造文件数据长度的byte[],取出文件数据
                        byte[] bufferData = new byte[buffer.length-4-lenName];
                        System.arraycopy(buffer, 4+lenName, bufferData, 0, bufferData.length);
                         * 将文件数据写入文件中
                        FileOutputStream fos = new FileOutputStream("E://TestCase//day20//" + new String(nameByte));
                        fos.write(bufferData);
                        fos.close();
                         * 在聊天窗口中显示文件保存位置
                        chatui.chatArea.setText(chatui.chatArea.getText() + "文件保存在E://TestCase//day20//" + new String(nameByte) + "\r\n");
                        System.out.println("文件保存位置 E://TestCase//day20//" + new String(nameByte));
                        }
                    }
                } catch(Exception e) {
                    e.printStackTrace();
                    try {
                        socket.close();
                        chatui.chatArea.setText(chatui.chatArea.getText() + "异常,连接已关闭" + "\r\n");
                    }
                }
            }
        };
         // 设置为守护线程,并启动
        t.setDaemon(true);
        t.start();
    }
    
     * 检测socket是否为null,是则创建,可以避免创建多个socket
    public Socket getSocket() {
        if(socket == null) {
            try {
                socket = new Socket("localhost", 8888);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("服务器没有开启");
                System.exit(-1);
            }
        }
        return socket;
    }public void close() {
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Utils,这个是一个工具类,客户端和服务端都是用其内部的方法处理数据

public class Utils {
     * 三种数据类型,用于区分发送的数据
     * 字符串标识
    public final static byte STRINGTYPE = 0;
     * 所有上线用户信息标识
    public final static byte USERSTYPE = 1;
     * 发送文件标识
    public final static byte FILETYPE = 2;
     * int转成四字节的byte[],便于写入到socket流中,也便于解码
    public static byte[] int2Byte(int number) {
        byte[] b = new byte[4];
        b[0] = (byte) (number >> 24);
        b[1] = (byte) (number >> 16);
        b[2] = (byte) (number >> 8);
        b[3] = (byte)number;
        return b;
    }
    
     * byte转int,和上面的int2byte对应
    public static int byte2Int(byte[] b) {
        int i3 = (b[0] & 0xFF)<< 24;
        int i2 = (b[1] & 0xFF)<< 16;
        int i1 = (b[2] & 0xFF)<< 8;
        int i0 =  b[3] & 0xFF;
        return i3 | i2 | i1 | i0;
    }
    
     * 重写read方法
    public static Map<Byte, byte[]> read(Socket socket) throws Exception {
        Map<Byte, byte[]> map = read(socket, null);
        return map;
    }
    
     * 获取字典数据(只有一个Entry)中的key,也就是Byte
    public static Byte getMapKey(Map<Byte, byte[]> map) {
        Set<Entry<Byte, byte[]>> entrySet = map.entrySet();
        Iterator<Entry<Byte, byte[]>> it = entrySet.iterator();
        Entry<Byte, byte[]> entry = it.next();
        return entry.getKey();
    }
    
     * 获取字典数据(只有一个Entry)中的value,也就是byte[]数组
    public static byte[] getMapValue(Map<Byte, byte[]> map) {
        Set<Entry<Byte, byte[]>> entrySet = map.entrySet();
        Iterator<Entry<Byte, byte[]>> it = entrySet.iterator();
        Entry<Byte, byte[]> entry = it.next();
        return entry.getValue();
    }
    
     * read方法对封装的数据进行解编码,取出封装的数据,但对数据类型不予区分,只返回类型和数据的map
     * 具体的区分数据类型在client中处理,对于文件类型需要特殊处理
    public static Map<Byte, byte[]> read(Socket socket, ChatUI chatui) throws Exception{
        Map<Byte, byte[]> map = new HashMap<Byte, byte[]>();
        InputStream in = socket.getInputStream();
        byte[] type = new byte[1];
        in.read(type);
        byte[] lengthByte = new byte[4];
        in.read(lengthByte);
        int length = Utils.byte2Int(lengthByte);

        byte[] buffer = new byte[length];
        in.read(buffer);
        
        map.put(type[0], buffer);
        return map;
    }
    
     * 重写write方法,使得可以写入文件,但注意不能写入过大的文件,对数据内容进一步重构
    public static void write(String path, FileInputStream fis, Socket socket) {
        try {
             * 把文件名称也写到数组中,首先是四个字节的文件名称长度,加上文件名称,再加内容
            String fileName = path.substring(path.lastIndexOf("\\")+1, path.length());
             * 文件名称
            byte[] nameByte = fileName.getBytes();
             * 文件名称长度
            byte[] lenByte = Utils.int2Byte(nameByte.length);
             * 文件数据
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
             * 新建足够长的数组用于存放合并的数据
            byte[] newByte = new byte[4 + nameByte.length + buffer.length];
             * 合并数组
            System.arraycopy(lenByte, 0, newByte, 0, lenByte.length);
            System.arraycopy(nameByte, 0, newByte, lenByte.length, nameByte.length);
            System.arraycopy(buffer, 0, newByte, lenByte.length + nameByte.length, buffer.length);
            
            write(newByte, socket, Utils.FILETYPE);
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } }
    }
    
     * 重写write方法,使得可以写入String
    public static boolean write(String data, Socket socket, byte type) {
        return write(data.getBytes(), socket, type);
    }
     * 将byte类型数据写入流中, 对数据进行编码
     * 首先是数据类型一个字节,第二个是数据内容长度四个字节,第三个是数据内容
    public static boolean write(byte[] data, Socket socket, byte type) {
        try {
            OutputStream out = socket.getOutputStream();
             * 获得数据类型,数据长度,转成byte
            int length = data.length;
            byte[] typeByte = {(byte)type};
            byte[] lengthByte = Utils.int2Byte(length);
             * 新建足够长的数组用于存放合成的数据
            byte[] end = new byte[1 + 4 + data.length];
             * 合并数组
            System.arraycopy(typeByte, 0, end, 0, typeByte.length);
            System.arraycopy(lengthByte, 0, end, 1, lengthByte.length);
            System.arraycopy(data, 0, end, 5, data.length);
            out.write(end);
            System.out.println("发送完毕:" + new String(data));
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            try {
                socket.close();
            } return false;
        }
    }
} 
posted on 2020-03-28 18:03  左手指月  阅读(309)  评论(0编辑  收藏  举报