TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
步骤:
JAVA中,提供了服务器类与客户端类
Socket
对象,向服务端发出连接请求,服务端响应请求,两者建 立连接开始通信。ServerSocket
对象,相当于开启一个服务,并等待客户端 的连接。构造方法:
public Socket(String host, int port)
:创建套接字对象并将其连接到指定主机上的指定端口号。如果指 定的host是null ,则相当于指定地址为回送地址。
成员方法:
public InputStream getInputStream()
: 返回此套接字的输入流。public OutputStream getOutputStream()
: 返回此套接字的输出流。public void close()
:关闭此套接字。public void shutdownOutput()
: 禁用此套接字的输出流ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求
构造方法 :
public ServerSocket(int port)
:使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指 定的端口号上,参数port就是端口号。
成员方法:
public Socket accept()
:侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法 会一直阻塞直到建立连接。getOutputStream()
方法获取OutputStream
,然后OutputStream
调用write()来写数据给服务器端getInputStream
方法获取 InputStream
,调用read()读客户端的数据getOutputStream()
方法获取OutputStream
,write()回写数据给客服端getInputStream
方法获取 InputStream
,read()解析回写的数据服务器端:
package restudy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
// 1.创建服务器ServerSocket对象和指定端口
ServerSocket serverSocket = new ServerSocket(8888);
// 2.使用ServerSocket的方法accept,获取请求的客户端对象Socket
Socket socket = serverSocket.accept();
// 至此,服务器创建完成,等待客户端发送数据过来
// 3.使用Socket获取InputStream
InputStream inputstream = socket.getInputStream();
// 4.读取数据
byte[] bytes = new byte[1024];// 一次读取1024个字节
int len = inputstream.read(bytes);// 读取,空就返回-1
System.out.println(new String(bytes, 0, len));
// 5.获取OutputStream,准备回写给客户端
OutputStream outputStream = socket.getOutputStream();
// 6.回写
outputStream.write("服务器端收到,服务器端回写".getBytes());
// 7.释放资源
socket.close();
serverSocket.close();
}
}
客户端:
package restudy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1.创建Socket对象
Socket socket = new Socket("127.0.0.1", 8888);
// 2.获取OutputStream
OutputStream cos = socket.getOutputStream();
// 3.write数据
cos.write("客户端向服务器端发送".getBytes());// getBytes(),把字符串转换成字符数组
// 到这里,客户端向服务器发送数据了,服务器端处理数据了
// 这里,就是客户端已经发送给服务器端了,然后服务器端回写给客户端
// 4.获取InputStream,准备读服务器端的回写
InputStream inputstream = socket.getInputStream();
// 5.read
byte[] bytes = new byte[1024];// 一次读取1024个字节
int len = inputstream.read(bytes);// 读取,空就返回-1
System.out.println(new String(bytes, 0, len));
// 6.释放资源
socket.close();
}
}
先启动服务器,再启动客户端,进行两个控制台的查看
服务器端控制台:
客户端控制台:
上面的例子可以看到,我们的读,都只是读一个,没有循环的,那么但read的bytes[]数组超过我们预设的时候,就要用到while循环来判断是否为-1了,但是,这有个很神奇的BUG
就把上面注释的,改成下面的,就只有服务器端有接受到消息,客户端没有接收到消息
至于为什么这样,是因为read和write有阻塞读不到-1,至于为什么我也不懂,都是非人话看不懂,那我就说怎么解决吧
在一个端口中write完之后,使用socket.shutdownOutput();
,就可以解决
客户端:
package restudy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1.创建Socket对象
Socket socket = new Socket("127.0.0.1", 8888);
// 2.获取OutputStream
OutputStream cos = socket.getOutputStream();
// 3.write数据
cos.write("客户端向服务器端发送 \n".getBytes());// getBytes(),把字符串转换成字符数组
socket.shutdownOutput();
// 到这里,客户端向服务器发送数据了,服务器端处理数据了
// 这里,就是客户端已经发送给服务器端了,然后服务器端回写给客户端
// 4.获取InputStream,准备读服务器端的回写
InputStream inputstream = socket.getInputStream();
// 5.read
byte[] bytes = new byte[1024];// 一次读取1024个字节
int len;
while ((len = inputstream.read(bytes)) != -1) {
String str = new String(bytes, 0, len);
System.out.println(str);
}
// 6.释放资源
socket.close();
}
}
服务器端:
package restudy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
// 1.创建服务器ServerSocket对象和指定端口
ServerSocket serverSocket = new ServerSocket(8888);
// 2.使用ServerSocket的方法accept,获取请求的客户端对象Socket
Socket socket = serverSocket.accept();
// 至此,服务器创建完成,等待客户端发送数据过来
// 3.使用Socket获取InputStream
InputStream inputstream = socket.getInputStream();
// 4.读取数据
byte[] bytes = new byte[1024];// 一次读取1024个字节
int len;
while ((len = inputstream.read(bytes)) != -1) {
String str = new String(bytes, 0, len);
System.out.println(str);
}
// 5.获取OutputStream,准备回写给客户端
OutputStream outputStream = socket.getOutputStream();
// 6.回写
outputStream.write("服务器端收到,服务器端回写 \n".getBytes());
socket.shutdownOutput();
// 7.释放资源
socket.close();
serverSocket.close();
}
}
第一个方法,的确好用,但是这样,就只能对服务器发送一次对话而已,如果想要和服务器多次对话,每对一次都要连接一次,效率低下在一段话结尾加上\n
,在另一个端口read时,如果有\n
,就break
,结束循环
客户端:
package restudy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1.创建Socket对象
Socket socket = new Socket("127.0.0.1", 8888);
// 2.获取OutputStream
OutputStream cos = socket.getOutputStream();
// 3.write数据
cos.write("客户端向服务器端发送 \n".getBytes());// getBytes(),把字符串转换成字符数组
// socket.shutdownOutput();
// 到这里,客户端向服务器发送数据了,服务器端处理数据了
// 这里,就是客户端已经发送给服务器端了,然后服务器端回写给客户端
// 4.获取InputStream,准备读服务器端的回写
InputStream inputstream = socket.getInputStream();
// 5.read
byte[] bytes = new byte[1024];// 一次读取1024个字节
// int len = inputstream.read(bytes);// 读取,空就返回-1
// System.out.println(new String(bytes, 0, len));
int len;
while ((len = inputstream.read(bytes)) != -1) {
String str = new String(bytes, 0, len);
System.out.println(str);
// 如果有结束符号,退出循环
if (str.indexOf('\n') > 0) {
break;
}
}
// 6.释放资源
socket.close();
}
}
服务器端
package restudy;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
// 1.创建服务器ServerSocket对象和指定端口
ServerSocket serverSocket = new ServerSocket(8888);
// 2.使用ServerSocket的方法accept,获取请求的客户端对象Socket
Socket socket = serverSocket.accept();
// 至此,服务器创建完成,等待客户端发送数据过来
// 3.使用Socket获取InputStream
InputStream inputstream = socket.getInputStream();
// 4.读取数据
byte[] bytes = new byte[1024];// 一次读取1024个字节
// int len = inputstream.read(bytes);// 读取,空就返回-1
// System.out.println(new String(bytes, 0, len));
int len;
while ((len = inputstream.read(bytes)) != -1) {
String str = new String(bytes, 0, len);
System.out.println(str);
// 如果有结束符号,退出循环
if (str.indexOf('\n') > 0) {
break;
}
}
// 5.获取OutputStream,准备回写给客户端
OutputStream outputStream = socket.getOutputStream();
// 6.回写
outputStream.write("服务器端收到,服务器端回写 \n".getBytes());
// socket.shutdownOutput();
// 7.释放资源
socket.close();
serverSocket.close();
}
}
和文字传送差不多吧,就复杂一点而已,在这基础上加点东西
然后服务器端是要一直开着的,因为你关了客户不就炸了吗。客户端可能有好几个,当上传大的东西,就会很慢,所以我们每一次接受到客户的请求,都要新创建一个线程。然后上传的名字也是要随机的。
package restudy;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;
public class TCPServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器 启动..... ");
// 1. 创建服务端ServerSocket
ServerSocket serverSocket = new ServerSocket(8888);
// 查看这个路径是不是真的有,没有就创建
File file = new File("E:\\uu\\upload");
if (!file.exists()) {
file.mkdirs();
}
// 服务器要一直存在
while (true) {
// 2. 建立连接
Socket accept = serverSocket.accept();
// 接受到一个,就new一个线程
new Thread(() -> {
// try包住
try {
// 3. 创建流对象
// 3.1 获取输入流,读取文件数据
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
// 3.2 创建输出流,保存到本地 .
String fileName = "\\" + System.currentTimeMillis() + new Random().nextInt(999) + ".png";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file + fileName));
// 4. 读写数据
byte[] b = new byte[1024 * 8];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
} // while
System.out.println("文件上传已保存");
bos.close();
bis.close();
accept.close();
} catch (IOException e) {
// TODO: handle exception
System.out.println(e);
} // catch
}).start();
} // while--true
}// main
}
package restudy;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1.创建流对象
// 1.1 创建输入流,读取本地文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\uu\\p5.png"));
// 1.2 创建输出流,写到服务端
Socket socket = new Socket("127.0.0.1", 8888);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
// 2.写出数据.
byte[] b = new byte[1024 * 8];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
bos.flush();
}
System.out.println("文件发送完毕");
// 3.释放资源
bos.close();
socket.close();
bis.close();
System.out.println("文件上传完毕 ");
}
}