Java Socket
一、网络基础
TCP/IP协议:是以TCP和IP为接触的不同层次上多个协议的集合。
TCP:传输控制协议 IP:网际协议
IP地址:为实现网络中不通过计算机之间的通信,每台计算机必须有一个唯一的标识。
端口:用于区分不同的应用程序,端口号范围为0~65535,其中0~1023位系统所保留。
IP地址和端口号组成了所谓的Socket,Socket是网络上个运行的程序之间双向通信链路的终结点,是TCP UDP的基础。
二、Java中网络相关API应用
1)InetAddress
InetAddress类终于标识网络上的硬件资源,表示IP地址,该类没有构造方法(private),可通过getByAddress(),getByName(),getLocalHost()等静态方法得到InetAddress实例。InetAddress实例可得到当前InetAddress的ip地址 主机名等信息。
public static void main(String[] args) throws UnknownHostException { //获取本机的InetAddress实例 InetAddress address=InetAddress.getLocalHost(); System.out.println("计算机名:"+address.getHostName()); System.out.println("IP地址:"+address.getHostAddress()); //获取字节数组形式的IP地址 byte[] bytes=address.getAddress(); System.out.println("字节数组形式的IP:"+Arrays.toString(bytes)); //直接输出InetAddress对象 System.out.println(address); //根据机器名获取InetAddress实例 InetAddress address2=InetAddress.getByName("zhao"); System.out.println(address2); //根据IP地址获取InetAddress实例 InetAddress address3=InetAddress.getByAddress(bytes); System.out.println(address3); } 2)URL URL:统一资源定位符,标识Internet上某一资源的地址,由两部分组成:协议名和资源名称,可以通过String创建URL实例,也可以根据父URL创建子URL实例。 public static void main(String[] args) throws IOException { //创建一个URL实例 URL imooc=new URL("http://www.imooc.com"); //?后面表示参数 #后面表示锚点 // URL url=new URL(imooc, "/index.html?username=tom#test"); System.out.println("协议 "+imooc.getProtocol()); System.out.println("主机 "+imooc.getHost()); //如果为指定端口号,则使用默认的端口号,此时getPort方法返回值为-1 System.out.println("端口号 "+imooc.getPort()); // System.out.println("文件路径 "+url.getPath()); // System.out.println("文件名 "+url.getFile()); // System.out.println("相对路径 "+url.getRef()); // //通过URL的openStream方法获取URL对象所表示的资源的字节输入流 InputStream inputStream=imooc.openStream(); //将字节输入流转换成字符输入流 InputStreamReader inputStreamReader=new InputStreamReader(inputStream,"utf-8"); //为字符输入流添加缓冲 BufferedReader reader=new BufferedReader(inputStreamReader); //读取数据 String data=reader.readLine(); while(data!=null){ System.out.println(data); data=reader.readLine(); } reader.close(); inputStreamReader.close(); inputStream.close(); }
三、Socket
1)TCP协议是面向连接的,可靠的 有序的 以字节流方式发送数据
基于TCP协议实现网络通信的类,客户端的Socket类,服务端的Server Socket类
//客户端
package com.zhao.TCPSocket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; public class Client { public static void main(String[] args) throws IOException { //1:创建客户端Socket,指定服务器地址和端口 InetAddress address=InetAddress.getLocalHost(); try { Socket socket=new Socket(address,8888); //2:获取输出流,向服务器端发送登录的信息 OutputStream outputStream=socket.getOutputStream(); PrintWriter printWriter=new PrintWriter(outputStream); printWriter.write("dfasfas"); printWriter.flush(); socket.shutdownOutput(); //关闭输入流 //3:获取输入流,并读取服务器端响应的信息 InputStream inputStream=socket.getInputStream(); InputStreamReader inputStreamReader=new InputStreamReader(inputStream); BufferedReader bufferedReader=new BufferedReader(inputStreamReader); String info=null; while((info=bufferedReader.readLine())!=null) { System.out.println("服务器端回复:"+info); } //4:关闭资源 bufferedReader.close(); inputStreamReader.close(); inputStream.close(); printWriter.close(); outputStream.close(); socket.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
//服务端
package com.zhao.TCPSocket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; /** * 基于tcp协议的Socket通信,实现用户登录 服务器端 * * @author admin * */ public class Server { public static void main(String[] args) throws IOException { // 1:创建一个服务器端Socket,即ServerSocket,绑定指定的端口,并监听此端口 ServerSocket serverSocket = new ServerSocket(8888); System.out.println("***服务器即将启动,等待客户端的连接***"); //记录客户端的数量 int count=0; Socket socket=null; //循环监听,等待客户端的连接 while(true){ // 2:调用accept()方法开始监听,等待客户端的连接 //注:在调用accept()方式时,将进入阻塞状态,等待客户端的请求。当请求到来时,这个Socket实例并没有完成创建,要等到与客户端3次握手完成后,这个Socket才会返回。 socket=serverSocket.accept(); //创建一个新的线程 ServerThread serverThread=new ServerThread(socket); //启动线程 serverThread.start(); //统计客户端的数量 count++; System.out.println("客户端的数量: "+count); } } }
package com.zhao.TCPSocket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; /** * 服务器线程处理类 * * @author admin * */ public class ServerThread extends Thread { // 和本线程相关的socket Socket socket = null; public ServerThread(Socket socket) { this.socket = socket; } // 执行线程的操作,响应客户端的请求 @Override public void run() { // 3:获取输入流,读取客户端所发送的信息 InputStream inputStream = null; InputStreamReader inputStreamReader = null; BufferedReader bufferedReader = null; OutputStream outputStream = null; PrintWriter printWriter = null; try { inputStream = socket.getInputStream();// 字节输入流 inputStreamReader = new InputStreamReader(inputStream);// 将字节流转成字符流 bufferedReader = new BufferedReader(inputStreamReader);// 为字符流添加缓冲 String info = null; while ((info = bufferedReader.readLine()) != null) {// 循环读取客户端的信息 System.out.println("我是服务器,客户端说 : " + info); } socket.shutdownInput();// 关闭输入流 // 4:获取输出流,响应客户端的请求 outputStream = socket.getOutputStream(); printWriter = new PrintWriter(outputStream); printWriter.write("欢迎您"); printWriter.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 5:关闭资源 try { if (printWriter != null) { printWriter.close(); } if (outputStream != null) { outputStream.close(); } if (bufferedReader != null) { bufferedReader.close(); } if (inputStreamReader != null) { inputStreamReader.close(); } if (inputStream != null) { inputStream.close(); } if (socket != null) { socket.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
分析:
对服务器Server:
1\ 创建一个服务器Socket,即ServerSocket,绑定指定的端口,并监听此端口;
2\调用accept()方法监听,等待客户端的连接;
3\获取输入流,读取客户端所发送的信息;
4\获取输出流,响应客户端请求;
5\关闭资源。
在多线程情况下,ServerSocket不关闭,每一个ServerThead类继承Thread类,在run方法中实现上述3 4 5输入流输出流的相关操作以及资源的关闭,在Server中 有while(true)循环监听,等待客户端的连接。
对客户端Client,和对Server操作大同小异。自定义Socket指定其IP和端口号,在这里用的本机IP,通过InetAddress类获取。
Socket就是IP和端口号,而言指定具体的计算机上具体的线程,网络通信的基础是Socket,服务器监听具体的端口,一旦有客户端访问此端口,服务器也是创建Socket,在服务器 客户机上,两个Socket进行i/o流操作,便完成了网络通信。
2)UDP以数据报作为数据传输的载体
进行数据传输时首先要将传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口),然后将数据报发送出去。
//客户端
package com.zhao.UDPSocket; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; public class Client { public static void main(String[] args) throws IOException { /* * 向服务器端发送数据 */ //1:定义服务器的地址,端口号和数据 InetAddress address=InetAddress.getLocalHost(); int port=8800; byte[] bytes="用户名:admin".getBytes(); //2:创建数据报,包含发送的信息 DatagramPacket datagramPacket=new DatagramPacket(bytes, bytes.length, address, port); //3:创建DatagramSocket对象 DatagramSocket datagramSocket=new DatagramSocket(); //4:向服务器端发送数据报 datagramSocket.send(datagramPacket); /* * 接收服务器端响应数据 */ //1:创建数据报,用于接收服务器端响应的数据 byte[] data=new byte[1024]; DatagramPacket datagramPacket2=new DatagramPacket(data, data.length); //2:接收服务器端响应的数据 datagramSocket.receive(datagramPacket2); //3:读物数据 String str=new String(data,0,datagramPacket2.getLength()); System.out.println("我是客户端,服务器说: "+str); //4:关闭资源 datagramSocket.close(); } }
//服务端
package com.zhao.UDPSocket; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; /* * 服务器端,实现基于UDP的用户登录 */ public class Server { public static void main(String[] args) throws IOException { /* * 接收客户端发送的数据 */ //1:创建服务器端DatagramSocket,指定端口 DatagramSocket datagramSocket=new DatagramSocket(8800); //2:穿件数据报,用于接收客户端发送的数据 byte[] bytes=new byte[1024];//创建字节数组,指定接收的数据报的大小 DatagramPacket datagramPacket=new DatagramPacket(bytes, bytes.length); //3:接收客户端发送的数据 datagramSocket.receive(datagramPacket);//此方法在接收到数据报之前会一直阻塞 //4:读取数据 String info=new String(bytes,0,datagramPacket.getLength()); System.out.println("我是服务器,客户端说 :"+info); /* * 向客户端作出响应 */ //1:定义客户端的地址,端口号和数据 InetAddress address=datagramPacket.getAddress(); int port=datagramPacket.getPort(); byte[] data="欢迎您".getBytes(); //2:创建数据报, DatagramPacket datagramPacket2=new DatagramPacket(data, data.length, address, port); //3:响应客户端 datagramSocket.send(datagramPacket2); //4:关闭资源 datagramSocket.close(); } }
分析:
1\创建服务器端DatagramSocket,指定监听端口
2\创建数据报,用于接收客户端发送的数据
3\接收客户端发送的数据
4\读取数据
在客户端,DatagranPacket的创建需要指明服务器地址和端口号。
对于UDP来说,存信息的是数据报DatagramPacket,发信息和接收信息的是DatagramSocket。
TCP的socket有一个ServerSocket来负责监听宽口,Socket用来连接通信链路,信息是通过Socket和Socket建立I/O流来传递的。
UDP的socket有点像单向连接,没有通道,服务端和客户端是平等的,信息存在DatagramPacket数据报中,在其中指定IP和Port,直接send便可以了,目标DatagramSocket接收receive之后,把信息存入DatagramPacket,或者说 就是接收到一个DatagramSocket。