网络模型
OSI参考模型
TCP/IP参考模型
网络通讯要素
1.IP地址---InetAddress
找到对方,网络中设备的标识
因为IP地址不易记忆,可以用主机名代替
本地回环地址:127.0.0.1 主机名:localhost
默认IP地址
127.0.0.1 安装网卡就会有,可以ping此IP地址测试网卡
192.168.*.* 为常用的保留地址段 用在局域网中
//192.168.1.0 代表一个网络段
//192.168.1.255 代表这个网段的广播地址
2.端口号
用于标识进程的逻辑地址,
可以对不同进程进行标识,以便区分
有效端口:0~65535,其中0~1024为系统使用或保留端口
数据要发送到对方指定的应用程序上,
为了表示这些应用程序,就给这些
网络应用程序都用数据进行了标识。
0--65535端口范围
0--1024被系统保留
WEB服务器 80 默认
Tomcat服务器 8080 默认
MySQL 3306 默认
如果默认端口被其他应用程序占用,就会修改端口号
3.传输协议
通信规则。也称为协议。
国际组织定义了通用协议TCP
另外一个常用的:UDP
不同主机之间通信,也可以使用其他协议进行通信。
比如:微软公司的
UDP
将数据及源和目的三项封装成数据包,不要建立连接
(不判断接收方是否存在,存在,则接收数据对方若不存在,则丢弃数据。是不可靠的协议)
每个数据报的大小限制在64K内
因无连接,是不可靠协议
不需要建立连接,速度快
---速度第一,适用于数据可以适当的丢失(视频、聊天、桌面共享)
(对讲机---不知道对方是否在,也在进行数据传输)
TCP
建立连接,形成传输数据的通道
(需要先确定对方存在并与对方先握手,再传输数据。是可靠的协议)
在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低
握手:对方在?-->对方回复-->回复对方-----完成握手,开始建立TCP数据传输通道,传输数据。
---保证数据不丢失,适用于数据完整(下载文件)。消耗资源,速度稍慢,但可以大数据量传输。
(打电话---首先需要电话通了(对方在),才进行通讯)
Socket
网络编程就是Socket编程
(JSP、Servlet--JavaEE框架部分的一些组件技术)
Socket就是为网络服务提供的一种机制
通信的两端都有Socket
网络通信其实就是Socket间的通信
数据在两个Socket间通过IO传输
UDP传输
DatagramSocket与Datagrampacket
建立发送端,接收端
建立数据包
调用Socket的发送接收方法
关闭Socket
发送端与接收端是两个独立的运行程序
UDP的Socket服务的建立
java.net.*
DatagramSocket
此类表示用来发送和接收数据报包的套接字
既能发送又能接收
void receive(DatagramPacket p)
从此套接字接收数据报包
void send(DatagramPacket p)
从此套接字发送数据报包
DatagramPacket
此类表示数据报包
数据报包用来实现无连接包投递服务
构造方法
DatagramPacket(byte[] buf,int length)
构造DatagramPacket,用来接收长度为length的数据包
DatagramPacket(byte[] buf,int length,InetAddress,int port)
构造数据包,用来将长度为length的包发送到指定主机的指定端口号
TCP传输
Socket和ServerSocket
建立客户端和服务端
建立连接后,通过Socket中的IO流进行数据的传输
关闭Socket
同样,客户端与服务器端是两个独立的应用程序
类Socket
此类实现客户端套接字,套接字是两台机器间通信的端点。
套接字的实际工作由SocketImpl类的实例执行
应用程序通过更改创建套接字实现的套接字工厂
可以配置它自身,以创建适合本地防火墙的套接字。
构造函数
Socekt(InetAddress address,int port)
创建一个流套接字并将其连接到指定IP地址的指定端口号
创建了对象就连接到服务端
Socket(String host,int port)
创建一个流套接字并将其连接到指定主机上的指定端口号
方法:
InputStream getInputStream()
返回此套接字的输入流
OutputStream getOutputStream()
返回此套接字的输出流
InetAddress getInetAddress()
返回套接字连接的地址
类ServerSocket
构造函数:
ServerSocket(int port)
创建绑定到特定端口的服务器套接字
方法:
Socket accept()
侦听并接收到此套接字的连接
//先打开服务端,在开客户端,否则客户端连接不到服务端。。。
//本示例:客户端向服务端发送一次数据,服务端接收到数据,并断开与客户端的连接
/*
TCP传输
1,tcp分客户端和服务端
2,客户端对应的对象是Socket
服务端对应的对象是ServerSocet
客户端:
查阅Socket对象,发现在
该类对象建立时,就可以去连接指定主机
因为tcp是面向连接的,所以在建立
Socekt服务时,就要有服务端存在,并连接成功
形成通路后,在该通道进行数据传输。
需求:给服务端发送一个文本数据
步骤:
1,创建Socket服务,并指定要连接的主机和端口
*/
import java.io.*;
import java.net.*;
class TcpClient
{
public static void main(String[] args) throws Exception
{
//创建客户端的socekt服务,指定目的主机和端口
Socket s = new Socket("127.0.0.1",6789);
//为了发送数据,应该获取socket流中的输出流
OutputStream out = s.getOutputStream();
out.write("我来了".getBytes());
s.close();
}
}
/*
定义端点接收数据并打印到控制台上
服务端
1,建立服务端的socket服务,ServerSocket()并监听一个端口
2,获取连接过来的客户端对象,通过ServerSocket的accept方法
如果没有连接就会等,所以这个方法是阻塞式的
3,客户端如果发过来数据,那么服务端要使用对应的客户端对象
并获取到该客户端对象的读取刘来读取发过来的数据
4,关闭服务(可选--服务端常开)
*/
class TcpServer
{
public static void main(String[] args) throws Exception
{
//建立服务端的Socket服务,并监听一个端口
ServerSocket ss = new ServerSocket(6789);
//通过accept方法获取连接过来的客户端对象
Socket s = ss.accept();
//获取客户端发送过来的数据,使用客户端对象的读取流读取数据
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
s.close();//关闭客户端
//ss.close();//服务端一般都保持常开,不关闭
}
}
/*
演示tcp传输的客户端和服务端的互动
需求:客户端给服务端发送数据,服务端收到数据后,给客户端反馈数据
*/
import java.io.*;
import java.net.*;
/*
客户端
1,建立socekt服务,指定要连接的主机和端口
2,获取socekt流中的输出流,将数据写到该流中
通过网络发送给服务端
3,获取socket流中的输入流,将服务端反馈的数据获取到并打印
4,关闭客户端资源
*/
//客户端
class TcpClient2
{
public static void main(String[] args) throws Exception
{
//建立Socket连接
Socket s = new Socket("127.0.0.1",10000);
//向服务端发送数据
OutputStream out = s.getOutputStream();
out.write("服务端我来了".getBytes());
//获取服务端的反馈数据
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);//阻塞式方法,Scoket没有收到数据就等待
System.out.println(new String(buf,0,len));
//关闭连接
s.close();
}
}
//服务端
class TcpServer2
{
public static void main(String[] args) throws Exception
{
//建立服务端
ServerSocket ss = new ServerSocket(10000);
//侦听并接收数据
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println("ip:"+ip+"...connected");
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);//阻塞式方法,Scoket没有收到数据就等待
System.out.println(new String(buf,0,len));
//向客户端发送数据
OutputStream out = s.getOutputStream();
out.write("收到!".getBytes());
//断开连接
s.close();
//ss.close();
}
}
/*
需求:建立一个文本转换服务器
客户端通过键盘给服务端发送文本数据,
服务端将文本转成大写并返回给客户端
而且客户端可以不断的进行文本转换,
并输入over之后结束转换
*/
/*字符打印流
PrintWriter---非常常用。web开发时使用它将数据一行一行打到客户端
构造函数可以接收的参数类型:
1,file对象 File
2,字符串路径 String
3,字节输出流 OutputStream
4,字符输出流 Writer
*/
/*
注意:
1, PrintWriter 非常常用,应该掌握
Printwriter 在构造时,可传入字节流,也可以传入字符流,很通用。还能传入字符串和文件流对象
在构造时,如果传入参数true,则使用println会换行并自动刷新缓冲区
即,可以替代缓冲区写出流的newLine()和flush()
2,该例中操作的源和目的都是文本数据,即字符数据
为了提高效率,所以采用缓冲区字符读取和写入流
这里要注意的是,对数据的操作使用缓冲区必须进行刷新动作
另外,write(line)写入一行是不会写入“回车符”的
而read(line)则要以读到“回车符”为标记,才能判断一行结束
所以,在write的时候,就必须newLine
接收方读的时候才会依据换行符读取成功!
3,关闭客户端时,会返回-1到socket输出流,当服务端读取到-1之后,
应该产生相应动作来处理。
服务端可以退出,也可以不退出!
该怎么做呢??
*/
import java.io.*;
import java.net.*;
class TcpClient3
{
public static void main(String[] args) throws Exception
{
//建立Socket连接
Socket s = new Socket("127.0.0.1",8888);
//定义读取键盘数据的流对象
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//定义目的,将数据写入到socket输出流,发送给服务端
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//定义一个socket读取流,读取服务端返回的信息
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null)
{
if(line.equals("over"))
break;
out.println(line);//换行并带自动刷新
// bufOut.write(line);//写一行是不会写入回车符的
// bufOut.newLine();//所以这里一定要换行
// bufOut.flush();
String serverMessage = bufIn.readLine();//读一行,要读到行结束标记才算
System.out.println("服务端:"+serverMessage);
}
bufr.close();
s.close();//关闭该流,会返回-1到该socket读取流中,导致服务端读取到之后,服务端也关闭
}
}
class TcpServer3
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(8888);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println("客户端ip:"+ip+"...is connect");
//读取socket读取流中的数据
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//目的为socket输出流。将转换后的大写数据写入到socket输出流,并发送给客户端
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);//true对println是有效自动刷新
String line = null;
while((line=bufIn.readLine())!=null)
{
if(line.equals("close server"))
break;
System.out.println("客户端message:"+line);
out.println(line.toUpperCase());//true对println是有效自动刷新
// bufOut.write(line.toUpperCase());
// bufOut.newLine();
// bufOut.flush();
}
s.close();
ss.close();
}
}
/*
IO 结合 Socket Tcp文件传输
需要注意的问题:
1,异常处理
2,判断文件是否存在
3,缓冲区写需要flush
4,PrintWriter的应用
5,readLine读数据到缓冲区不会读入结束标记
另一个readLine再读缓冲区因为其中没有结束标记,所以会一直读,死循环
6,文件结束标记
1,自定义一个结束标记。客户端与服务端协商一个读取缓冲区数据结束标记
缺点在于:有可能读取到的文件中的数据的一行有这个标记
2,利用时间来标记。。时间是唯一的
3,socket提供的方法
void shutdownInput() 关闭输入流--在流中加入结束标记
void shutdownOutput() 关闭输出流--在流中加入结束标记
7,发送文件之前,应该先发读取的文件名到服务端,以便服务端创建好文件
8,服务端应判断文件名是否重名,如果重名则会覆盖以前的文件,所以需要判断文件名
*/
import java.io.*;
import java.net.*;
class TextClient
{
public static void main(String[] args) throws Exception
{
//建立socket连接
Socket s = new Socket("127.1.1.0",7777);
//带缓冲区的字符读取流关联文件
BufferedReader bufr =
new BufferedReader(new FileReader("E:/day19/SetSystemDemo.java"));
//字符打印流对象包装socket字节输出流
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
//获取当前时间并传给服务端,以此为上传结束标记(时间是唯一的)
long time = System.currentTimeMillis();
//out.println(time+"");//long型转为字符串
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeLong(time);
String line = null;
while((line=bufr.readLine())!=null)//读取文件,文件有结束标记!当判断到结束标记是循环就结束了!
{
out.println(line);//结束标记是不会写入到socket字节输出流中的
}
//out.println("over");
//s.shutdownOutput();//写入流结束标记
out.println(time+"");
//带缓冲区的字符读取流包装socket字节输入流
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//服务端只返回”上传成功“一句,不用循环
String str = bufIn.readLine();//阻塞式方法。没有数据就等待
System.out.println(str);
bufr.close();
s.close();
}
}
class TextServer
{
public static void main(String[] args) throws Exception
{
//建立服务端socket连接
ServerSocket ss = new ServerSocket(7777);
//侦听并接收数据
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"......连接成功");
DataInputStream dis = new DataInputStream(s.getInputStream());
String time = dis.readLong()+"";
//包装socket字节输入流
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//创建接收文件
PrintWriter pw = new PrintWriter(new FileWriter("e:/tcp.java"),true);
String line = null;
while((line=bufIn.readLine())!=null)//这里读socket输入流,当中不会有文件结束标记,所以循环停不下来
{
//if(line.equals("over")) //底层一直在调用read()读取缓冲区数据!这里需要加入结束标记!
if(line.equals(time))
break;
pw.println(line);
}
//BufferedWriter bufOut =
//new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//bufOut.write("上传成功");
//bufOut.flush();//必须刷新。
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("上传成功!");//自动刷新
s.close();
ss.close();
}
}