Java socket 通信例子

进程之间的相互通信用的就是套接字

socket通信.png

tcp三次握手.png
Socket编程
    1.进程间通信
    2.API
        ServerSocket  服务器套接字
        Socket        套接字
    3.协议
        TCP   transfer control protocal 传输控制协议,面向连接,基于流
        UDP   user datagram protocal 用户数据报协议,无连接,无顺序,不安全
        IP    Internet protocal
        FTP   file transfer protocal
        SMTP  simple

UDP
    1.user datagram protcal,用户数据报协议
    2.DatagramSocket
        发送数据报包(Datagram Packet),也可以接收包。
        数据报套接字

Java socket 聊天室 例子

server 服务器端,处理数据连接,并转发消息

package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

 * 聊天室服务端
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();
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
    
     * 该线程处理客户端的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 客户端,监听发送事件,启动一个线程专门处理接收数据

package chat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

 * 聊天室客户端
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);
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}



效果如下,有个bug没解决,就是Client输入昵称无效,服务端没有收到,而是把Client说的第一句话当成了nickName····
原来是输入的nick字符串没有发送到服务器······
pic1.png

可以 @nickName:word,与其他上线的人交流
pic2.png



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



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

与上面的实现是完全不同的方式写的,但基本是一样的。

可以实现多人聊天,一对一聊天没写。

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

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

服务器server端,转发数据给所有上线的客户端

package day19;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

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界面,整个界面布局,对按钮的事件处理,调用客户端处理连接和显示数据

package day19;

import java.awt.FileDialog;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

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 = "连接出错";
                     * 关闭socket
                    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交互

package day19;

import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.util.List;
import java.util.Map;

 * 负责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");
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                }
            }
        };
         * 设置为守护线程,并启动
        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;
    }
    
     * 关闭socket方法
    public void close() {
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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

package day19;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

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();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
     * 重写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();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            return false;
        }
    }
}

发送消息


1.png

可以获得上线的所有客户端,显示在面板上


2.png

点击发送文件按钮可以发送文件


3.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 7.2 面向套接字编程我们已经通过了解Socket的接口,知其所以然,下面我们就将通过具体的案例,来熟悉Socke...
    lucas777阅读 1,172评论 0 2
  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 1,209评论 0 10
  • 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编...
    程序员欧阳阅读 1,998评论 1 37
  • 今天晚上宝贝本来想要全部的作业都写完,然后剩下的两天可以干点别的,结果她朋友又约她去玩会,她问我怎么办?我就说不然...
    孙雅璐妈妈阅读 153评论 0 0