网络编程
1 网络编程的基础知识
1.网络模型 5层模型和七层模型
2.网络编程的三要素 ip 端口 协议
3.Tcp连接过程 三次握手和四次挥手
1 网络模型
https://blog.csdn.net/qq_22238021/article/details/80279001
Tcp连接过程 三次握手和四次挥手
https://baijiahao.baidu.com/s?id=1596016296668694374&wfr=spider&for=pc
2 网络编程的三要素
1.IP
2.端口
3.协议
eg: 找个泰国人说话
ip类似于一个人的身份证
端口类似于门牌号
协议类似两个人说话的语言
3 IP地址:
网络中针对计算机的唯一表示,类似于一个人的身份证,每个计算机都有自己的ip地址
计算机中是用点分十进制来表示的: 192.168.5.76
ip的存储是使用二进制存储
11000000.10101000.00000101.01001100
分为网络区段号和主机段号
IP地址分类
A类地址: 第一段为网络区段号,后面三段为主机段号 256 * 256 * 256 = 16777216 国家 政府 军方
B类地址: 前两段为网络区段号,后面两段为主机段号 256 * 256 = 65536 学校 公司
C类地址: 前三段为网络区段号,后面一段为主机段号 256 尚学堂 网吧
特殊地址:
127.0.0.1 : 本机地址/回环地址 == localhost == 192.168.5.76
192.168.x.x : 表示私有地址 (这个地址不能够在互联网上使用,只能够在局域网中使用)
x.x.x.25: 表示广播地址
掌握2个命令:
ipconfig: 查看本机网络配置
ping ip地址: 测试是否能够和目标ip进行正常的发送和接收数据包
4 端口: 同一台计算机进程的标识
端口的范围: 0~65535是我们可以使用端口的范围
0~1024一般是系统进程端口,不建议使用
1024~65535可以是我们使用的端口
建议我们不要和系统进程或者和已经打开的程序端口重复,否则会出现端口被占用的问题
5 协议: 不同计算机通信的规则
HTTP: 超文本传输协议
FTP: 文件传输协议
TCP: 传输控制协议
UDP: 用户数据报协议
TCP协议的特点:
1.面向连接的协议
2.数据传输量没有限制
3.数据安全,可靠
4.速度慢
eg: 文件上传 文件下载
UDP协议:
1.面向无连接
2.数据传输量有限制,最好不要超过64K
3.速度快
4.数据不安全,很容丢失数据
5.传输数据之前必须要打包处理
eg: 发短信 群聊
Java是面向对象语言: Ip提供了对应的Java类 InetAddress
public class NetDemo01 {
public static void main(String[] args) throws UnknownHostException {
// 创建IP对象
// static InetAddress getByName(String host)
// InetAddress ip = InetAddress.getByAddress(new byte[] {(byte) 192,(byte) 168,5,76} );
InetAddress ip = InetAddress.getByName("192.168.5.76");
// InetAddress ip = InetAddress.getByName("www.baidu.com");
System.out.println(ip);
String hostAddress = ip.getHostAddress();
System.out.println(hostAddress);
String hostName = ip.getHostName();
System.out.println(hostName);
InetAddress loopbackAddress = InetAddress.getLoopbackAddress();
System.out.println(loopbackAddress);
}
}
2 UDP协议
Socket成为套接字
网络编程的本质就是 Socket编程 (IO流 + 多线程 + Socket)
服务器端和客户端都会有一个对应的Socket对象进行数据传输和交互
UDP协议特点:
1.面向无连接
2.数据传输量有限制,最好不要超过64K
3.速度快
4.数据不安全,很容丢失数据
5.传输数据之前必须要打包处理
eg: 发短信 群聊
UDP协议的原理就类似于寄快递
1 UDP协议实现数据的发送和接收
UDP编程发送端 如下代码
public class ClientDemo {
public static void main(String[] args) throws IOException {
System.out.println("发送端启动了...");
// 创建IP对象
InetAddress clientIp = InetAddress.getByName("192.168.5.76");
int clientPort = 10086;
// 1.创建Socket对象 DataGramSocket
// 这里的ip和端口指的是发送端的ip和端口
DatagramSocket ds = new DatagramSocket(clientPort, clientIp);
// 2.创建数据
String data = "HelloWorld";
// 3.对数据进行打包
byte[] buf = data.getBytes();
int length = buf.length;
InetAddress serverIp = InetAddress.getByName("192.168.5.76");
int serverPort = 12306;
DatagramPacket dp = new DatagramPacket(buf, length, serverIp, serverPort);
// 4.调用socket的send方法发送数据包
ds.send(dp);
// 5.释放资源
ds.close();
}
}
UDP编程接受端 如下代码
public class ServerDemo {
public static void main(String[] args) throws Exception {
System.out.println("接收端启动了...");
// 1.创建接收端Socket对象
DatagramSocket ds = new DatagramSocket(12306, InetAddress.getByName("192.168.5.76"));
// 2.创建一个空的包裹用于存储拆包的数据
byte[] buf = new byte[100];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
// 3.调用Socket对象的receive方法来接受传递过来的数据包到我们创建好的空包中
// 这还是一个阻塞方法,会阻塞当前正在执行的线程,等待包裹传递过来,一旦接受到包裹,程序继续执行
ds.receive(dp);
// 4.解析包裹
byte[] data = dp.getData();
String clientIp = dp.getAddress().getHostAddress();
int clientPort = dp.getPort();
int length = dp.getLength();
String result = new String(data, 0, length);
System.out.println("来自于" + clientIp + ",端口号为: " + clientPort + "的数据:" + result);
// 5.释放资源
ds.close();
}
}
2 UDP协议实现群聊功能
发送端
/*
* 网络编程的本质就是 Socket编程 (IO流 + 多线程 + Socket)
* Socket成为套接字
* 服务器端和客户端都会有一个对应的Socket对象进行数据传输和交互
*
* UDP协议的原理就类似于寄快递
*/
public class ClientDemo {
public static void main(String[] args) throws IOException {
System.out.println("老肖聊天室启动了...");
Scanner input = new Scanner(System.in);
DatagramSocket ds = new DatagramSocket(12306);
while (true) {
System.out.print("请输入:");
String line = input.nextLine();
if (line.equals("byebye")) {
break;
}
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.5.76"), 9999);
ds.send(dp);
}
ds.close();
input.close();
}
}
接收端
public class ServerDemo {
public static void main(String[] args) throws Exception {
System.out.println("聊天服务器启动了...");
// 默认使用本机作为服务器
DatagramSocket ds = new DatagramSocket(9999);
while (true) {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
byte[] data = dp.getData();
int length = dp.getLength();
String ip = dp.getAddress().getHostAddress();
String name = "";
Properties prop = PropertiesUtil.prop;
Set<String> keys = prop.stringPropertyNames();
for (String ipKey : keys) {
if (ipKey.equals(ip)) {
name = prop.getProperty(ipKey);
}
}
String result = new String(data, 0, length);
if (ip.equals("192.168.5.76")) {
System.err.println(name + "说:" + result);
} else {
System.out.println(name + "说:" + result);
}
}
}
}
3 tcp协议
tcp协议特点
1.面向连接的协议,必须先建立连接才能通信,三次握手建立连接
2.数据传输量没有限制
3.数据不会丢失,安全可靠
4.效率低
1 TCP协议发送数据和接收数据
TCP协议发送数据
public class ClientDemo {
public static void main(String[] args) throws IOException {
System.out.println("客户端启动了...");
// 1.创建Socket对象
Socket s = new Socket("192.168.5.76", 10086);
// 2.通过Socket对象和服务器对应的Socket对象进行通信
OutputStream os = s.getOutputStream();
// 3.通过输出流输出数据
String data = "HelloWorld";
os.write(data.getBytes());
// 4.释放资源
s.close();
}
}
tcp协议接收数据
public class ServerDemo {
public static void main(String[] args) throws Exception {
System.out.println("服务器启动了....");
// 1.创建服务器端Socket
ServerSocket ss = new ServerSocket(10086);
// 2.监听客户端的连接
// 该方法是一个阻塞方法,监听客户端的连接,一旦连接成功会返回与客户端匹配的Socket对象
Socket s = ss.accept();
// 3.通过返回的对应客户端的Socket对象来获取输入流
InputStream is = s.getInputStream();
// 4.通过io流读取数据
byte[] bys = new byte[100];
int len = is.read(bys);
String data = new String(bys, 0, len);
// 5.显示数据
System.out.println("客户端传递过来的消息是: " + data);
// 6.释放资源
s.close();
ss.close();
}
}
2 tcp协议实现文件上传
发送端
/*
* 将文件读取出来写入到服务器,同时接收服务器的响应字符串
*/
public class FileUploadClient {
public static void main(String[] args) throws Exception {
Socket s = new Socket("192.168.5.76", 10086);
System.out.println("客户端连接成功!!!");
BufferedReader br = new BufferedReader(new FileReader("client/NetDemo01.java"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// 半关闭的Socket
s.shutdownOutput();
System.out.println("==============>>接收服务器响应");
InputStream is = s.getInputStream();
byte[] bys = new byte[100];
int len = is.read(bys);
System.out.println("服务器响应:" + new String(bys, 0, len));
br.close();
s.close();
}
}
接收端
/*
* 文件上传服务器
* tomcat
* MIME类型
*/
public class FileUploadServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(10086);
System.out.println("文件服务器启动了...");
// 这种写法存在问题: 第一个文件没有上传完毕,那么后面的文件都不能够上传,不能够同时上传
while (true) {
Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bw = new BufferedWriter(new FileWriter("server/server.txt"));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
// 关闭输入流 (半关闭的Socket)
s.shutdownInput();
System.out.println("==============>>给出客户端响应");
/*
* 数据源: 内存中的 文件上传成功!!!
* 目的地: 网络流中
* 交通工具: 字节流
*/
OutputStream os = s.getOutputStream();
os.write("文件上传成功".getBytes());
os.close();
bw.close();
}
}
}
3 多线程+TCP实现多个视频同时上传
发送端
*
* 文件上传客户端
*/
public class FileUploadClient1 {
public static void main(String[] args) throws Exception {
Socket s = new Socket("192.168.5.76", 10086);
OutputStream os = s.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("video/01.进程和线程.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(os);
int len = 0;
byte[] bys = new byte[1024];
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
s.shutdownOutput();
// 读取服务器响应
InputStream is = s.getInputStream();
len = is.read(bys);
System.out.println(new String(bys, 0, len));
bis.close();
s.close();
}
}
接收端
/*
* 文件上传客户端
*/
public class FileUploadClient2 {
public static void main(String[] args) throws Exception {
Socket s = new Socket("192.168.5.76", 10086);
OutputStream os = s.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("video/02.线程的开启方式一_继承Thread方式开启线程.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(os);
int len = 0;
byte[] bys = new byte[1024];
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
bos.flush();
}
s.shutdownOutput();
// 读取服务器响应
InputStream is = s.getInputStream();
len = is.read(bys);
System.out.println(new String(bys, 0, len));
bis.close();
s.close();
}
}
4 tcp协议在网络中传输对象
将学生类写入到网络中
public class ClientDemo {
public static void main(String[] args) throws Exception {
Socket s = new Socket("192.168.1.112", 7878);
OutputStream os = s.getOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new Student("隔壁老王", 30));
ServerSocket ss = new ServerSocket(7878);
Socket s2 = ss.accept();
InputStream is = s2.getInputStream();
ObjectInputStream ois = new ObjectInputStream(is);
Object obj = ois.readObject();
if (obj instanceof Student) {
Student stu = (Student) obj;
System.out.println(stu.getName() + ":" + stu.getAge());
}
ss.close();
}
}
//注意要实现Serializabl
public class Student implements Serializable {
private static final long serialVersionUID = -6552531012158918514L;
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
5 通过键盘输入写入到文件
发送端
// 客户端通过键盘输入,服务器端写入到文件
public class ClientDemo {
public static void main(String[] args) throws Exception {
// 客户端通过键盘输入
Socket s = new Socket(Global.IP_SERVER, Global.PORT_SERVER);
System.out.println("客户端成功连接上了服务器...");
/*
* 数据源: 键盘输入
* 目的地: 网络中
* 交通工具:
* System.in
* s.getOutputStream
*/
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
s.close();
}
}
接收端
// 客户端通过键盘输入,服务器端写入到文件
public class ServerDemo {
public static void main(String[] args) throws Exception {
// 服务器端写入到文件
ServerSocket ss = new ServerSocket(Global.PORT_SERVER);
System.out.println("服务器端启动成功了...");
Socket s = ss.accept();
InputStream is = s.getInputStream();
/*
* 数据源: 网络中
* 目的地: 文件中
* 交通工具:
* s.getInputStream()
* BufferedWriter
*/
BufferedReader br = new BufferedReader(new InputStreamReader(is));
BufferedWriter bw = new BufferedWriter(new FileWriter("server/server.txt"));
String line = null;
while ((line = br.readLine()) != null) {
bw.write(line);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
s.close();
ss.close();
}
}
6 properties配置文件中产生中文乱码的问题
public class PropertiesUtil {
public static String default_properties = "config/ipconfig.properties";
public static Properties prop;
static {
prop = new Properties();
try {
InputStream is = new BufferedInputStream(new FileInputStream(default_properties));
//解决读取properties文件中产生中文乱码的问题
BufferedReader bf = new BufferedReader(new InputStreamReader(is,"UTF-8"));
prop.load(bf);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getProperty(String key) {
return prop.getProperty(key);
}
public static String getProperty(String key, String defaultValue) {
String value = prop.getProperty(key);
if (value == null)
return defaultValue;
return value;
}
public static boolean getBooleanProperty(String name, boolean defaultValue) {
String value = prop.getProperty(name);
if (value == null)
return defaultValue;
return (new Boolean(value)).booleanValue();
}
public static int getIntProperty(String name) {
return getIntProperty(name, 0);
}
public static int getIntProperty(String name, int defaultValue) {
String value = prop.getProperty(name);
if (value == null)
return defaultValue;
return (new Integer(value)).intValue();
}
public static void main(String[] args) {
Set<String> keys = PropertiesUtil.prop.stringPropertyNames();
for (String key : keys) {
String value = prop.getProperty(key);
System.out.println(key + "=" + value);
}
}
}
4 http协议
该类用于处理浏览器发送过来的http请求
请求协议的格式: http://ip:端口/路径1/路径2/…/路径n/web资源 (html css js xml json png jpg)
例如:http://localhost/admin/admin.html
浏览器默认端口是80端口,如果浏览器地址没有写明端口,默认请求的就是服务器的80端口
HTTP协议的格式
请求协议
请求首行 GET /helloworld/index.jsp HTTP/1.1
请求头信息
空行
请求体
例如:
GET /helloworld/index.jsp HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9;q=0.8 MIME类型
get没有请求体
响应协议
响应首行 HTTP/1.1 200 OK
响应头信息
空行
响应体
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=UTF-8
Content-Length: 367
Set-Cookie: JSESSIONID=C97E2B4C55553EAB46079A4F263437B6; Path=/helloworld
Date: Wed, 16 Sep 2017 03:15:43 GMT
该线程的任务:
1.解析请求参数
2.将浏览器请求的web资源返回给浏览器
public class HandlerRequestThread implements Runnable {
private Socket s;
public HandlerRequestThread() {
super();
}
public HandlerRequestThread(Socket s) {
super();
this.s = s;
}
@Override
public void run() {
PrintWriter pw = null;
OutputStream os = null;
String webServerRoot = "";
try {
InputStream is = s.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null && line.length() > 0) {
sb.append(line);
sb.append("\r\n");
}
System.out.println(sb.toString());
String[] messages = sb.toString().split(" ");
// 给出浏览器响应
webServerRoot = "D:/WebRoot";
os = s.getOutputStream();
pw = new PrintWriter(new OutputStreamWriter(os));
// 给浏览器写响应首行
pw.println("HTTP/1.1 200 OK");
pw.println("Content-Type: */*;charset=UTF-8");
// 给浏览器写空行
pw.println();
// 给浏览器写响应体
pw.flush();
if (messages.length > 2) {
File f = new File(webServerRoot + messages[1]);
FileInputStream fis = new FileInputStream(f);
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
os.write(bys, 0, len);
os.flush();
}
fis.close();
}
} catch (FileNotFoundException e) {
File errorFile = new File(webServerRoot, "error.html");
try {
FileInputStream fis = new FileInputStream(errorFile);
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
os.write(bys, 0, len);
os.flush();
}
fis.close();
} catch (Exception e2) {
e2.printStackTrace();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}