Java的Socket客户端多线程实例

dahlin
11
2020-04-01

Java的Socket客户端多线程实例

最近看博客,发现了一个比较好用的Java原生Socket通信程序,但是它传输接收的都是原始字符串,我们知道实际用的时候,很少用原生的字符串传,都是编码成字节,做一些自定义协议转换后才传,目的是加密和防止粘包丢包脏包,而且传输的是字节数组,很少直接传串的。而且原博客里的代码有个bug,就是发送完消息,没等接收,输出流就给关闭了,造成接收线程无法收到服务端发来的消息,一直报Socket is Closed,所以我稍微改了一下,在这里分享一下,我只改了客户端,服务端同理,不在赘述。

1. 所需包引用导入

这是所需导入的类库包,建议从Maven上链接自动导入。

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import java.io.*;
import java.math.BigInteger;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

2. 源代码讲解

看起来不复杂,定义一个Client类,继承Thread线程类,这点跟Python的多线程差不多,不知道到底谁抄的谁的,然后定义全局的Socket对象,和全局的 输出流对象,这点儿是我后改。然后重写run的执行方法,在run 方法中先异步另起一个线程调用一个发送消息的方法,然后继续走主流程,在主线程里等待收到的服务器消息。

在发送消息的时候,需要先把待发送的打包好的自定义对象转换成json串,然后按自定义的规则将字符串转换成byte数组,再发送出去。这里的StrConvetToByteArray(string jsonStr)就是将json串按照自定义的规则转换为byte数组的方法,该方法需要自己去实现。因为自定义的协议头多种多样,可以随意设计。

然后就是在负责接收消息的主线程中的 有个ByteAarryConvertToString(byte[] receiveMsg)方法,该方法的作用是将收到的服务端返回的字节数组转成字符串,方法名是我随意起的,这里需要自己去实现。其实就是和StrConvetToByteArray(string jsonStr)方法反着来就行,类似于一个编码,一个解码。

public class Client extends Thread {

    //定义一个Socket对象
    Socket socket = null;
    //输出流写操作
    OutputStream os= null;
    
    public Client(String host, int port) {
        try {
            //需要服务器的IP地址和端口号,才能获得正确的Socket对象            socket = new Socket(host, port);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        //客户端一连接就可以写数据给服务器了
        new sendMessThread().start();
        super.run();

        try {
            System.out.println(socket.isConnected());
            // 读Sock里面的数据
            InputStream s = socket.getInputStream();
            byte[] buf = new byte[1024];
            int len = 0;
            while ((len = s.read(buf)) != -1) {
            	// 通过自定义的协议将读取的字节流转换成字符串。
                String msgServer = ByteAarryConvertToString(buf);
                System.out.println("Server Message:" + msgServer);
            }
            s.close();
            os.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

	//往Socket里面写数据,需要新开一个线程
	class sendMessThread extends Thread{

		@Override
		public void run() {
			super.run();
			// 自定义对象
			CObject cc = new CObject ("admin","123456",
					"test",df.format(new Date()),
					UUID.randomUUID().toString(),"");
			// 用Gson将对象序列化成转成Json串
			Gson gson = new GsonBuilder().create();
			String cJsonStr= gson.toJson(cc);
			System.out.println(cJsonStr);

			try {
				byte[] serverData= StrConvetToByteArray(cJsonStr);
				os= socket.getOutputStream();
				os.write(serverData);
				os.flush();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (DecoderException e) {
				e.printStackTrace();
			}
		}
	}
	
	//函数入口
	public static void main(String[] args) {
		//需要服务器的正确的IP地址和端口号
		Client clientTest=new Client("192.168.1.4", 54001);
		clientTest.start();
	}
}
	

3. 代码出处链接

动物装饰