做个笔记,在接收16进制数据的时候乱码了。原因是Socket在接收数据的时候需要根据不同的数据定义不同的接收方式,也就是约定好传输协议(具体体现在后面服务端接收16进制那里)。
字符串的发送接收
字符串发送:
字符串接收:
客户端代码:
没什么好说的,复制粘贴导包。
public static void main(String[] args)
{
try
{
Socket socket = new Socket("localhost", 8888);
//得到一个输出流,用于向服务器发送数据
OutputStream outputStream = socket.getOutputStream();
//将写入的字符编码成字节后写入一个字节流
OutputStreamWriter writer = new OutputStreamWriter(outputStream, "UTF-8");
System.out.println("请输入数据:");
while (true)
{
Scanner sc = new Scanner(System.in);
String data = sc.nextLine();
writer.write(data);
//刷新缓冲
writer.flush();
//只关闭输出流而不关闭连接
socket.shutdownOutput();
//获取服务器端的响应数据
//得到一个输入流,用于接收服务器响应的数据
InputStream inputStream = socket.getInputStream();
//字节流以UTF-8的编码转换为字符流
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
//为输入流添加缓冲
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println("客户端IP地址:" socket.getInetAddress().getHostAddress());
String info;
//输出服务器端响应数据
while ((info = bufferedReader.readLine()) != null)
{
System.out.println("收到来自服务端的信息:" info);
}
//关闭资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
writer.close();
outputStream.close();
socket.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
服务端代码:
注意readLine()方法,客户端发送的内容结尾一定要有换行符,不然会一直阻塞导致客户端得不到响应。其它注释里都有。
try
{
System.out.println(">>>服务启动,等待终端的连接\n");
ServerSocket server = new ServerSocket(8888);
int count = 0;
while (true)
{
//开启监听
Socket socket = server.accept();
count ;
System.out.println(">>>第" count "个终端连接成功");
ServerThread thread = new ServerThread(socket);
thread.start();
}
}
catch (IOException e)
{
e.printStackTrace();
}
/**
* 短连接
*/
public class ServerThread extends Thread
{
private Socket m_socket;
public ServerThread(Socket socket)
{
this.m_socket = socket;
}
@Override
public void run()
{
//字节输入流
InputStream inputStream = null;
//字节输出流
OutputStream outputStream = null;
//字节输入流到字符输入流的转换
InputStreamReader inputStreamReader = null;
//加快字符读取速度
BufferedReader bufferedReader = null;
//打印输出流
PrintWriter printWriter = null;
try
{
inputStream = m_socket.getInputStream();
String info;
inputStreamReader = new InputStreamReader(inputStream);
bufferedReader = new BufferedReader(inputStreamReader);
//注意,readLine()方法如果没有读到报文结束符(换行)会一直阻塞
while ((info = bufferedReader.readLine()) != null)
{
System.out.println(">>>线程" this.getId() "收到来自终端的信息:" info);
}
//关闭终端的输入流(不关闭服务端的输出流),此时m_socket虽然没有关闭,但是客户端已经不能再发送消息
m_socket.shutdownInput();
//解析终端的信息
String responseStr = "Null...";
if (null != info && !"".equals(info))
{
//模拟业务处理Thread.sleep(10000);
responseStr = new MessageReceive().RecevieHexStr(info);
}
outputStream = m_socket.getOutputStream();
printWriter = new PrintWriter(outputStream);
printWriter.write(responseStr);
printWriter.flush();
}
catch (Exception e)
{
e.printStackTrace();
}
//关闭资源
finally
{
System.out.println(">>>本次连接已断开\n");
try
{
if (printWriter != null)
printWriter.close();
if (outputStream != null)
outputStream.close();
if (bufferedReader != null)
bufferedReader.close();
if (inputStreamReader != null)
inputStreamReader.close();
if (inputStream != null)
inputStream.close();
if (m_socket != null)
m_socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
16进制数据的发送接收
客户端代码:无
由Socket测试工具来完成,网上大把,自行下载。
服务端代码:
开头说过使用Socket通信需要约定好协议,否则服务端不知道你客户端内容是否发送完毕,就会一直阻塞。前面的字符串的发送和接收其实也是有协议存在的,这个协议就是字符串和换行符,服务端读取的时候将字节流转为字符流,然后一行行的读取,读到换行符的时候就表示客户端的消息已发送完毕。
但是当客户端发送的是16进制数据的时候,这个时候服务端仍然用读取字符串的方式去处理就会乱码。所以这里采用的方法是将字节流以字节的形式一个一个的读出来,然后每个字节转为16进制的字符串。
byte[] bytes = new byte[1];
while ((dataInputStream.read(bytes)) != -1)
String tempStr = ConvertUtil.ByteArrayToHexStr(bytes);
同理,也需要一个标识符表示客户端的内容已经发送完毕,否则read()方法也会一直阻塞,导致客户端得不到响应。比如客户端发送内容的末尾加个7E,当服务端读到7E就跳出While循环,但前提是要保证发送内容里面7E是唯一的,如果有其它需要用7E表示的自行转义,然后服务端读取完内容以后再转义回来。
这里我使用的是另外一种方法判断客户端的内容是否发送完毕:InputStream对象的available()方法。
dataInputStream.available()
官方解释:返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数。下一个调用可能是同一个线程,也可能是另一个线程。一次读取或跳过此估计数个字节不会受阻塞,但读取或跳过的字节数可能小于该数。大白话就是:返回剩余未读长度
至于其它的注释里都有写
/**
* 短连接
*/
public class ServerThread extends Thread
{
private Socket m_socket;
public ServerThread(Socket socket)
{
this.m_socket = socket;
}
@Override
public void run()
{
//字节输入流
InputStream inputStream = null;
//字节输出流
OutputStream outputStream = null;
//缓冲输入流
BufferedInputStream bufferedInputStream = null;
//数据输入流
DataInputStream dataInputStream = null;
//打印输出流
PrintWriter printWriter = null;
try
{
inputStream = m_socket.getInputStream();
String info = "";
bufferedInputStream = new BufferedInputStream(inputStream);
dataInputStream = new DataInputStream(bufferedInputStream);
//一次读取一个byte
byte[] bytes = new byte[1];
//注意,read()方法如果没有数据会一直阻塞,也就是永远不会等于-1,除非客户端调用close(),如果想在while循环外部获取数据则需要设定跳出条件
while ((dataInputStream.read(bytes)) != -1)
{
String tempStr = ConvertUtil.ByteArrayToHexStr(bytes) " ";
info = tempStr;
//返回下次调用可以不受阻塞地从此流读取或跳过的估计字节数,如果等于0则表示已经读完
if (dataInputStream.available() == 0)
{
System.out.println(">>>终端信息读取完毕,最后一位:" tempStr);
break;
}
}
System.out.println(">>>线程" this.getId() "收到来自终端的信息:" info);
//关闭终端的输入流(不关闭服务端的输出流),此时m_socket虽然没有关闭,但是客户端已经不能再发送消息
m_socket.shutdownInput();
//解析终端的信息
String responseStr = "Null...";
//模拟业务处理
Thread.sleep(10000);
outputStream = m_socket.getOutputStream();
printWriter = new PrintWriter(outputStream);
printWriter.write(responseStr);
printWriter.flush();
}
catch (Exception e)
{
e.printStackTrace();
}
//关闭资源
finally
{
System.out.println(">>>本次连接已断开\n");
try
{
if (printWriter != null)
printWriter.close();
if (outputStream != null)
outputStream.close();
if (inputStream != null)
inputStream.close();
if (bufferedInputStream != null)
bufferedInputStream.close();
if (dataInputStream != null)
dataInputStream.close();
if (m_socket != null)
m_socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
自定义的转换类:
/**
* 普通byte[]转16进制Str
*
* @param array
*/
public static String ByteArrayToHexStr(byte[] array)
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < array.length; i )
{
String hex = Integer.toHexString(array[i] & 0xFF);
if (hex.length() == 1)
{
stringBuilder.append("0");
}
stringBuilder.append(hex);
}
return stringBuilder.toString();
}
来源:https://www./content-1-373801.html
|