1、网络模型

Java之网络编程-LMLPHP

2、网络通讯要素

IP地址:InetAddress类

  1. 本地回环地址:127.0.0.1,主机名:localhost
  2. IP地址的分类:
    Java之网络编程-LMLPHP

端口号:
3. 用于标识进程的逻辑地址,不同进程的标识;
4. 有效端口号:1 ~ 65535,其中0 ~ 1024为系统使用或者保留端口;

传输协议:

  1. UDP:传输控制协议
    1). 面向无连接;
    2). 每个数据报限制在64k内;
    3). 因无连接,所以不可靠,但速度快;

  2. TCP:
    1). 面向连接;
    2). 通过三次握手建立连接,是可靠协议;
    3). 必须建立连接,所以效率稍低;

3、基于UDP协议的网络传输

1、UDP发送端

  1. 建立UDP的DatagramSocket对象。它具备发送或者接收的功能;
  2. 将数据封装到数据包中。数据包对象为DatagramPacket
  3. 使用socket对象的send方法将数据包发送出去。
  4. 关闭资源。
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接收端

  1. 建立UDP的DatagramSocket对象。它具备发送或者接收的功能,并且接收端创建时一定要明确端口号,否则将无法接收到数据
  2. 接收数据,将接收到的数据存储到数据包中;接收数据,将接收到的数据存储到数据包中;
  3. 解析数据包,获取数据包的内容,包括发送端的IP,发送端的端口号,发送的数据;
  4. 关闭资源。
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客户端

  1. 建立TCP的客户端Socket对象。明确服务器端的地址和端口;
  2. 如果连接建立成功就可以获取Socket的IO流,客户端要做的就是获取Socket中的输出流将数据发送到服务器端;
  3. 通过Socket发送数据;
  4. 关闭资源。
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服务器端

  1. 创建服务器端ServerSocket对象。明确端口,监听一个端口;
  2. 服务器端获取连接过来的客户端对象,就可以和客户端进行通信了;
  3. 通过获取客户端的输入流对象,读取客户端发送的数据;
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案例——客户端和服务器端收发数据

  1. 客户端
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();
	}
}
  1. 服务器端
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. 客户端
    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. 服务器端
    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案例——上传文本文件

  1. 客户端
    通过调用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();
	}
}
  1. 服务器端
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、常见网络架构

  1. C/S:client/server
    特点:
    1)需要开发客户端和服务端;
    2)维护较麻烦;
    3)将一部分运算转移到客户端来完成,减轻服务器端的压力;

  2. B/S:browser/server
    特点:
    1)只需要开发服务器端,客户端使用系统已有的浏览器即可。
    2)维护很简单,只需要维护服务端;
    3)所有的运算都在服务器端完成;

10-03 19:23