网络编程分为BIO(传统IO)、NIO、AIO。Socket编程属于BIO这种传统IO。java
java.net.InetAddress是JAVA中管理IP地址的类,经常使用编程
public static void main(String[] args) throws UnknownHostException { InetAdressDemo.getLocalHost(); System.out.println("---------------------------"); getHostByName("Lenovo-Autumn"); } /** * 获取主机ip和主机名 * @throws UnknownHostException */ public static void getLocalHost() throws UnknownHostException { //根据InetAddress获取主机名和主机ip InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost); //打印:Lenovo-Autumn/192.168.56.1 //根据getLocalHost()返回的值获取ip和主机名 String hostName = localHost.getHostName(); String hostAddress = localHost.getHostAddress(); System.out.println(hostName); //打印 Lenovo-Autumn System.out.println(hostAddress); //打印 192.168.56.1 //根据切割获取主机名和ip String[] str = localHost.toString().split("/"); System.out.println(str[0]); //打印 Lenovo-Autumn System.out.println(str[1]); //打印 192.168.56.1 } /** * 根据主机名称获取ip地址 * @param otherName 主机名(能够是局域网中的机器名或者是域名或者是ip) * @throws UnknownHostException */ public static void getHostByName(String otherName) throws UnknownHostException { InetAddress otherHost = InetAddress.getByName(otherName); String hostName = otherHost.getHostName(); String hostAddress = otherHost.getHostAddress(); System.out.println(hostName); //打印 Lenovo-Autumn System.out.println(hostAddress); //打印 192.168.56.1 System.out.println(otherHost); //打印:Lenovo-Autumn/192.168.56.1 }
code数组
发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址同样。服务器
接收端不须要明确知道数据的来源,只须要接收到数据便可。网络
构造函数:并发
第一种是用来接受的数据包,不须要指定IP和端口,第二种是用来发送的数据包须要指定ip和端口异步
方法:socket
获取ip地址,获取服务器ip或者客户端ipide
返回端口号,获取发送方或者函数
返回数据缓冲区
返回要发送或接受的数据包大小
DatagramPacket数据包的做用就如同是“集装箱”,能够将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还须要有码头。在程序中须要实现通讯只有DatagramPacket数据包也一样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的做用就相似于码头,使用这个类的实例对象就能够发送和接收DatagramPacket数据包,发送数据的过程以下图所示。
构造函数:
用来建立发送端的DatagramSocket对象
用来建立接收端的DatagramSocket对象
方法:
发送数据报包
接收数据报包
1, 建立DatagramSocket对象
DatagramSocket()
2,建立DatagramPacket对象,封装数据,并指定ip和端口。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
3,发送数据
socket.send(DatagramPacket dp)
4,释放流资源
ds.close();
1,建立DatagramSocket对象,只须要指定端口
DatagramSocket(port)
2,建立DatagramPacket对象
DatagramPacket(byte[] data, int length)
3,接收数据存储到DatagramPacket对象中
receive(DatagramPackage dp)
4,获取DatagramPacket对象的内容
new String(data,0,dp.getLength());
5,释放流资源
ds.close();
/** * 实现udp发送端 * 用java.net.DatagramPackage封装数据 * 用java.net.DatagramSocket发送数据 * * 实现步骤 * 1.用DatagramPackage对象,封装数据,接受的地址和端口 * 2.建立DatagramSocket * 3.调用DatagramSocket对象send方法,发送数据 * 4.关闭资源 * * DatagramPackage构造函数 * DatagramPacket(byte[] buf, int length, InetAddress address, int port) * DatagramSocket构造函数 * DatagramSocket() * 方法:send(DatagramPacket d) * Created by Autumn on 2018/2/5. */public class UdpSend { public static void main(String[] args) throws Exception { Scanner scanner = new Scanner(System.in); //获取地址 InetAddress inet = InetAddress.getByName("127.0.0.1"); //建立DatagramSocket,负责接受和发送数据 DatagramSocket ds = new DatagramSocket(); while(true){ String msg = scanner.nextLine(); //建立数据包对象对象 byte[] data = msg.getBytes(); //封装数据,接受的地址和端口 DatagramPacket dp = new DatagramPacket(data,data.length,inet,6000); //发送数据包 ds.send(dp); if(msg.equals("exit")){ break; } } //关闭 ds.close(); } }/** * 实现udp接收端 * 用java.net.DatagramPackage 接受数据 * 用java.net.DatagramSocket 接受数据包 * * 步骤 * 1.建立DatagramSocket对象,绑定端口号(要和发送端端口一致) * 2.建立字节数组用来接受数据 * 3.建立数据对象包DatagramPackage * 4.建立DatagramSocket * receive(DatagramPackage dp)接受数据,将数据封装如dp中 * 5.拆包 * 发送端的ip地址(DatagramPackage.get) * 接受到的字节数组 * 发送的端口号 * 6.关闭资源 * Created by Autumn on 2018/2/5. */public class UdpReceive { public static void main(String[] args) throws IOException { //建立数据包传输的对象,并绑定端口号 DatagramSocket ds = new DatagramSocket(6000); //建立字节数组 byte[] data = new byte[1024]; while(true){ //建立数据包对象,传递字节数组 DatagramPacket dp = new DatagramPacket(data,data.length); //调用ds对象的receive接受数据包,receive()有线程阻塞效果会一直等待接受数据 ds.receive(dp); //获取数据包大小 int len = dp.getLength(); //获取发送端的ip地址 InetAddress sendAddress = dp.getAddress(); String sendHostAddress = sendAddress.getHostAddress(); //System.out.println(sendHostAddress); //获取发送端端口号 int port = dp.getPort(); //System.out.println(port); //System.out.println(new String(data)); //直接打印1024个字节的字符串,有不少空格 System.out.println(sendHostAddress+":"+port+" "+new String(data,0,len)); //这样打印没有多余的空格 if(new String(data,0,len).equals("exit")){ break; } } //关闭 ds.close(); } }
code
ServerSocket类,用于表示服务器端,Socket类,用于表示客户端。创建链接后用流进行输入和输出
实例化一个ServerSocket类,指定端口号
监听并接受此套接字的链接
返回此服务器套接字的本地地址
实例化一个Socket并指定ip和端口
返回一个输出流,用于客户端发送数据
返回一个输入流,用于服务器端接受数据
返回ip地址(服务器端的地址)
返回端口号
1,建立客户端的Socket对象,指定服务器IP和端口号
Socket(String host, int port)
2,获取Socket的输出流对象
getOutputStream();
3,写数据给服务器
out.write("服务器数据".getBytes());
4,关闭流资源
socket.close();
1,建立服务器端ServerSocket对象,指定服务器端端口号
ServerSocket(int port)
2,开启服务器,等待着客户端Socket对象的链接,若有客户端链接,返回客户端的Socket对象
accept()
3,经过客户端的Socket对象,获取客户端的输入流,为了实现获取客户端发来的数据
socket.getInputStream();
4,经过客户端的输入流,获取流中的数据
byte[] data = new byte[1024];
int len = inputStream.read(data);
System.out.println(new String(data,0,len));
5,经过客户端的Socket对象,获取客户端的输出流,为了实现给客户端反馈信息
6,经过客户端的输出流,写数据到流中
7,关闭流资源
socket.close();
serverSocket.close();
/** * 实现TCP服务器程序 * 表示服务器程序的类java.net.ServerSocket * 构造方法: * ServerSocket(int port); 传递端口号 * * Important:必须得到客户端的套接字(Socket) * 方法:Socket accept() * 服务器能够获取到客户端的套接字 * Created by Autumn on 2018/2/5. */public class TCPServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888); //调用服务器套接字对象accept()获取客户端套接字,具备线程等待效果 Socket socket = serverSocket.accept(); //这里会阻塞等待链接接入 cmd中telnet 127.0.0.1 8888便可链接 //根据得到的客户端的socket获取输入流 InputStream inputStream = socket.getInputStream(); //根据输入流将数据读入到data中 byte[] data = new byte[1024]; int len = inputStream.read(data); //这里会阻塞,等待数据 cmd中ctrl+]进入到telnet操做模式send value发送数据 System.out.println(new String(data,0,len)); socket.close(); serverSocket.close(); } }/** * 实现TCP客户端,链接到服务器 * 和服务器实现数据交换 * 实现TCP客户端程序的类 java.net.Socket * * 构造方法: * Socket(String host, int port)传递服务器IP和端口号 * 注意:构造方法只要运行,就会和服务器进行链接,链接时报,抛出异常 * * OutputStream getOutputStream() 返回套接字的输出流 * 做用:将数据输出,输出到服务器 * InputStream getInputStream() 返回套接字的输入流 * 做用:从服务器端读取数据 * * 客户端服务器数据交换,必须使用套接字对象Socket中的获取的IO刘,本身new的流不行 * * Created by Autumn on 2018/2/5. */public class TCPClient { public static void main(String[] args) throws IOException { //建立Socket对象,链接服务器 Socket socket = new Socket("127.0.0.1",8888); //经过客户端的套接字对象Socket方法,获取字节输出流,将数据写向服务器 OutputStream out = socket.getOutputStream(); out.write("这是一条来客户端的数据".getBytes()); socket.close(); } }
import java.io.IOException;import java.io.InputStream;import java.net.ServerSocket;import java.net.Socket;public class TCPServer { /*同步阻塞*/ public static void main(String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务端启动成功..."); while(true){ /*一次只能处理一个链接,在一个链接没关闭前没法接收第二个链接,在这里开一个框发送数无问题,开放两个框时会出现第二个无反应*/ Socket socket = serverSocket.accept(); //这里会阻塞等待链接接入 cmd中telnet 127.0.0.1 8888即表明链接 System.out.println("新客户端链接成功...."); InputStream inputStream = socket.getInputStream(); while(true) { byte[] data = new byte[1024]; System.out.println("正在等待数据..."); int len = inputStream.read(data); //这里会阻塞,等待数据,若是直接关闭cmd窗口会由于关闭socket通道致使len返回-1 cmd中ctrl+]进入到telnet操做模式send value发送数据 if (len != -1){ System.out.println(new String(data, 0,len, "GBK")); //用GBK是由于CMD窗口命令发送的数据是GBK编码 }else{ break; } } } //socket.close(); //serverSocket.close(); //System.out.println("服务器端关闭...."); } }
链接服务端
向服务端发送数据
再开一个cmd而后telnet发送数据,发现无反应。必须关闭第一个cmd才能链接成功。
用ExecutorService线程池实现每个链接建立一个新的线程
import java.io.IOException;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class TCPServer { /*异步阻塞*/ public static void main(String[] args) throws IOException { ExecutorService threadPool = Executors.newCachedThreadPool(); //线程池 ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务端启动成功..."); while(true){ /*一次只能处理一个链接,在一个链接没关闭前没法接收第二个链接,在这里开一个框发送数无问题,开放两个框时会出现第二个无反应*/ System.out.println("等待客户端链接..."); final Socket socket = serverSocket.accept(); //这里会阻塞等待链接接入 cmd中telnet 127.0.0.1 8888即表明链接 threadPool.execute(new Runnable() { //启动一个线程 public void run() { try { System.out.println("新客户端链接成功...."); InputStream inputStream = socket.getInputStream(); while(true) { byte[] data = new byte[1024]; System.out.println("正在等待数据..."); int len = inputStream.read(data); //这里会阻塞,等待数据,若是直接关闭cmd窗口会由于关闭socket通道致使len返回-1 cmd中ctrl+]进入到telnet操做模式send value发送数据 if (len != -1){ System.out.println(Thread.currentThread()+new String(data, 0,len, "GBK")); //用GBK是由于CMD窗口命令发送的数据是GBK编码 }else{ System.out.println("break循环"); break; } } System.out.println("一次Socket链接关闭"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }); } //socket.close(); //serverSocket.close(); //System.out.println("服务器端关闭...."); } }
优势:传输质量好(因此BIO适合传输链接少可是数据量大的请求)
缺点:每个链接都占用一个线程,很占用系统资源。
tip:全部的调优都关联到系统资源(IO、存储、内存、CPU)
code