文章目录
- 前言
- 一、简单通信
- 1. 服务端
- 2. 客户端
- 二、文件上传
- 1. 服务端
- 2. 客户端
- 3. 工具类的实现
- 总结
前言
使用Java实现进程的TCP连接和Socket通信。
一、简单通信
实现Server和Client的简单通信。
需要实现的功能:Client发送“hello server”至服务端,服务端成功接收后发送“hello Client”。然后关闭服务端和客户端。
1. 服务端
首先是Server端的代码
/**
服务端,接收“hello server”语句并发送“hello client”语句
*/
public class SocketTCPSimpleChatServer{
public static void main(String[] args) throws IOException{
//1. 在9999端口监听Client
ServerSocket serverSocket = new ServerSocket(9999);
//2. 此时socket在阻塞等待,如果有client接入则返回一个Socket对象
Socket socket = serverSocket.accept();
//3. 通过IO来读取Client发送过来的消息
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];//使用字节流的方式读取消息内容
int readLen=0;
while((readLen=inputStream.read(buf))!=-1){
System.out,println(new String(buf,0,readLen));
}
//4. 接收成功后回传“hello client”,这里使用字符流方式(演示两种方式传送)
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.writer("hello client");
bufferedWriter.newLine();//换行符结束,要求对方使用readLine
bufferedWriter.flush();//需要刷新才能写入通道
//5. 关闭所有流
outputStream.close();
inputStream.close();
socket.close();
serverSocket.close();
}
}
2. 客户端
接下来是Client端的代码
/**
客户端,发送“hello server”并接受“hello client”
*/
public class SocketTCPSimpleChatClient{
public static void main(String[] args) throws Exception{
//1. 使用InetAddress绑定ip和端口
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
//2. 获取输出流并发送hello server
OutputStream outputStream = new socket.getOutputStream();
//使用字符流发送
outputStream.write("hello server".getBytes());
socket.shutdownOutput();
//3. 使用字符流的方式接收hello client
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s = bufferedReader.readLine();//由于server用了newLine
System.out,println(s);
//4. 关闭相关的流
inputStream.close();
outputStream.close();
socket.close();
}
}
二、文件上传
思路就是客户端获取文件(这里是图片)的内容存入到二进制数组中,再通过socket传输到服务端,服务端获取成功后保存到相应的文件夹,最后发送ok到客户端表示已经成功接收。
1. 服务端
代码示例如下
public class SocketTCPUploadServer{
public static void main(String[] args) throws IOException{
//1. 启动,监听端口
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
//2. 接收客户端的信息
BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
//另外编写了一个工具类,将流转换成二进制 StreamUtils.java
byte[] buf = StreamUtils.streamToByteArray(bufferedInputStream);
//将接收到的内容存到服务器本地
String serverFilePath = "xxx";
BufferedOutputStream bufferedOutputStream = new BufferedOuputStream(new FileOutputStream(serverFilePath));
bufferedOutputStream.write(buf);
bufferedOutputStream.close();
//回传接收成功给客户端
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bufferedWriter.write("OK");
bufferedWriter.newLine();
bufferedWriter.flush();
//关闭所有流
bufferedWriter.close();
bufferedOutputStream.close();
bufferedInputStream.close();
socket.close();
serverSocket.close();
}
}
关于InputStream和BufferedInputStream的区别:
BufferedInputStream是一个带有缓冲区域的InputStream,它的继承体系如下:
InputStream
|__FilterInputStream
|__BufferedInputStream
以看作是BufferedInputStream对外提供滑动读取的功能实现,通过预先读入一整段原始输入流数据至缓冲区中,而外界对BufferedInputStream的读取操作实际上是在缓冲区上进行,如果读取的数据超过了缓冲区的范围,那么BufferedInputStream负责重新从原始输入流中载入下一截数据填充缓冲区,然后外界继续通过缓冲区进行数据读取。这样的设计的好处是:避免了大量的磁盘IO,因为原始的InputStream类实现的read是即时读取的,即每一次读取都会是一次磁盘IO操作(哪怕只读取了1个字节的数据),可想而知,如果数据量巨大,这样的磁盘消耗非常可怕。而通过缓冲区的实现,读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进行一次磁盘IO,载入一段数据填充缓冲,那么下一次读取一般情况下就直接可以从缓冲区读取,减少了磁盘IO。
————————————————
原文链接:
2. 客户端
客户端代码如下:
public class SocketTCPUploadClient{
public static void main(String[] args) throws Exception{
Socket socket = new Socket(InetAddress.getLocalHost(),9999);
//获取所要上传的文件内容
String filePath = "xxx";
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileOutputStream(filePath));
byte[] buf=StreamUtils.streamToByteArray(bufferedInputStream);
//创建一个输出流传输内容
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
bufferedOutputStream.write(buf);
bufferedOutputStream.close();
socket.shutdownOutput();
//接收服务端的响应
InputStream inputStream = socket.getInputStream();
String s = StreamUtils.streamToString(inputStream);
System.out.println(s);
//关闭所有流
inputStream.close();
bufferedInputStream.close();
socket.close();
}
}
3. 工具类的实现
上面代码中所用到的工具类StreamUtils.java代码如下:
/**
StreamUtils.java
实现了两个方法,分别是streamToByteArray()和streamToString()
*/
public class StreamUtils{
public static byte[] streamToByteArray(InputStream is) throws Exception{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int readLen = 0;
while((readLen=is.read(b))!=-1){
bos.write(b,0,readLen);
}
//将流转换成二进制数组
byte[] array = bos.toByteArray();
bos.close();
return array;
}
public static String streamToString(InputStream is) throws Exception{
BufferedReader bufferdReader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line;
while((line=builder.readLine())!=null){
builder.append(line+"\r\n");
}
return builder.toString();
}
}
总结
文章给出了简易的TCP进程通信代码以及客户端上传文件至服务端的简单代码。