笔记——网络编程

网络编程

5.1.2:IP地址

目前Java对IPv6和IPv两种地址均进行了封装,其实现类分别是Inet4Address和Inet6Address,它们都继承了类InetAddress。

InetAddress是Java对Ip地址的封装,在java.net中许多类都用到。InetAddress对域名进行解析是用本地机器配置或者网络命名服务(如DNS)和网络信息服务(NIS)。以下是获取InetAddress对象获取最常用的方法getByName(String host)。

import java.net.InetAddress;
import java.net.UnknownHostException;
​
public class NetTest {
​
    public static void main(String[] args) throws UnknownHostException {
        InetAddress address = InetAddress.getByName("www.cnblogs.com");
        System.out.println("==========获取博客园的IP地址============");
        System.out.println(address.toString());
        InetAddress[] addresses = InetAddress.getAllByName("www.cnblogs.com"); 
        System.out.println("==========获取博客园的IP地址列表==========");
        for(InetAddress add : addresses)
            System.out.println(add.toString());
    }
​
}
​

 

 

运行结果:

 

5.2 TCP编程

TCP是一种可靠的、基于连接的网络协议,它是面向字节流的,即从一个进程到另一个进程的二进制序列。一条TCP连接需要两个断电,这两个端点需要分别创建各自的套接字。通常一方用于发送请求和数据(成为客户端),而另一方用于监听网络请求和数据(成为服务端)。

5.2.1 核心类

java.net包中有恒多用于网络编程的类,其中有两个常用于TCP编程。

① Socket ,Socket是建立网络连接时用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。

方法说明
public Socket(String host,int port) 创建一个流套接字并将其连接到指定主机上的指定端口号
public Socket(InetAddress address,int port,Inetaddress loaclAddr, int localPort) 创建一个流套接字并制定了本地的端口以及目的地地址的端口。
public Socket() 创建一个流套接字,但此套接字并未连接
public InputStream getInputStream() 该方法返回程序中套接字所能读取的输入流
public OutputStream getOutputStream() 返回套接字中的输出流
public void close 关闭指定的套接字,套接字中的输入输出流也即将被关闭

 

②ServerSocket ServerSocket类实现服务器套接字,等待请求通过网络传入,给予该请求执行某些操作,然后可能向请求者返回结果

方法说明
public ServerSocket() 创建一个服务器套接字,并未指明地址和端口
public ServerSocket(int port) 创建一个服务器套接字,指明了监听的端口,默认接受的最大连接数为50,但与则拒绝新接入
public ServerSocket(int port,int backlog) 创建一个服务器套接字,指明了监听的端口,接受的最大连接数由参数backlog设定,多于则拒绝。
public Socket accept() 建立连接后,该方法会返回一个套接字,用于处理客户端请求以及服务端响应
public void setSoTimeout(int timeout) 用于设置accept()方法的最大阻塞时间,超过将抛出异常
public void close() 关闭服务器套接字

5.2.2 一对一通信

Server和Socket能创建进行通信的网络程序,下面示例演示了这一过程,盖里定义了一个客户端进程、一个服务端进程。客户端进程发送请求,服务端接收请求后,返回处理结果,代码如下:

//Server.java
​
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 Server {
​
    public static void main(String[] args) throws IOException {
        
        ServerSocket server = new ServerSocket(888);
        Socket socket  = server.accept();
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        BufferedReader bufferReader = new BufferedReader(reader);
        PrintWriter writer = new PrintWriter(socket.getOutputStream());
        
        String request = bufferReader.readLine();
        System.out.println("Client say:" + request);
        String line = "Hello, too!";
        writer.println(line);
        writer.flush();
        writer.close();
        bufferReader.close();
        
        socket.close();
        server.close();
        
    }
​
}
​
 

//Client.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
​
public class Client {
​
    public static void main(String[] args) throws Exception {
        
        Socket socket = new Socket("127.0.0.1",888);
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        BufferedReader bufferReader = new BufferedReader(reader);
        PrintWriter writer = new PrintWriter(socket.getOutputStream());
        
        String readLine = "Hello!";
        writer.println(readLine);
        writer.flush();
        String response = bufferReader.readLine();
        System.out.println("Server say:" + response);
        writer.close();
        bufferReader.close();
        socket.close();
    }
​
}
​

 

 

 

运行结果:

 

 

5.2.3一对多通信

前面的 Client/ Server程序只能实现 Server和一个客户的对话。在实际应用中,往往在服务器上运行一个永久的程序,接收来自其他多个客户端的请求,提供相应的服务。为了实现在服务器方给多个客户提供服务的功能,需要利用多线程实现多客户机制。服务器总是在指定的端口上监听是否有客户请求,一旦监听到客户请求,服务器就会启动一个专门的服务线程来响应该客户的请求,而服务器本身在启动完线程后马上又进入监听状态,等待下个客户的到来,下面用例5-4来实现这一过程:在服务端定义两个类,一个类负责监听连接和线程分配,另一个类负责套接字及流的处理;客户端代码仍然使用例5-3中的客户端。

import java.net.ServerSocket;
import java.net.Socket;
​
​
public class server1 {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(888);
        while (true) {
            Socket socket = server.accept();
            SocketHandler handler = new SocketHandler(socket);
            Thread thread = new Thread(handler);
            thread.start();
        }
    }
}
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
​
public class SocketHandler implements Runnable {
    private Socket socket;
​
    public SocketHandler(Socket socket) {
        this.socket = socket;
    }
​
    public void run() {
        try {
            InputStreamReader reader = new InputStreamReader(socket.getInputStream());
            BufferedReader buffer_reader = new BufferedReader(reader);
            PrintWriter writer = new PrintWriter(socket.getOutputStream());
            String client = "<" + socket.getInetAddress().toString() + ":" + socket.getPort() + ">";
            String request = buffer_reader.readLine();
            System.out.println(client+" say: " + request);
            String line = client + " Hello, too! ";
            writer.println(line);
            writer.flush();
            buffer_reader.close();
            socket.close();
        } catch (Exception e) {
​
            e.printStackTrace();
        }
    }
}

 

 

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
​
public class Client {
​
    public static void main(String[] args) throws Exception {
        
        Socket socket = new Socket("127.0.0.1",888);
        InputStreamReader reader = new InputStreamReader(socket.getInputStream());
        BufferedReader bufferReader = new BufferedReader(reader);
        PrintWriter writer = new PrintWriter(socket.getOutputStream());
        
        String readLine = "Hello!";
        writer.println(readLine);
        writer.flush();
        String response = bufferReader.readLine();
        System.out.println("Server say:" + response);
        writer.close();
        bufferReader.close();
        socket.close();
    }
​
}
​

 

 

 

运行结果:

 

 

 

 

 

5.3 UDP编程

 

UDP是一种不可靠无连接的网络协议,与可靠连接协议TCP相比,它不能保证发送的数据一定能被对方按顺序收到,如果数据在传送的过程中丢失,也不会自动重发。然而由于它不需要像TCP那样,每次通信都要建立一条特定的连接通道,进行传输控制,UDP本身的数据就自带了传输控制信息,因此UDP传输能节省系统开销,而且在数据传输效率上UDP要比TCP高。

5.3.1核心类

①DatagramPacket 与TCP发送接收消息都是用流不同,UDP使用数据报文,当 发送/接受 数据报文时Java程序都创建数据报文实例DatagramPacket封装发送信息/存储接受的报文信息。

方法说明
public DatagramPacket(byte[] buf,int length) 构造DatagramPacket接受长度为length的数据包
public DatagramPacket(byte[] buf,int offset,int length) 构造DatagramPacket接受长度为length的数据包,缓冲区中指定了offset偏移量
public DatagramPacket(byte[] buf,int offset,int length, InetAddress address,int port) 构造数据包,用来将长度为length偏移量为offset的包发送到指定主机上的指定端口号
public void setAddress(InetAddress iaddr) 设置目标主机的IP地址
public void setPort(int iport) 设置目标程序的端口
public void setSocketAddress(SocketAddress address) 设置数据包的目的地(包括IP地址和端口号)
public void setData(byte[] buf) 为此包设置数据缓冲区。将此DatagramPacket设置为偏移量为0,长度为buf
public void setData(byte[] buf,int offset,int length) 为此包设置数据缓冲区,包括数据、长度和偏移量
   

②DatagramSocket 用于发送和接受UDP数据包,在Java中即为接受和发送DatagramPacket,与TCP的Socket不同的是,通信前无须事先建立连接。

方法说明
public DatagramSocket() 创建数据包套接字,该套接字并未指明监听的地址和端口,一般为发送方使用
public DatagramSocket(int port) 创建数据包套接字并指明了监听地址、端口,一般为接收方使用
public DatagramSocket(int port,InetAddress) 创建数据包套接字,指明了监听的地址和端口(主机有多个地址)和监听的端口,该数据包只接受发往指定地址和端口的数据包
public void send(DatagramPaccket p) 此方法用于发送封装好的数据报,数据报中序含有发送的数据、数据的长度以及目标地址和端口等信息。
public void recevie(DatagramPaccket p) 此方法用于接收封装好的数据报,数据报中序含有发送的数据、数据的长度以及目标地址和端口等信息。
public void Connect(InetAddress address,int port) 用于连接指定IP地址和端口的目的地址,建立连接后,接收方就只能接收目标地址发送的数据报,其他的数据报将被丢弃。
public void connect(SocketAddress addr) 与connect(InetAddress address,int port)类似
   

 

5.3.2 UDP传输实例

下面是一个简单的UDP传输实例。客户端封装在一个数据包DatagramPacket对象,该对象包含目标的IP地址和端口以及需要传输的数据。然后使用UDP套接字DatagramSocket发送出去。服务端一直使用UDP套接字监听指定端口,当监听到数据时,条用接收方法,填充DatagramPacket对象,接着发送一条确认信息会客户端。

 
package UDP编程;
​
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
​
public class Client {
​
    public static void main(String[] args) {
        try {
            
            DatagramSocket sendSocket = new DatagramSocket();
            //创建发送方的套接字,IP默认为本地,端口号随机
            String mes = "你好,接收方!";
            //由于数据报中传输的数据是以字节数组的形式存储的,所以要把字符串转化为字节数组
            
            byte[] buf = mes.getBytes();
            int port = 8888;
            //确定发送方的IP地址及端口号,地址为本地机器地址
            InetAddress ip = InetAddress.getLocalHost();
            DatagramPacket sendPacket = new DatagramPacket(buf,buf.length, ip,port);
            //创建发送类型的数据包
            sendSocket.send(sendPacket);
            //通过套接字发送数据
            
            byte[] getBuf = new byte[1024];
            //确定接受反馈数据的缓冲存储器,即存储数据的字节数组
            DatagramPacket getPacket = new DatagramPacket(getBuf, getBuf.length);
            //创建接收类型的数据报
            sendSocket.receive(getPacket);
            //套接字接收数据
            String backMes = new String(getBuf,0,getPacket.getLength());
            //解析反馈信息并打印
            System.out.println("接收方返回的信息:" + backMes);
            sendSocket.close();
            //关闭套接字
        }catch(Exception e) {
            e.printStackTrace();
        }
​
    }
​
}
​
 

package UDP编程;
​
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketAddress;
​
public class Server {
    
    public static void main(String[] args) {
        try {
            
            InetAddress ip = InetAddress.getLocalHost();
            int port = 8888;
            DatagramSocket getSocket = new DatagramSocket(port,ip);
            //创建接收方的套接字,并指定端口号和IP地址
            byte[] buf = new byte[1024];
            //确定接收数据报的数据的数组大小
            DatagramPacket getPacket = new DatagramPacket(buf,buf.length);
            //创建接收类型的数据报,并存储在buf中
            
            getSocket.receive(getPacket);
            //套接字接收数据
            String getMes = new String(buf,0,getPacket.getLength());
            System.out.println("对方发送的消息: " + getMes);
            InetAddress sendIP = getPacket.getAddress();
            //通过数据包获得发送放的套接字地址
            int sendPort = getPacket.getPort();
            System.out.println("对方地址是:" + sendIP.getHostAddress() + ": " + sendPort);
            
            SocketAddress sendAddress = getPacket.getSocketAddress();
            String feedback = "接收方说: 我收到了!";
            byte[] backBuf = feedback.getBytes();
            
            DatagramPacket sendPacket = new DatagramPacket(backBuf,backBuf.length, sendAddress);
            getSocket.send(sendPacket);
            getSocket.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
​

 

 

 

posted @ 2021-02-25 23:07  supermoon6  阅读(154)  评论(0)    收藏  举报