笔记——网络编程

网络编程

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  Jesen等不等式  阅读(111)  评论(0编辑  收藏  举报