Java Socket通信

Socket简介

Socket综述
Socket被翻译为套接字,是端到端的链接通信方式,处在网络七层(或五层)协议模型的传输层和应用层之间,我们这里主要基于TCP/IP链接
的Socket,这是对传输层的一个抽象封装,通过简单的api就可以实现端到端的传输。
Socket相关的类
  1. Socket:实现通信的主要类,流的读写等操作都是通过此类来完成。
  2. ServerSocket:socket服务器,创建服务器,等待链接接入,接入完成返回Socket进行相关通信。
  3. SocketImpl:实际工作的类,Socket和ServerSocket内部都是基于此完成,有子类如AbstractPlainSocketImpl、DualStackPlainSocketImpl等。
  4. SocketImplFactory:工厂模式,用于产生SocketImpl对象。

Socket使用

简单使用

服务器代码:

package edu.gj.demo1;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author Simple
 * @date 2020/4/23 14:58
 */
public class Server {


    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;

        OutputStream outputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        PrintStream printStream = null;
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader reader = null;

        try {
            //创建服务  等待链接
            serverSocket = new ServerSocket(8888);
            //链接获取到socket对象
            socket = serverSocket.accept();
            //获取输入输出流
            outputStream = socket.getOutputStream();
            inputStream = socket.getInputStream();
            bufferedOutputStream = new BufferedOutputStream(outputStream);
            printStream = new PrintStream(bufferedOutputStream);
            inputStreamReader = new InputStreamReader(inputStream);
            reader = new BufferedReader(inputStreamReader);

            String info = reader.readLine();
            System.out.println("服务器获取到信息:" + info);

            printStream.println("客户端,你好!");
            printStream.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null != reader){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStreamReader){
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStream){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != printStream){
                printStream.close();
            }
            if (null != bufferedOutputStream){
                try {
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != outputStream){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != socket){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != serverSocket){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }


}


客户端代码:

package edu.gj.demo1;

import java.io.*;
import java.net.Socket;

/**
 * @author Simple
 * @date 2020/4/23 14:58
 */
public class Client {

    public static void main(String[] args) {

        Socket socket = null;

        OutputStream outputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        PrintStream printStream = null;
        InputStream inputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader reader = null;


        try {
             socket = new Socket("localhost",8888);
            //获取输入输出流
            outputStream = socket.getOutputStream();
            inputStream = socket.getInputStream();
            bufferedOutputStream = new BufferedOutputStream(outputStream);
            printStream = new PrintStream(bufferedOutputStream);
            inputStreamReader = new InputStreamReader(inputStream);
            reader = new BufferedReader(inputStreamReader);

            printStream.println("服务器,你好!");
            printStream.flush();

            String info = reader.readLine();
            System.out.println("客户端收到反馈:" + info);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (null != reader){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStreamReader){
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != inputStream){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != printStream){
                printStream.close();
            }
            if (null != bufferedOutputStream){
                try {
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != outputStream){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != socket){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }

}

启动:
先启动服务器Server代码,再启动客户端代码Client
运行结果如下:
服务器:
日志1
客户端:
日志2
通信过程总结如下
1、服务器:

  1. 创建ServerSocket,等待链接。
  2. 链接获取到Socket对象。
  3. 获取输入输出流进行通信。
  4. 通信前程,关闭流。

2、客户端:

  1. 创建Socket对象,链接服务器。
  2. 获取输入输出流进行通信。
  3. 通信完成,关闭输入输出流
Socket长链接进行两个客户端之间通信
大家可以发现,上述只是一次性通信,并没有实现随时接入,也没有实现多客户端接入,下面我们解决这两个问题,实现两个客户端通信。

1、引入ServerThread类启动线程去处理多客户端接入问题问题。
2、服务器死循环实现接收多个客户端连接。
3、Socket死循环,随时接收信息,接收多条信息。
4、防止资源浪费,引入心跳机制。
5、防止消息混乱,引入消息机制。
具体代码实现如下:
1、消息分类实现,引入类MessageBean:

package edu.gj.demo;

/**
 * @author Simple
 * @date 2020/4/17 16:53
 */
public class MessageBean {

    /**
     * 消息类型
     * 0 心跳消息
     * 1 链接初始化消息
     * 2 系统分发消息
     * 3 点对点消息
     */
    private int type;
    /**
     * 用户名  发给系统初始化信息的时候用到
     */
    private String userName;

    /**
     * 消息发送者
     */
    private String sender;
    /**
     * 消息接受者
     */
    private String to;
    /**
     * 消息内容
     */
    private String content;

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getSender() {
        return sender;
    }

    public void setSender(String sender) {
        this.sender = sender;
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

2、多客户端接入问题,引入线程处理ServerThread:

package edu.gj.demo;

import com.google.gson.Gson;
import org.json.JSONObject;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;

/**
 * @author Simple
 * @date 2020/4/17 15:00
 */
public class ServerThread extends Thread {


    private String clientName;

    private Socket socket;


    InputStream inputStream = null;
    InputStreamReader inputStreamReader = null;
    BufferedReader bufferedReader = null;
    OutputStream outputStream = null;
    BufferedOutputStream bufferedOutputStream = null;
    PrintStream printStream = null;


    public ServerThread(Socket socket) {
        this.socket = socket;
        try {
            socket.setKeepAlive(true);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        try {
            inputStream = socket.getInputStream();
            inputStreamReader = new InputStreamReader(inputStream);
            bufferedReader = new BufferedReader(inputStreamReader);
            outputStream = socket.getOutputStream();
            bufferedOutputStream = new BufferedOutputStream(outputStream);
            printStream = new PrintStream(bufferedOutputStream);
            startMessageAccept(bufferedReader);
        } catch (Exception e) {
            e.printStackTrace();
            closeResources();
        }
    }

    private void closeResources() {
        try {
            inputStream.close();
            inputStreamReader.close();
            bufferedReader.close();
            outputStream.close();
            bufferedOutputStream.close();
            printStream.close();
            socket.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 发送消息
     *
     * @param message
     */
    public void sendMessage(String message) {
        printStream.println(message);
        printStream.flush();
    }


    public void startMessageAccept(BufferedReader reader) {
        lastHeartTime = System.currentTimeMillis();
        new Thread(new NoticeTimeRunnable()).start();
        isHearting = true;
        while (isHearting) {
            try {
                String message = reader.readLine();
                MessageBean messageBean = new Gson().fromJson(message,MessageBean.class);
                JSONObject jsonObject = new JSONObject(message);
                if (messageBean.getType() == 0) {
                    //心跳消息
                    System.out.println("心跳消息" + jsonObject.toString());
                } else {
                    //正常消息
                    handleMessage(messageBean);
                }
            } catch (IOException e) {
                e.printStackTrace();
                closeResources();
                return;
            }
        }
    }


    private void handleMessage(MessageBean body) {
        System.out.println("收到消息:" + new Gson().toJson(body));
        if (body.getType() == 3){
            Server.sendMessage(body);
        }else if (body.getType() == 1){
            clientName = body.getUserName();
        }
    }


    private long lastHeartTime;
    private boolean isHearting;

    private class NoticeTimeRunnable implements Runnable{

        public void run() {
            while (true){
                try {
                    Thread.sleep(50);
                    if (System.currentTimeMillis() - lastHeartTime > 1000){
                        isHearting = false;
                        closeResources();
                        return;
                    }
                    lastHeartTime = System.currentTimeMillis();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public String getClientName() {
        return clientName;
    }

}

3、两个客户端代码大同小异Client和Client02:

package edu.gj.demo;

import com.google.gson.Gson;
import org.json.JSONObject;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author Simple
 * @date 2020/4/16 13:55
 */
public class Client {

    public static void main(String[] args) throws IOException {


        Socket socket = new Socket("localhost", 8080);
        final PrintStream printStream = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
        final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        MessageBean messageBean = new MessageBean();
        messageBean.setType(1);
        messageBean.setUserName("client");
        printStream.println(new Gson().toJson(messageBean));
        printStream.flush();
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    try {
                        Thread.sleep(2 * 60 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    MessageBean messageBean = new MessageBean();
                    messageBean.setType(0);
                    messageBean.setContent("心跳消息");
                    printStream.println(new Gson().toJson(messageBean));
                    printStream.flush();
                }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        String message = reader.readLine();
                        MessageBean messageBean = new Gson().fromJson(message, MessageBean.class);
                        if (messageBean.getType() == 2) {
                            //系统分发:
                            System.out.println("系统消息:" + messageBean.getContent());
                        } else if (messageBean.getType() == 3) {
                            //点对点消息
                            System.out.println("收到来自:" + messageBean.getSender() + "的消息:" + messageBean.getContent());
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        return;
                    }
                }
            }
        }).start();


        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            MessageBean messageBeanSend = new MessageBean();
            messageBeanSend.setType(3);
            messageBeanSend.setContent(scanner.nextLine());
            messageBeanSend.setSender("client");
            messageBeanSend.setTo("client2");
            printStream.println(new Gson().toJson(messageBeanSend));
            printStream.flush();
        }

    }


}

package edu.gj.demo;

import com.google.gson.Gson;
import org.json.JSONObject;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author Simple
 * @date 2020/4/16 13:55
 */
public class Client02 {

    public static void main(String[] args) throws IOException {


        Socket socket = new Socket("localhost", 8080);
        final PrintStream printStream = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));
        final BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        MessageBean messageBean = new MessageBean();
        messageBean.setType(1);
        messageBean.setUserName("client2");
        printStream.println(new Gson().toJson(messageBean));
        printStream.flush();
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    try {
                        Thread.sleep(2 * 60 * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    MessageBean messageBean = new MessageBean();
                    messageBean.setType(0);
                    messageBean.setContent("心跳消息");
                    printStream.println(new Gson().toJson(messageBean));
                    printStream.flush();
                }
            }
        }).start();

        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    try {
                        String message = reader.readLine();
                        MessageBean messageBean = new Gson().fromJson(message, MessageBean.class);
                        if (messageBean.getType() == 2) {
                            //系统分发
                            System.out.println("系统消息:" + messageBean.getContent());
                        } else if (messageBean.getType() == 3) {
                            //点对点消息
                            System.out.println("收到来自:" + messageBean.getSender() + "的消息:" + messageBean.getContent());
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        return;
                    }
                }
            }
        }).start();


        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            MessageBean messageBeanSend = new MessageBean();
            messageBeanSend.setType(3);
            messageBeanSend.setContent(scanner.nextLine());
            messageBeanSend.setSender("client2");
            messageBeanSend.setTo("client");
            printStream.println(new Gson().toJson(messageBeanSend));
            printStream.flush();
        }

    }

}

4、服务端Server代码:

package edu.gj.demo;

import com.google.gson.Gson;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Simple
 * @date 2020/4/16 13:48
 */
public class Server {
    private static List<ServerThread> serverThreads = new ArrayList<ServerThread>();

    public static void main(String[] args) throws IOException {

        int count = 0;

        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("Server is start");
        while (true) {
            count++;
            Socket socket = serverSocket.accept();
            System.out.println("accept client count:"+count);
            ServerThread serverThread = new ServerThread(socket);
            serverThreads.add(serverThread);
            serverThread.start();
        }
    }


    public static void sendMessage(MessageBean messageBean){
        for (ServerThread serverThread :
                serverThreads) {
            if (serverThread.getClientName().equals(messageBean.getTo())){
                serverThread.sendMessage(new Gson().toJson(messageBean));
            }
        }
    }


}

5、启动:
先启动Server,接着启动客户端Client和客户端Client02
启动后在Client控制台输入你好,再在客户端Client02控制台输入你好,日志如下:
服务器:
日志3
Client:
日志4
Client02:
日志5

源码

源码链接: Demo源码

总结

1、以前总觉得Socket很神秘,玩不转,其实不是那么回事,只是自己没有仔细研究而已。
2、多思考,思路很重要。
3、这里只是抛砖引玉,实现了简单的通信过程,难免有考虑不周或者思维局限。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值