1.概述
Java网络编程是利用Java语言进行网络通信应用程序开发的过程。它主要基于TCP/IP协议栈,支持Socket编程,包括流式Socket(TCP)和数据报Socket(UDP)。通过Socket,程序可以创建服务器端或客户端,实现网络上的数据传输与交互。Java的网络编程接口简洁且功能强大,提供了InetAddress类用于处理IP地址,ServerSocket和Socket类分别用于服务器和客户端的连接建立,以及InputStream和OutputStream类来读写数据,还有通过多线程机制可以实现高效地处理并发连接。
2.网络通信协议
要使计算机连成的网络能够互通信息,需要对数据传输速率、传输代码、代码结构、传输控制步骤、出错控制等制定一组标准,这一组共同遵守的通信标准就是网络通信协议,不同的计算机之间必须使用相同的通讯协议才能进行通信。
七层模型,也称为OSI(Open System Interconnection)参考模型,是国际标准化组织(ISO)制定的一个用于计算机或通讯系统间互联的标准体系。它是一个七层的、抽象的模型体,不仅包括一系列抽象的术语或概念,也包括具体的协议。
- 应用层
应用程序之间如何相互传递报文,比如HTTP协议,FTP协议
- 传输层
传输层的作用是为两台主机之间的"应用进程"提供端到端的逻辑通信,比如TCP协议
- 网络层互联层
网络层互联层提供了主机到主机的通信,将传输层产生的数据包封装成分组数据包发送到目标主机,并提供路由选择能力. IP协议是网络层的主要协议,TCP和UDP都是用IP协议作为网络层协议.这一层的主要作用是给包加上源地址和目标地址,将数据包传送到目标地址.
- 网络访问层
网络访问层也可以称为网络接口层,以太网,WIFI,蓝牙就是工作在这一层,网络访问层提供了主机连接到物理网络需要的硬件和相关协议
3.网络协议(TCP)
-
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
使用TCP协议实现通信,需要使用流式套接字。即客户端采用socket,而服务器端采用ServerSocket来完成通信的方式
此时实现服务器端与客户端通信的思路:
其中,处理服务器端通信的ServerSocket类,其常见构造如下:
构造方法 | 说明 |
---|---|
ServerSocket() | 创建一个ServerSocket对象 |
ServerSocket(int port) | 创建一个ServerSocket对象,并绑定到指定端口 |
ServerSocket常用方法如下:
常用方法 | 说明 |
---|---|
Socket accept() | 侦听并接收到此ServerSocket的连接,此方法在连接传入之前一直阻塞 |
void close() | 使服务器释放占用的资源,并断开所有与客户端的连接 |
InetAddress getInetAddress() | 返回当前服务器绑定的IP地址信息 |
处理客户端通信的Socket类: |
Socket的构造共有9种,这里介绍2种常用的构造方法:
构造方法 | 说明 |
---|---|
Socket(String host, int port) | 向host主机的port端口发起连接请求 |
Socket(String host,int port,InetAddress localAddr,int localPort) | 向host主机的port端口发起连接请求,发起请求的计算机为localAddr,端口为localPort |
注意:InetAddress类表示互联网协议地址,包含IP地址,实际上就是java对IP地址的封装。
Socket的常用方法:
常用方法 | 说明 |
---|---|
InetAddress getInetAddress() | 返回与当前Socket对象关联的InetAddress对象 |
void shutdownInput() | 此套接字的输入流置于“流的末尾” |
void shutdownOutput() | 禁用此套接字的输出流 |
InputStream getInputStream() | 返回当前Socket对象关联的InputStream对象,它是服务器端向客户端发送回来的数据流. |
OutputStream getOutputStream() | 返回当前Socket 对象关联的OutputStream对象,它是客户端想服务器端发送的数据流 |
void close() | 关闭该socket建立的连接 |
3.简单的TCP网络程序
- 【服务端】启动,创建ServerSocket对象,等待连接。
- 【客户端】启动,创建Socket对象,请求连接。
- 【服务端】接收连接,调用accept方法,并返回一个Socket对象。
- 【客户端】Socket对象,获取OutputStream,向服务端写出数据。
- 【服务端】Scoket对象,获取InputStream,读取客户端发送的数据。
到此,客户端向服务端发送数据成功。
自此,服务端向客户端回写数据。
- 【服务端】Socket对象,获取OutputStream,向客户端回写数据。
- 【客户端】Scoket对象,获取InputStream,解析回写数据。
- 【客户端】释放资源,断开连接。
运用字节缓冲流让客户端和服务器各传一句话:
服务端:
public class Server {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
serverSocket = new ServerSocket(8888);//创建一个端口号为“8888”的ServerSocket对象
System.out.println("服务器启动!!!");
//运用serverSocket.accept()等待客户端连接,然后再把连接上的客户端的信息传入socke中
socket = serverSocket.accept();
System.out.println("客户端已连接!!!");
//有客户端连接上之后先读取客户端发来的消息
//运用字节缓冲流
byte[] bytes = new byte[1024];
bufferedInputStream = new BufferedInputStream(socket.getInputStream());
int len = bufferedInputStream.read(bytes);
String str = new String(bytes, 0, len);
System.out.println("客户端说:" + str);
//读取后运用输出流给客户端发送消息
bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
bufferedOutputStream.write("客户端,大样".getBytes());
//消息发送之后进行刷新
bufferedOutputStream.flush();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
bufferedOutputStream.close();
bufferedInputStream.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
客户端:
public class Client {
public static void main(String[] args) {
Socket socket=null;
BufferedInputStream bufferedInputStream=null;
BufferedOutputStream bufferedOutputStream=null;
try {
//先连接服务器,IP地址"localhost"即为本机,“8888”为端口号
socket=new Socket("localhost",8888);
//连接上服务器直接向服务器发送消息
bufferedOutputStream=new BufferedOutputStream(socket.getOutputStream());
bufferedOutputStream.write("服务器,小样".getBytes());
bufferedOutputStream.flush();
//接收服务端发送来的消息
System.out.print("服务器说:");
bufferedInputStream=new BufferedInputStream(socket.getInputStream());
int len;
byte[] bytes=new byte[1024];
String str="";
int len = bufferedInputStream.read(bytes);
String str = new String(bytes, 0, len);
System.out.println(str);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
bufferedOutputStream.close();
bufferedInputStream.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
运行结果:
服务器:
客户端:
运用字符缓冲流让客户端和服务器一直聊天:
服务端:
public class Server {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
BufferedReader br = null;
BufferedWriter bw = null;
Scanner input = new Scanner(System.in);
try {
serverSocket = new ServerSocket(8888);
System.out.println("服务器已启动!!!");
socket = serverSocket.accept();
System.out.println("客户端已连接!!!");
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while (true) {
String str = br.readLine();
System.out.println("客户端说:" + str);
bw.write(input.next());
bw.newLine();
bw.flush();
if (str.equals("666")) {
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
bw.close();
br.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
客户端:
public class Client {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
Socket socket = null;
BufferedWriter bw = null;
BufferedReader br=null;
try {
socket = new Socket("localhost", 8888);
System.out.println("已成功连接服务器!!!");
System.out.print("向服务器发送消息:");
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str;
while (true) {
bw.write(input.next());
bw.newLine();
bw.flush();
str=br.readLine();
System.out.println("服务器说:"+str);
if(str.equals("666")){
break;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
bw.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
服务端:
客户端: