Java编程:掌握TCP通信

发表时间: 2024-04-20 08:04

1.1. Java的TCP

面向连接, 数据安全, 区分服务器端和客户端.

TCP分为Socket(客户端)和ServerSocket(服务端)

需要分别建立客户端和服务器端

客户端和服务端建立连接后,通过Socket中的IO流进行数据的传输

需要关闭关闭socket

同样,客户端与服务器端是两个独立的应用程序。

1.1.1. TCP客户端

第一步:创建客户端Socket

需要指定连接到服务器的地址和端口号, 并尝试连接

客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。

Socket socket = new Socket("192.168.1.220", 8888);

第二步:连接成功获取输入输出流

连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通getInputStream(),getOutputStream()获取即可。

socket.getInputStream();

socket.getOuputStream();

第三步: 将数据写出到服务端

使用字节输出流的write() 方法

第四步:关闭socket

调用close方法

连接成功之后获取输入输出流

socket.getInputStream();

socket.getOuputStream();

获取流之后就可以通过输入输出流发送和读取数据了, 客户端的输入流连接服务端输出流, 客户端输出流连接服务端输入流

客户端案例:

public class TcpClient {

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

System.out.println("客户端启动...");

// 创建客户端

Socket socket = new Socket("127.0.0.1", 50000);

// 与服务端建立连接,获取输入输出流

InputStream in = socket.getInputStream();

OutputStream out = socket.getOutputStream();

// 将数据写出到服务端

System.out.println("客户端发送数据...");

out.write("Tcp,你好我是客户端...".getBytes());

// 关闭socket

out.close();

}

}


1.1.2. TCP服务端:

第一步: 创建服务端

ServerSocket, 需要指定端口号. 客户端连接的就是这个端口.

创建ServerSocket

ServerSocket serverSocket = new ServierSocket(8888);

第二步:和客户端建立连接

通过accept方法

Socket accept()

侦听并接受到此套接字的连接。

该方法会侦听是否有客户端连接,如果有建立连接,并获取客户端的Socket

也就是说服务端创建之后可以获取客户端连接, 返回一个Socket对象, 这个Socket就是和客户端连接的Socket

Socket socket = serverSocket.accept();

第三步: 接受客户端的数据,获取客户端的数据

服务端获取这个socket的输入输出流, 就可以和客户端发送接收数据了socket.getInputStream();

socket.getOutputStream();

第五步:获取客户端的ip地址和端口号

使用服务端获取的Socket 获取ip地址和端口.

InetAddress getInetAddress()

返回套接字连接的地址。


int getPort()

返回此套接字连接到的远程端口。

第四步:关闭客户端和服务端

在服务端中分别调用close方法.

1.1.3. 案例一:

客户端向服务端发送数据,服务端获取并进信息打印在控制台.

public class TcpServer {

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

System.out.println("服务端启动:");

// 服务端,监听端口

ServerSocket server = new ServerSocket(50000);

// 使用acept进入侦听状态,获取客户端数据

Socket socket = server.accept();

// 获取输入流输出流

InputStream in = socket.getInputStream();

OutputStream out = socket.getOutputStream();

// 获取客户端ip,端口

InetAddress inetAddress = socket.getInetAddress();

String ip = inetAddress.getHostAddress();

int port = socket.getPort();

// 获取客户端数据

byte[] byt = new byte[1024];

System.out.println("服务端接受数据:");

int len = in.read(byt);

String mess = new String(byt, 0, len);

System.out.println("该客户端:" + ip + ":" + port + "发送了:" + mess);

// 关闭客户端

socket.close();

// 关闭服务端

server.close();

}

}

1.1.4. 案例二

客户端和服务端实现交互

客户端给服务端发送信息,并接收服务端的回馈信息。

例如:

首先客户端向服务器通话

客户端:你好吗?服务器

服务器接到信息

服务器:收到客户端消息

客户端说:你好吗?服务器

 服务器回馈信息

服务器:收到客户端消息

客户端说:你好吗?

服务器说:我很好

 客户端接到服务器的信息

客户端:你好吗?服务器

客户端: 收到服务器消息

服务器说: 我很好

客户端:

public class TcpClient {

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

System.out.println("客户端启动...");

// 创建客户端

Socket socket = new Socket("127.0.0.1", 50000);

// 与服务端建立连接,获取输入输出流

InputStream in = socket.getInputStream();

OutputStream out = socket.getOutputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(

System.in));

byte[] byt = new byte[1024];

while (true) {

// 将数据写出到服务端

System.out.println("客户端发送数据...");


System.out.println("你说:");

String mess = br.readLine();

System.out.println("你告诉了服务端:");

out.write(mess.getBytes());

// 获取服务端的回话

int len = in.read(byt);

System.out.print("服务端告诉我了:");

System.out.println(new String(byt, 0, len));

if ("bye".equals(mess)) {

break;

}

}

// 关闭socket

out.close();

}

}

服务端:

public class TcpServer {

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

System.out.println("服务端启动:");

// 服务端,监听端口

ServerSocket server = new ServerSocket(50000);

// 使用acept进入侦听状态,获取客户端数据

Socket socket = server.accept();

// 获取输入流输出流

InputStream in = socket.getInputStream();

OutputStream out = socket.getOutputStream();

// 获取客户端ip,端口

InetAddress inetAddress = socket.getInetAddress();

String ip = inetAddress.getHostAddress();

int port = socket.getPort();

byte[] byt = new byte[1024];

BufferedReader br = new BufferedReader(new InputStreamReader(

System.in));

while (true) {

// 获取客户端数据

System.out.println("服务端接收数据:");

int len = in.read(byt);

String mess = new String(byt, 0, len);

System.out.println("客户端:" + ip + ":" + port + "告诉了服务端:" + mess);

// 向客户端回话.

System.out.println("你要告诉客户端:");

String str = br.readLine();

out.write(str.getBytes());

if ("bye".equals(mess)) {

break;

}

}

// 关闭客户端

socket.close();

// 关闭服务端

server.close();

}

}


1.1.5. 案例三:

服务器可以客户端的连接.客户端通过键盘录入数据,发送到服务端,服务端接收到数据后,转换成大写在返回给客户端。

需求:

服务器可以客户端的连接.客户端通过键盘录入数据,发送到服务端,服务端接收到数据后,转换成大写在返回给客户端。

客户端:

public class Cilent {

public static void main(String[] args) throws UnknownHostException,

IOException {

System.out.println("客户端启动...");

Socket socket = new Socket("127.0.0.1", 50000);

InputStream in = socket.getInputStream();

OutputStream out = socket.getOutputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

System.out.println("请录入:");

String mess = br.readLine();

out.write(mess.getBytes());

System.out.println("-----------获取服务器的回馈--------------");

byte[] byt = new byte[1024];

int len = in.read(byt);

System.out.println(new String(byt, 0, len));

socket.close();

}

}

服务端:

public class Cilent {

public static void main(String[] args) throws UnknownHostException,

IOException {

System.out.println("客户端启动...");

Socket socket = new Socket("127.0.0.1", 50000);

InputStream in = socket.getInputStream();

OutputStream out = socket.getOutputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

System.out.println("请录入:");

String mess = br.readLine();

out.write(mess.getBytes());

System.out.println("-----------获取服务器的回馈--------------");

byte[] byt = new byte[1024];

int len = in.read(byt);

System.out.println(new String(byt, 0, len));

socket.close();

}

}

1.1.6. 多线程服务器

服务器一般是为多个客户端同时服务的, 当每个客户端连接到服务器时, 可以开一条单独的线程处理这个连接.

服务器端:

public class Server {

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

System.out.println("服务器启动...");

ServerSocket server = new ServerSocket(50000);

while (true) {

Socket socket = server.accept();

ServerRunnable serverRunnable = new ServerRunnable(socket);

Thread t1 = new Thread(serverRunnable);

t1.start();

}

}

}

class ServerRunnable implements Runnable {

Socket socket;

ServerRunnable(Socket socket) {

this.socket = socket;

}

@Override

public void run() {

try {

InputStream in = socket.getInputStream();

OutputStream out = socket.getOutputStream();

byte[] byt = new byte[1024];

int len = in.read(byt);

String str = new String(byt, 0, len);

System.out.println(str);

System.out.println("------------服务器转大写--------------");

String upperCase = str.toUpperCase();

System.out.println(upperCase);

// 转大写,写回给客户端

out.write(upperCase.getBytes());

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

socket.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}


1.1.7. 上传文件

分析:

先启动服务器,再启动客户端.客户端和服务端需要建立连接,服务端向客户端反馈信息.告知客户端”连接成功,请上传文件路径:

客户端:

一: 客户端需要检测文件是否存在,是否是文件.

二: 客户端将文件名,和文件的长度发给服务器,服务器以此判断文件是否存在,文件大小用以验证文件是否上传完毕.

三: 客户端根据服务器端的反馈信息,如果服务器存在该文件,上传结束,不存在开始上传.

四: 客户端开始上传, 使用字节输入流,将文件加载到输入流中,读取输入流,通过socket的输出流写到服务器.

五: 服务器:

根据客户传送的信息,判断文件是否存在如果存在不再上传,需要将结果反馈给客户端.

如果不存在,新建字节输出流,将客户端传送的数据,写到服务器中.判断服务器文件的长度和客户端文件的长度是否一致.一致上传完毕.

客户端:

public class Client {

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

System.out.println("客户端启动");

Socket socket = new Socket("127.0.0.1", 5000);

InputStream ips = socket.getInputStream();

OutputStream ops = socket.getOutputStream();

// 1

ops.write("客户端请求上传文件".getBytes());

// 4

byte[] byt = new byte[1024];

int len = 0;

len = ips.read(byt);

String mess = new String(byt, 0, len);

System.out.println(mess);

// 获取上传文件

File file = getFile();

// 获取上传文件名

mess = file.getName();

System.out.println("文件名:" + mess);

// 5

ops.write(mess.getBytes());

// 7上传文件大小

ops.write(String.valueOf(file.length()).getBytes());

// 10

len = ips.read(byt);

mess = new String(byt, 0, len);

if ("文件存在".equals(mess)) {

System.out.println("文件存在,无需上传");

return;

}

// 11 上传

FileInputStream fis = new FileInputStream(file);

while ((len = fis.read(byt)) != -1) {

ops.write(byt, 0, len);

}

System.out.println("文件上传完毕");

len = ips.read(byt);

System.out.println("服务器说:" + new String(byt, 0, len));

ops.close();

socket.close();

}


private static File getFile() {

// 用户输入文件路径

Scanner sc = new Scanner(System.in);

while (true) {

System.out.println("请输入文件的路径名:");

String nextLine = sc.nextLine();

// 创建File

File file = new File(nextLine);

if (!file.exists()) {

System.out.println("文件不存在");

continue;

}

if (file.isDirectory()) {

System.out.println("不支持目录");

continue;

}

return file;

}

}

}

}

}

服务端:

public class Server {

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

System.out.println("服务端启动");

ServerSocket server = new ServerSocket(5000);

Socket client = server.accept();

InputStream ips = client.getInputStream();

OutputStream ops = client.getOutputStream();

// 2

byte[] byt = new byte[1024];

int len = -1;

len = ips.read(byt);

System.out.println(new String(byt, 0, len));

// 3

ops.write("接收请求,请上传".getBytes());

// 5

len = ips.read(byt);

String fileName = new String(byt, 0, len);

System.out.println("文件名:" + fileName);

// 根据文件名创建File对象,

File file = new File("d:\", fileName);

// 8获取文件长度

len = ips.read(byt);

long length = Long.parseLong(new String(byt, 0, len));

// 9判断文件是否存在

if (file.exists() && file.length() == length) {

System.out.println("文件已经存在");

ops.write("文件存在".getBytes());

return;

} else {

ops.write("文件不存在".getBytes());

}

// 12接收

FileOutputStream fos = new FileOutputStream(file);

while ((len = ips.read(byt)) != -1) {

fos.write(byt, 0, len);

fos.flush();

if (file.length() == length) {

System.out.println("文件上传完毕...");

ops.write("文件接收完毕".getBytes());

break;

}

}

fos.close();

client.close();

server.close();

}

}

注意: 服务器中的写出文件时,如果客户端socket没有关闭,那么读不到文件末尾,需要强制结束(判断文件长度是否一致)