Java之网络编程
1、网络模型
2、网络通讯要素
IP地址:InetAddress类
- 本地回环地址:127.0.0.1,主机名:localhost
- IP地址的分类:
端口号:
3. 用于标识进程的逻辑地址,不同进程的标识;
4. 有效端口号:1 ~ 65535,其中0 ~ 1024为系统使用或者保留端口;
传输协议:
-
UDP:传输控制协议
1). 面向无连接;
2). 每个数据报限制在64k内;
3). 因无连接,所以不可靠,但速度快; -
TCP:
1). 面向连接;
2). 通过三次握手建立连接,是可靠协议;
3). 必须建立连接,所以效率稍低;
3、基于UDP协议的网络传输
1、UDP发送端
- 建立UDP的DatagramSocket对象。它具备发送或者接收的功能;
- 将数据封装到数据包中。数据包对象为DatagramPacket。
- 使用socket对象的send方法将数据包发送出去。
- 关闭资源。
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* UDP发送端
*/
public class UdpSend {
public static void main(String[] args) throws Exception {
// 1. 建立UDP的socket,DatagramSocket对象。它具备发送或者接收的功能;
DatagramSocket dsSend = new DatagramSocket();
// 2. 将数据封装到数据包中。数据包对象为DatagramPacket。
String text = "Hello udp, I'm coming!";
byte []buf = text.getBytes();
DatagramPacket dpSend = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 10000);
// 3. 使用socket对象的send方法将数据包发送出去。
dsSend.send(dpSend);
// 4. 关闭资源。
dsSend.close();
}
}
2、UDP接收端
- 建立UDP的DatagramSocket对象。它具备发送或者接收的功能,并且接收端创建时一定要明确端口号,否则将无法接收到数据;
- 接收数据,将接收到的数据存储到数据包中;接收数据,将接收到的数据存储到数据包中;
- 解析数据包,获取数据包的内容,包括发送端的IP,发送端的端口号,发送的数据;
- 关闭资源。
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* UDP接收端
*/
public class UdpReceive {
public static void main(String[] args) throws Exception {
// 1. 建立UDP的socket,DatagramSocket对象。它具备发送或者接收的功能;
DatagramSocket dsRec = new DatagramSocket(10000);
// 2. 接收数据,将接收到的数据存储到数据包中;接收数据,将接收到的数据存储到数据包中;
byte [] buf = new byte[1024];
DatagramPacket dpRec = new DatagramPacket(buf, buf.length);
dsRec.receive(dpRec); //阻塞,直到接收到数据
// 3. 解析数据包,获取数据包的内容,包括发送端的IP,发送端的端口号,发送的数据;
String ip = dpRec.getAddress().getHostAddress();
int port = dpRec.getPort();
String text = new String(dpRec.getData(),0,dpRec.getLength());
System.out.println(ip+"--->"+port+"--->"+text);
// 4. 关闭资源。
dsRec.close();
}
}
3、UDP发送键盘录入数据
1)发送端
package udp协议网络编程2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* UDP发送端,发送的数据来自于键盘录入
* @author WangYifeng
*
* 2018年10月2日下午9:58:32
*/
public class UdpSend2 {
public static void main(String[] args) throws Exception {
System.out.println("UDP发送端。。。");
// 1. 建立UDP的socket,DatagramSocket对象。它具备发送或者接收的功能;
DatagramSocket dsSend = new DatagramSocket();
// 2. 将数据封装到数据包中。数据包对象为DatagramPacket。
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String text = null;
while((text=br.readLine())!=null) {
if(text.equals("over")) {
break;
}
byte []buf = text.getBytes();
DatagramPacket dpSend = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 10000);
// 3. 使用socket对象的send方法将数据包发送出去。
dsSend.send(dpSend);
}
// 4. 关闭资源。
br.close();
dsSend.close();
}
}
2)接收端
package udp协议网络编程2;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* UDP接收端
* @author WangYifeng
*
* 2018年10月2日下午10:21:12
*/
public class UdpReceive2 {
public static void main(String[] args) throws Exception {
System.out.println("UDP接收端。。。");
// 1. 建立UDP的socket,DatagramSocket对象。它具备发送或者接收的功能;
DatagramSocket dsRec = new DatagramSocket(10000);
while(true) {
// 2. 接收数据,将接收到的数据存储到数据包中;接收数据,将接收到的数据存储到数据包中;
byte [] buf = new byte[1024];
DatagramPacket dpRec = new DatagramPacket(buf, buf.length);
dsRec.receive(dpRec); //阻塞,直到接收到数据
// 3. 解析数据包,获取数据包的内容,包括发送端的IP,发送端的端口号,发送的数据;
String ip = dpRec.getAddress().getHostAddress();
int port = dpRec.getPort();
String text = new String(dpRec.getData(),0,dpRec.getLength());
System.out.println(ip+"--->"+port+"--->"+text);
}
// 4. 关闭资源。
// dsRec.close();
}
}
4、UDP案例——群聊程序(在一个文件中收发数据)
package udp案例_群聊;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* UDP实现群聊
* @author WangYifeng
*
* 2018年10月3日上午10:01:15
*/
public class UdpChatting {
public static void main(String[] args) throws IOException {
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket = new DatagramSocket(10000);
Send send = new Send(sendSocket);
Receive rece = new Receive(receiveSocket);
Thread t1 = new Thread(send);
Thread t2 = new Thread(rece);
t1.start();
t2.start();
}
}
//发送端线程
class Send implements Runnable {
private DatagramSocket ds;
public Send(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String text = null;
while((text=br.readLine())!=null) {
byte []buf = text.getBytes();
DatagramPacket dpSend = new DatagramPacket(buf, buf.length, InetAddress.getLocalHost(), 10000);
ds.send(dpSend);
if(text.equals("over")) {
break;
}
}
br.close();
ds.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Receive implements Runnable {
private DatagramSocket ds;
public Receive(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
try {
while(true) {
byte [] buf = new byte[1024];
DatagramPacket dpRec = new DatagramPacket(buf, buf.length);
ds.receive(dpRec);
String ip = dpRec.getAddress().getHostAddress();
int port = dpRec.getPort();
String text = new String(dpRec.getData(),0,dpRec.getLength());
System.out.println(ip+"--->"+port+"--->"+text);
if(text.equals("over")) {
System.out.println("拜拜----离开聊天室!");
break;
}
}
} catch(IOException e) {
e.printStackTrace();
}
}
}
4、基于TCP协议的网络传输
1、TCP客户端
- 建立TCP的客户端Socket对象。明确服务器端的地址和端口;
- 如果连接建立成功就可以获取Socket的IO流,客户端要做的就是获取Socket中的输出流将数据发送到服务器端;
- 通过Socket发送数据;
- 关闭资源。
package tcp协议网络编程;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws IOException {
// 1. 建立TCP的客户端Socket。明确服务器端的地址和端口;
Socket client = new Socket("192.168.2.102",10000);
// 2. 如果连接建立成功就可以获取Socket的IO流,客户端要做的就是获取Socket中的输出流将数据发送到服务器端;
OutputStream out = client.getOutputStream();
// 3. 通过Socket的IO流发送数据;
out.write("Hello tcp, I'm comming!".getBytes());
// 4. 关闭资源。
client.close();
}
}
2、TCP服务器端
- 创建服务器端ServerSocket对象。明确端口,监听一个端口;
- 服务器端获取连接过来的客户端对象,就可以和客户端进行通信了;
- 通过获取客户端的输入流对象,读取客户端发送的数据;
package tcp协议网络编程;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
// 1. 创建服务器端ServerSocket对象。明确端口,监听一个端口;
ServerSocket ss = new ServerSocket(10000);
// 2. 服务器端获取连接过来的客户端对象,就可以和客户端进行通信了;
Socket s = ss.accept(); //阻塞,等待客户端连接
InetAddress ip = s.getInetAddress();
System.out.println(ip.getHostAddress()+"....connected");
// 3. 通过获取客户端的输入流对象,读取客户端发送的数据;
InputStream in = s.getInputStream();
byte []buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
// 4. 关闭资源。
s.close();
ss.close(); //服务器端一般不关闭;
}
}
3、TCP案例——客户端和服务器端收发数据
- 客户端
package tcp协议网络编程2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class TcpClient2 {
public static void main(String[] args) throws IOException {
System.out.println("客户端。。。");
// 1. 建立TCP的客户端Socket。明确服务器端的地址和端口;
Socket client = new Socket("192.168.2.102",10000);
// 2. 如果连接建立成功就可以获取Socket的IO流,客户端要做的就是获取Socket中的输出流将数据发送到服务器端;
// 3. 通过Socket的输出流发送数据;
OutputStream out = client.getOutputStream();
out.write("Hello tcp, I'm comming!".getBytes());
// 4. 通过Socket的输入流读取数据;
InputStream in = client.getInputStream();
byte buf[] = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
// 5. 关闭资源。
client.close();
}
}
- 服务器端
package tcp协议网络编程2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer2 {
public static void main(String[] args) throws IOException {
System.out.println("服务器端。。。。");
// 1. 创建服务器端ServerSocket对象。明确端口,监听一个端口;
ServerSocket ss = new ServerSocket(10000);
while(true) {
// 2. 服务器端获取连接过来的客户端对象,就可以和客户端进行通信了;
Socket s = ss.accept(); //阻塞,等待客户端连接
InetAddress ip = s.getInetAddress();
System.out.println(ip.getHostAddress()+"....connected");
// 3. 通过获取客户端的输入流对象,读取客户端发送的数据;
InputStream in = s.getInputStream();
byte []buf = new byte[1024];
int len = in.read(buf);
String text = new String(buf,0,len);
System.out.println(text);
// 4. 通过获取客户端的输出流对象,向客户端发送回馈数据;
OutputStream out = s.getOutputStream();
out.write("客户端,我已收到数据。。。。".getBytes());
// 5. 关闭资源。
s.close();
}
// ss.close(); //服务器端一般不关闭;
}
}
4、TCP案例——频繁的客户端和服务器端通信
大写转换服务器
需求:客户端通过键盘录入数据发送到服务器端,服务器端将接收到的数据显示,并将数据转换成大写后返回客户端。当客户端发送over时,大写转换结束。
分析:
- 客户端
1)创建Socket对象,明确地址和端口;
2)获取键盘录入的数据;
3)获取Socket输出流,发送数据;
4)获取Socket输入流,读取服务器反馈的数据,并显示;
5)频繁的读写操作;
6)关闭资源;
package tcp案例_频繁的大写转换;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TcpClient {
public static void main(String[] args) throws IOException {
System.out.println("客户端。。。。");
// 1)创建Socket对象,明确地址和端口;
Socket s = new Socket("192.168.2.102",10000);
// 2)获取键盘录入的数据;
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
// 3)获取Socket输出流,发送数据;
// OutputStream out = s.getOutputStream();
//因为操作的都是字符,为了便于操作,可以使用字符缓冲流进行转换,提高效率
// BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(out));//可以使用打印流
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
// 4)获取Socket输入流,读取服务器反馈的数据,并显示;
// InputStream in = s.getInputStream();
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 5)频繁的读写操作;
String line = null;
while((line=bufr.readLine())!=null) {
//发送键盘录入数据到服务器端
out.println(line);
//如果发送的是over就结束
if(line.equals("over")) {
break;
}
//接收服务器端返回的数据
String upperText = bufIn.readLine();
System.out.println(upperText);
}
// 6)关闭资源;
s.close();
}
}
- 服务器端
1)创建ServerSocket,明确端口;
2)获取客户端Socket对象,获取Socket输入流,读取客户端发送过来的数据;
3)获取Socket输出流,将转换后的大写数据返回客户端;
4)频繁的读写操作;
5)关闭客户端;
package tcp案例_频繁的大写转换;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer {
public static void main(String[] args) throws IOException {
System.out.println("服务器端。。。。");
// 1)创建ServerSocket,明确端口;
ServerSocket ss = new ServerSocket(10000);
while(true) {
// 2)获取客户端Socket对象,获取Socket输入流,读取客户端发送过来的数据;
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress()+".....connected");
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
// 3)获取Socket输出流,将转换后的大写数据返回客户端;
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
// 4)频繁的读写操作;
String line = null;
while((line=bufIn.readLine())!=null) {
if(line.equals("over")) {
break;
}
System.out.println(line);
//将客户端发送的字母数据转成大写返回给客户端
out.println(line.toUpperCase());
}
// 5)关闭客户端;
s.close();
}
}
}
5、TCP案例——上传文本文件
- 客户端
通过调用Socket对象的**shutdownOutput()**方法给服务器端发送一个传输结束标记,服务器端读此标记时读到null结束循环,给客户端返回上传成功信息。
package tcp.tcp案例_上传文本文件;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TcpClient_FileUpload {
public static void main(String[] args) throws IOException {
System.out.println("客户端上传文件。。。。");
// 1)创建Socket对象,明确地址和端口;
Socket s = new Socket("192.168.2.102",10000);
// 2)获取文本文件的数据;
BufferedReader bufr = new BufferedReader(new FileReader("src\\file\\client.txt"));
// 3)通过流操作,发送数据;
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
// 4)频繁的读写操作;
String line = null;
while((line=bufr.readLine())!=null) {
//发送文本文件数据到服务器端
out.println(line);
}
// 5)给服务器端发送一个发送结束标记
s.shutdownOutput();
// 6)获取Socket输入流,读取服务器返回的上传成功信息;
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String upperText = bufIn.readLine();
System.out.println(upperText);
// 7)关闭资源;
bufr.close();
s.close();
}
}
- 服务器端
package tcp.tcp案例_上传文本文件;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer_FileUpload {
public static void main(String[] args) throws IOException {
System.out.println("服务器端。。。。上传文件");
//1)创建ServerSocket,明确端口;
ServerSocket ss = new ServerSocket(10000);
while(true) {
//2)获取客户端Socket对象,获取Socket输入流,读取客户端发送过来的数据;
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress()+".....connected");
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
//3)将获取的客户端数据存入文件
PrintWriter out = new PrintWriter(new FileWriter("src\\file\\server.txt"),true);
//4)频繁的读写操作;
String line = null;
while((line=bufIn.readLine())!=null) {
out.println(line);
}
//5)向客户端返回上传成功信息
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
pw.println("上传成功!");
//6)关闭客户端;
s.close();
}
}
}
http1.0和http1.1的区别:
http1.0:一次连接一次请求;
http1.1:一次连接多次请求;
5、常见网络架构
-
C/S:client/server
特点:
1)需要开发客户端和服务端;
2)维护较麻烦;
3)将一部分运算转移到客户端来完成,减轻服务器端的压力; -
B/S:browser/server
特点:
1)只需要开发服务器端,客户端使用系统已有的浏览器即可。
2)维护很简单,只需要维护服务端;
3)所有的运算都在服务器端完成;