Java 网络编程

1、概述

TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
步骤:

  1. 服务端程序,需要事先启动,等待客户端的连接
  2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端

JAVA中,提供了服务器类与客户端类

  • 客户端: java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建 立连接开始通信。
  • 服务端: java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端 的连接。

2、客户端 Socket

构造方法
public Socket(String host, int port):创建套接字对象并将其连接到指定主机上的指定端口号。如果指 定的host是null ,则相当于指定地址为回送地址。

成员方法:

  1. public InputStream getInputStream(): 返回此套接字的输入流。
    - 如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
    - 关闭生成的InputStream也将关闭相关的Socket
  2. public OutputStream getOutputStream() : 返回此套接字的输出流。
    - 如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道
    - 关闭生成的OutputStream也将关闭相关的Socket。
  3. public void close():关闭此套接字。
  4. public void shutdownOutput(): 禁用此套接字的输出流

3、服务器端 ServerSocket

ServerSocket 类:这个类实现了服务器套接字,该对象等待通过网络的请求

构造方法 :
public ServerSocket(int port):使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指 定的端口号上,参数port就是端口号。

成员方法:

  • 大多和客服端一样,但多了一个方法
  • public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法 会一直阻塞直到建立连接。

4、简单步骤

  1. 首先,创建客户端和服务器端
  2. 客户端创建Socket,根据指定服务器的IP地址创建Socket对象,创建的同时会自动向服务器端发起3次握手。如果相应,则建立通信路线,如果链接失败,抛出异常
  3. 当客户端创建Socket后,服务器端的ServerSocket调用accept方法,这个方法返回一个Socket对象,这样服务器端就与客服端连接了
  4. 客户端中的Socket对象,调用getOutputStream()方法获取OutputStream,然后OutputStream调用write()来写数据给服务器端
  5. 服务器端的Socket对象,调用 getInputStream方法获取 InputStream,调用read()读客户端的数据
    至此,客户端向服务器端发送数据成功!!!
    下面就是服务器端向客户端回写数据
  6. 服务器端 Socket对象,调用getOutputStream()方法获取OutputStream,write()回写数据给客服端
  7. 客户端 Socket对象,调用 getInputStream方法获取 InputStream,read()解析回写的数据
  8. 客户端与服务器端释放资源,断开连接

5、例子

服务器端:

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();
	}

}

先启动服务器,再启动客户端,进行两个控制台的查看
服务器端控制台:
在这里插入图片描述
客户端控制台:
在这里插入图片描述

6、阻塞状态

上面的例子可以看到,我们的读,都只是读一个,没有循环的,那么但read的bytes[]数组超过我们预设的时候,就要用到while循环来判断是否为-1了,但是,这有个很神奇的BUG
就把上面注释的,改成下面的,就只有服务器端有接受到消息,客户端没有接收到消息
在这里插入图片描述
至于为什么这样,是因为read和write有阻塞读不到-1,至于为什么我也不懂,都是非人话看不懂,那我就说怎么解决吧

1)解决1:加上socket.shutdownOutput();

在一个端口中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();

	}

}

2)在送数据时,加一个结尾标志,另一个端口检测这个标志

第一个方法,的确好用,但是这样,就只能对服务器发送一次对话而已,如果想要和服务器多次对话,每对一次都要连接一次,效率低下在一段话结尾加上\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();

	}

}

7、文件上传

和文字传送差不多吧,就复杂一点而已,在这基础上加点东西
然后服务器端是要一直开着的,因为你关了客户不就炸了吗。客户端可能有好几个,当上传大的东西,就会很慢,所以我们每一次接受到客户的请求,都要新创建一个线程。然后上传的名字也是要随机的。

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("文件上传完毕 ");

	}

}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值