Java基础(网络编程)

一、网络通信协议

计算机网络中,连接和通信的规则称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交互。

目前应用最广泛的网络通信协议是TCP/IP、UDP、ICMP和其他一些协议的协议组。

基于TCP/IP的参考模型将协议分为4个层次,链路层、网络层、传输层和应用层,每层负责不同的通信功能。

链路层:也称为网络接口层,负责监视数据在主机和网络之间的交互。

网络层:也称为网络互联层,是整个TCP/IP协议的核心,主要用于将传输的数据进行分组,将分组数据发送到目标计算机或网络。

传输层:主要完成网络程序的通信,进行网络通信时可以采用TCP或UDP。

应用层:主要负责应用程序的协议,如HTTP、FTP等。

1.1 IP地址和端口号

要想使网络中的计算机能够通信,必须为每台计算机制定一个标识号,在TCP/IP协议中,这个标识号就是IP地址。

目前IP地址广泛使用的版本是IPv4,它由四个字节大小的二进制数来表示,每个字节可以用一个十进制数字(0~255)表示,数字间用符号“.”分开。为了解决IP地址面临的枯竭问题,IPv6使用16个字节表示IP地址。

IP地址由“网络.主机”两部分组成。其中网络部分是网络的地址编码,主机部分是网络中一个主机的地址编码,二者是主从关系。IP地址总共分为5类,常用的有以下3类:

A类地址:由第一段的网络地址和其余三段的主机地址组成,范围1.0.0.0~127.255.255.255

B类地址:由前两段的网络地址和其余两段的主机地址组成,范围128.0.0.0~191.255.255.255

C类地址:由前三段的网络地址和最后一段的主机地址组成,范围192.0.0.0~223.255.255.255

回送地址127.0.0.1是指本机地址,常用来测试使用。

计算机中,不同的应用程序通过端口号区分。通过IP地址可以连接到指定的计算机,并通过端口号访问目标计算机中的某个应用程序。

端口号用两个字节表示,取值范围0~65535,其中0~1023的端口号由操作系统的网络服务占用,用户的普通应用程序需要使用1024以上的端口号。

1.2 InetAddress

Java中提供了一个与IP地址相关的InetAddress类,该类封装一个IP地址,常用方法如下:

eg.

import java.net.InetAddress;
public class Main{
    public static void main(String[] args){
        InetAddress localAddress = InetAddress.getLocalHost();
        InetAddress remoteAddress = InetAddress.getByName("www.baidu.com");
        System.out.println("本机的IP地址:"+localAddress.getHostAddress());
        System.out.println("baidu的IP地址:"+remoteAddress.getHostAddress());
        System.out.println("3s是否可到达:"+remoteAddress.isReachable(3000));
        System.out.println("baidu的主机名为:"+remoteAddress.getHostName());
    }
}

1.3 UDP与TCP

传输层的两个重要协议是UDP和TCP。UDP称为用户数据报协议,TCP称为传输控制协议。

UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。UDP消耗资源小,通信效率高,通常用于音频、视频和普通数据的传输,重要数据不建议使用UDP。

TCP是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据。TCP连接中必须要明确客户端和服务器端,由客户端向服务器端发出连接请求,每次连接的创建都要经过“三次握手”。TCP可以保证数据传输的安全性。

二、UDP通信

2.1 DatagramPacket

DatagramPacket类用于封装UDP通信中发送或接收的数据。

创建发送端和接收端的DatagramPacket对象的构造方法有所不同。

接收端:

(1)DatagramPacket(byte[] buf, int length) 指定了封装数据的字节数组和数据的大小

(2)DatagramPacket(byte[] buf,  int offset, int length) 增加了接收的数据开始的位置

发送端:

(1)DatagramPacket(byte[] buf, int length, InetAddress addr, int port) 增加了目标IP地址和端口号

(2)DatagramPacket(byte[] buf, int offset, int length, InetAddress addr, int port) 增加了发送的数据开始的位置

DatagramPacket类常用方法如下。

2.2 DatagramSocket

使用DatagramSocket类的实例对象可以发送和接收DatagramPacket数据包。

创建发送端和接收端的DatagramSocket对象的构造方法有所不同。

(1)DatagramSocket() 用于创建发送端的对象

(2)DatagramSocket(int port) 两个都可以

(3)DatagramSocket(int port, InetAddress addr) 两个都可以,适用于计算机上有多个网卡

DatagramSocket类常用方法如下。

2.3 UDP网络程序

为了避免发送端发送数据时找不到接收端造成数据丢失,要先完成接收端的编写。

import java.net.*;
//接收端程序
public class Receiver{
    public static void main(String[] args) throws Excpetion{
        byte[] buf = new byte[1024];
        DatagramSocket ds = new DatagramSocket(8954);
        DatagramPacket dp = new DatagramPacket(buf, buf.length);
        System.out.println("等待接收数据");
        ds.receive(dp);//等待接收数据,如果没有数据则阻塞
        String str = new String(dp.getData(), 0, dp.getLength())+
        "from" + dp.getAddress().getHostAddress() + ":" + dp.getPort(); 
        System.out.println(str);
        ds.close();
    }
}

只有接收到发送端发送的数据时,receive()方法才会结束阻塞状态。

import java.net.*;
//发送端程序
public class Sender{
    public static void main(String[] args) throws Exception{
        DatagramSocket ds = new DatagramSocket();
        String str = "hello world";
        byte[] arr = str.getBytes();//将字符串转为字节数组
        DatagramSocket ds = new DatagramSocket(arr, arr.length,                                             
      InetAddress.getByName("localhost"), 8954); 
        System.out.println("发送信息");
        ds.close();
    }
}

创建DatagramPacket对象时必须指定和接收端一样的端口号。

2.4 多线程的UDP网络程序

import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        new Receive().start();
        new Send().start();
    }
}
class Receive extends Thread{
    public void run(){
        try{
            DataSocket ds = new DataSocket(6666);
            byte[] buf = new byte[1024];
            DataPacket dp = new DataPacket(buf, buf.length);
            while(true){
                ds.receive(dp);
                byte[] arr = dp.getData();
                int len = dp.getLength();
                String ip = dp.getAddress().getHostAddress();
                System.out.println(ip + ":" + String(arr, 0, len)); 
            }
            ds.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}
class Send extends Thread{
    try{
        DatagramSocket ds = new DatagramSocket();
        Scanner sc = new Scanner(System.in);
        while(true){
            String str = sc.nextLine();
            if("quit".equals(str)){
                break;
            }
            byte[] arr = str.getBytes();
            int len = arr.length();
            DatagramPacket dp = new DatagramPacket(arr, len,                                                                         
          InetAddress.getByName("localhost"), 6666); 
            ds.send(dp);
        }
        ds.close();
    }catch(IOException e){
        e.printStackTrace();
    }
}

三、TCP通信

TCP和UDP的区别在于,UDP只有发送端和接收端,可以任意发送数据;TCP严格区分客户端和服务器端,通信时必须先启动服务器端,由客户端连接服务器端。

通信时要先创建代表服务器端的ServerSocket对象,调用该对象的accept()方法接受来自客户端的请求,然后创建代表客户端的Socket对象,使用该对象向服务器端发送连接请求,服务器端响应请求后,两者才建立连接开始通信。

客户端和服务器端建立连接后,数据是以I/O流的形式交互的。

3.1 ServerSocket

ServerSocket类提供了多种构造方法。

(1)ServerSocket()

该方法创建的ServerSocket对象不与任何端口绑定,不能直接使用,还需要继续调用bind(SocketAddress endpoint)方法将其绑定到指定的端口号上。其中endpoint参数用于封装IP地址和端口号

(2)ServerSocket(int port)

该方法最常用,指定了绑定的端口号。端口号可以指定为0,系统会分配一个未被使用的端口号。

(3)ServerSocket(int port, int backlog)

增加了backlog参数,用于指定在服务器忙时,可以与之保持连接请求的等待客户数量,如果未指定,默认为50。

(4)ServerSocket(int port, int backlog, InetAddress bindAddr)

增加了bindAddr参数,用于指定相关的IP地址。适用于计算机上有多个网卡和多个IP的情况。

ServerSocket类的常用方法如下。

3.2 Socket

Socket类有多种构造方法。

(1)Socket()

最常用的方法,该方法没有指定IP地址和端口号,即没有连接任何服务器,还需要调用connect(SocketAddress endpoint)方法完成于指定服务器的连接。

(2)Socket(String host, int port)

该方法指定了IP地址和端口号,其中host参数接受字符串类型的IP地址。

(3)Socket(InetAddress address, int port)

该方法与法二类似,参数address接受InetAddress类型的对象。

Socket类的常用方法如下。

3.3 TCP网络程序

import java.io.*;
import java.net.*;
public class Server{
    public static void main(String[] args)throws Exception{
        new TCPServer().listen();
    }
}
//TCP服务器端
class TCPServer{
    private static final int PORT = 7788;
    public void listen()throws Exception{
        ServerSocket server = new ServerSocket(PORT);
        Socket client = server.accept(); //接收数据
        OutputStream os = client.getOutputStream();
        os.write("welcome".getBytes());//当客户端连接到服务器端时,向客户端输出数据
        os.close();
        client.close();
    }
} 
import java.io.*;
import java.net.*;
public class Client{
    public static void main(String[] args)throws Exception{
        new TCPClient().connect();
    }
}
//TCP服务器端
class TCPClient{
    private static final int PORT = 7788;
    public void connect()throws Exception{
        Socket client = new Socket(InetAddress.getLocalHost(), PORT);
        InputStream is = client.getInputStream();
        byte[] buf = new byte[1024];
        int len = is.read(buf);
        System.out.println(new String(buf, 0, len));
        client.close();
    }
} 

3.4 多线程的TCP网络程序

改进服务器端程序,实现多客户访问服务器。

import java.io.*;
import java.net.*;
public class Server{
    public static void main(String[] args)throws Exception{
        new TCPServer().listen();
    }
}
//TCP服务器端
class TCPServer{
    private static final int PORT = 7788;
    public void listen()throws Exception{
        ServerSocket server = new ServerSocket(PORT);
        while(true){
            final Socket client = server.accept();
            new Thread(){
                public void run(){
                    try{
                        OutputStream os = client.getOutputStream();
                        os.write("welcome".getBytes());
                        os.close();
                        client.close();
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
} 
  • 17
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值