计算机网络:
所谓的计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备用通讯线路互联成一个规模大、功能强的网络系统,从而使众多的计算机可以方便的交换信息。
计算机网路是现代通讯技术也计算机技术相结合的产物,计算机网路可以提供如下的一些功能。
- 资源共享
- 信息传输与集中处理
- 均衡负荷与分布处理
- 综合信息服务
网路参考模型:
OSI(Open System Interconnection 开放系统互联)参考模型
TCP/IP 参考模型
国际标准组织于1978年提出“开放系统互联参考模型”,该模型将计算机网路分为7层。
1. 物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后再转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
2. 数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
3. 网络层:主要将下层接收到的数据进行IP地址(例,192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。
4. 传输层:定义了一些传输数据的协议和端口号(WWW端口号80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层叫做段。
5. 会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接收会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。
6. 表示层:主要是进行对接收的数据进行解释,加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够识别的东西(如图片、声音等)。
7. 应用层:主要是一些终端的应用,比如说FTP(各种文件下载)、WEB(IE浏览)、QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西,就是终端应用)。
2. 数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
3. 网络层:主要将下层接收到的数据进行IP地址(例,192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。
4. 传输层:定义了一些传输数据的协议和端口号(WWW端口号80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层叫做段。
5. 会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接收会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。
6. 表示层:主要是进行对接收的数据进行解释,加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够识别的东西(如图片、声音等)。
7. 应用层:主要是一些终端的应用,比如说FTP(各种文件下载)、WEB(IE浏览)、QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西,就是终端应用)。
TCP/IP 参考模型可以分为四层,是将OSI简化后的模型。
网路通讯的要素:
1、IP地址:InetAddress
网路中设备的标识
2、端口号
用来标识计算机中网络程序
3、传输协议
通讯的规则,常见的协议:UDP、TCP
UDP :
将数据及源和目的封装成数据包中,不需要建立连接。
每个数据报的大小在限制在64k内。
因无连接,是不可靠协议。
不需要建立连接,速度快。
每个数据报的大小在限制在64k内。
因无连接,是不可靠协议。
不需要建立连接,速度快。
TCP:
建立连接,形成传输数据的通道。
在连接中进行大数据量传输。
通过三次握手完成连接,是可靠协议。
必须建立连接,效率会稍低。
建立连接,形成传输数据的通道。
在连接中进行大数据量传输。
通过三次握手完成连接,是可靠协议。
必须建立连接,效率会稍低。
IP地址对象:InetAddress
public class IpAddressTest {
public static void main(String[] args)throws Exception{
//获得本机的ip地址对象
InetAddress myAddress = InetAddress.getLocalHost();
System.out.println(myAddress.getHostAddress());
System.out.println(myAddress.getHostName());
InetAddress address2 = InetAddress.getByName("192.168.1.101");
System.out.println(address2.getHostAddress());
System.out.println(address2.getHostName());
//获得百度 的IP地址对象,对于像baidu这样主机名对应的IP地址不唯一
//可以使用getAllbyName的到一个IP地址对象的数组。
InetAddress[] address = InetAddress.getAllByName("www.baidu.com");
for(int i=0;i<address.length;i++){
System.out.println(address[i].getHostAddress());
System.out.println(address[i].getHostName());
}
}
}
结果:
使用UDP进行传输
使用Udp进行通信时:
- 建立两个DatagramSocket,接收端的Socket必须制定一个端口。
- 建立数据包,将数据和接收端的地址和端口都封装进去
- 调用发送端Socket的send方法,将数据包发送出去
- 接收端,通过receive方法接收数据包
- 关闭资源
发送端:
public class UdpSend {
public static void main(String[] args)throws Exception {
System.out.println("客户端启动");
//建立Socket端,可以指定端口号,也可以不指定
DatagramSocket socket = new DatagramSocket();
//要传输的字符
String s = "abcd";
//将字符转换成,字节数组
byte[] buf = s.getBytes();
//建立数据包,将数据和发送的地址封装到里面。
DatagramPacket packet = new DatagramPacket(buf, buf.length,InetAddress.getByName("192.168.1.101") , 4004);
//调用DatagramSocket的send方法将包发送出去。
socket.send(packet);
//关闭资源
socket.close();
}
}
接收端:
public class UdpResive {
public static void main(String[] args) throws Exception{
System.out.println("接受端启动");
//建立,DatagramSocket端,绑定接受端口
DatagramSocket socket = new DatagramSocket(4004);
byte[] buf = new byte[1024];
//建立数据包,用于接受数据
DatagramPacket p = new DatagramPacket(buf, buf.length);
//调用DatagramSocket的resive方法,接收数据
socket.receive(p);
//数据包中有很多数据,getData可以拿到发过来的数据。
System.out.println((p.getAddress().getHostAddress()+"::"+new String(p.getData(),0,p.getLength())));
//关闭资源
socket.close();
}
}
结果:
利用多线程实现聊天:
public class Chat {
public static void main(String[] args)throws Exception {
//创建,接收和发送的socket端。
DatagramSocket ds1 = new DatagramSocket(4002);
DatagramSocket ds2 = new DatagramSocket();
new Thread(new Resive(ds1)).start();
new Thread(new Send(ds2)).start();
}
}
/*
* 发送线程
*/
class Send implements Runnable {
DatagramSocket ds ;
public Send(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run(){
try{
//接收来至键盘的输入
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String str =null ;
byte[] buf = new byte[1024];
//当输入的不是886时,不断的从键盘读取数据,
while((str=reader.readLine())!=null){
buf = str.getBytes();
//将读取的数据打包
DatagramPacket p = new DatagramPacket(buf, buf.length, InetAddress.getByName("192.168.1.255"),4002);
//将数据包发送出去
ds.send(p);
//判断结束标识
if(str.equals("886")){
//关闭资源
ds.close();
System.out.println("客户端退出");
break;
}
}
}catch(Exception e){
System.out.println(e);
}
}
}
/*
* 接收线程
*/
class Resive implements Runnable{
DatagramSocket ds ;
public Resive(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
try{
byte[] buf = new byte[1024];
DatagramPacket p = new DatagramPacket(buf, buf.length);
//不断的接收数据包
while(true){
ds.receive(p);
String s = new String(p.getData(),0,p.getLength());
System.out.println("接收: "+p.getAddress().getHostAddress()+"::"+s);
//当接收到886时退出,并关闭资源
if(s.equals("886")){
ds.close();
System.out.println("接收端退出");
break ;
}
}
}catch(Exception e){
System.out.println(e);
}
}
}
结果:
TCP传输数据
tcp的Socket分客户端和服务端
客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。与服务端通讯结束后,关闭Socket。
服务端需要明确它要处理的数据是从哪个端口进入的。当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与
客户端通过IO流进行数据传输。当该客户端访问结束,关闭该客户端。
一般步骤:
客户端通过IO流进行数据传输。当该客户端访问结束,关闭该客户端。
一般步骤:
- 分别建立Socket和ServerSocket,确保客户端一创建就可以连接服务端。
- 服务端接受客户端的请求,并拿到客户端的Socket对象
- 从Socket对象中获取输入输出流,进行数据传输。
- 关闭客户端和服务端。
public class SocketResive {
public static void main(String[] args)throws Exception {
//建立服务端的Socket,并监听8888端口
ServerSocket ss = new ServerSocket(8888);
System.out.println("服务端启动");
//接收 请求,拿到客户端的Socket
Socket s = ss.accept();
//得到Socket的输入流,将其转换包装成BufferedReader
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
//从流中 读取数据
System.out.println(s.getInetAddress().getHostAddress()+":"+reader.readLine());
//关闭资源
ss.close();
s.close();
}
}
客户端:
public class SocketSen {
public static void main(String[] args)throws Exception {
System.out.println("客户端启动");
//建立客户端Socket ,指定客户端的IP的端口
Socket s = new Socket("192.168.1.101",8888);
//拿到Socket的输出流,将其包装成打印流
PrintWriter writer = new PrintWriter(s.getOutputStream(),true);
//相流中输出数据
writer.println("Hello");
//关闭资源
s.close();
}
}
结果:
使用Socket上传文件:
服务端:
public class UperFile {
public static void main(String[] args)throws Exception {
ServerSocket ss = new ServerSocket(7779);
//服务端,不停的接收请求,
//当一个接收一个请求,获得请求的socket
//并开启一个线程储存文件。
while(true){
Socket s1 = ss.accept();
new Thread(new FileServer(s1)).start();;
}
}
}
class FileServer implements Runnable{
private Socket s ;
PrintWriter writer ;
File f ;
public FileServer(Socket s){
this.s = s ;
}
public void run(){
try{
//获得客户端的地址
String name = s.getInetAddress().getHostAddress();
//根据IP地址,建立一个文件
f = new File("c:\\", name+".txt");
int i=0;
//判断文件是否重复,重复的话,加一个数字标识。
while(f.exists()){
f=new File("c:\\",name+"("+ i++ +")"+".txt");
}
//根据上面的file,建立一个输出
BufferedWriter writer = new BufferedWriter(new FileWriter(f));
//获得socet的输入流
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line=null ;
while((line = reader.readLine())!=null){
writer.write(line);
writer.newLine();
writer.flush();
}
System.out.println("上传完毕");
writer.close();
reader.close();
s.close();
}catch(Exception e){
System.out.println(e);
}
}
}
客户端:
public class UperFileCli {
public static void main(String[] args)throws Exception {
Socket s = new Socket("192.168.1.101",7779);
new Thread(new FileClient(s)).start();
}
}
class FileClient implements Runnable{
Socket s ;
BufferedReader reader;
BufferedReader reader2;
public FileClient(Socket s){
this.s = s ;
}
public void run(){
try{
//从键盘输入 文件的路径
reader = new BufferedReader(new InputStreamReader(System.in));
//根据路径名,new一个file
String filename = reader.readLine();
File file = new File(filename);
//判断文件是不是一个文件
if(file.isDirectory())
return;
//判断文件大小。
if(file.length()>1024*1024*2)
return;
//根据上面的file 创建流对象
reader2 = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
//输出流,向服务端输出数据
PrintWriter writer = new PrintWriter(s.getOutputStream(),true);
String line =null ;
while((line =reader2.readLine())!=null){
writer.println(line);
}
writer.close();
reader.close();
//当写完了,结束流
s.shutdownOutput();
}catch(Exception e){
System.out.println(e);
}
}
}