java socket编程
Java Socket编程----通信是这样炼成的
前言:socket
做了什么
从一个概况的层面来说,Socket
是网络通信的端点,可以把一个Socket
绑定在一个ip+端口
上标识一个进程
当应用进程想通过Socket
传输数据,实际上是把Socket
绑定在了网卡驱动程序上,这样网卡就知道从这个ip+端口进来的数据都要给我这个应用进程
针对网络通信的不同层次,Java提供了不同的API,其提供的网络功能有四大类
1. 关于IP地址 – InetAddress
用于标识网络上的硬件资源,标识互联网协议(IP)地址。
//获取本机的InetAddress实例
InetAddress address =InetAddress.getLocalHost();//返回本机
//获取其他主机的InetAddress实例
InetAddress address2 =InetAddress.getByName("其他主机名");//根据域名得到InetAddress对象
InetAddress address3 =InetAddress.getByName("IP地址");//根据IP地址得到InetAddress对象
address.getHostName();//获取计算机名
address.getHostAddress();//获取IP地址
byte[] bytes = address.getAddress();//获取字节数组形式的IP地址,以点分隔的四部分
2. 关于IP+端口 – InetSocketAddress
此类实现 IP 套接字地址(IP 地址 + 端口号)。它还可以是一个对(主机名 + 端口号),在此情况下,将尝试解析主机名。如果解析失败,则该地址将被视为未解析地址,但是其在某些情形下仍然可以使用,比如通过代理连接
//获取本机的InetAddress实例
InetSocketAddress address =new InetSocketAddress("127.0.0.1",8080);//返回本机
InetSocketAddress address =new InetSocketAddress("localhost",8080);//返回本机
address.getHostName();//获取计算机名
address.getAddress();//获取IP地址
address.getPort();//获取端口号
3. URL类
URL:统一资源定位符,表示是一个地址,包括协议,域名,端口号,资源文件名 http://www.google.com:80/index.html
URI:“A Uniform Resource Identifier (URI) 是一个紧凑的字符串用来标示抽象或物理资源。”
“A URI 可以进一步被分为定位符、名字或两者都是. 术语“Uniform Resource Locator” (URL) 是URI的子集, 除了确定一个资源,还提供一种定位该资源的主要访问机制(如其网络“位置”)
URL是URI的一种,URI是URL的抽象,URL具体化了URI的某一部分,也就是说URL不仅表示了要请求资源的名称,而且确定了要访问资源的地址
//创建一个URL的实例
URL url=new URL("http://www.baidu.com/index.html?username=tom#test");
//或者
//URL baidu =new URL("http://www.baidu.com");
//URL url =new URL(baidu,"/index.html?username=tom#test");//?表示参数,#表示锚点
url.getProtocol();//获取协议
url.getHost();//获取主机
url.getPort();//如果没有指定端口号,根据协议不同使用默认端口。此时getPort()方法的返回值为 -1
url.getPath();//获取文件路径
url.getFile();//文件名,包括文件路径+参数
url.getRef();//相对路径,就是锚点,即#号后面的内容
url.getQuery();//查询字符串,即参数
3.1 使用URL读取网页内容
通过URL对象的openStream()
方法可以得到指定资源的输入流,通过流能够读取或访问网页上的资源
其实和也就是爬虫的基本原理
① 获取URL
②下载资源
③ 对资源分析(正则表达式)
④数据抽取存储
//使用URL读取网页内容
//创建一个URL实例
URL url =new URL("http://www.baidu.com");
InputStream is = url.openStream();//通过openStream方法获取资源的字节输入流
InputStreamReader isr =new InputStreamReader(is,"UTF-8");//将字节输入流转换为字符输入流,如果不指定编码,中文可能会出现乱码
BufferedReader br =new BufferedReader(isr);//为字符输入流添加缓冲,提高读取效率
String data = br.readLine();//读取数据
while(data!=null){
System.out.println(data);//输出数据
data = br.readerLine();
}
br.close();
isr.colose();
is.close();
可以使用HttpURLConnection模拟浏览器访问
4. TCP编程
tcp是传输层协议,其上层的应用有Http等
TCP协议是面向连接的、可靠的、有序的、以字节流的方式发送数据,通过三次握手方式建立连接,形成传输数据的通道,在连接中进行大量数据的传输,效率会稍低
4.1 Java中基于TCP协议实现网络通信的类
我们开发的网络应用位于应用层,而TCP, UDP是传输层协议,那么在应用层怎么使用传输层的协议呢,是通过套接字来进行分离的,套接字就是传输层为应用层开的小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据
TCP以IO流来交流
1. Socket
通信的步骤
① 创建ServerSocket
和Socket
② 打开连接到Socket
的输入/输出流
③ 按照协议对Socket
进行读/写操作
④ 关闭输入输出流、关闭Socket
2. 服务器端的ServerSocket
类
① 创建ServerSocket
对象,绑定监听端口
② 通过accept()
方法监听客户端请求
③ 连接建立后,通过输入流读取客户端发送的请求信息
④ 通过输出流向客户端发送信息
⑤ 关闭相关资源
import java.io.*;
import java.net.*;
import java.applet.Applet;
public class TalkServer{
public static void main(String args[]) {
try{
ServerSocket server=null;
try{
server=new ServerSocket(4700);
//创建一个ServerSocket在端口4700监听客户请求
}catch(Exception e) {
System.out.println("can not listen to:"+e);
//出错,打印出错信息
}
Socket socket=null;
try{
socket=server.accept();
//使用accept()阻塞等待客户请求,有客户
//请求到来则产生一个Socket对象,并继续执行
}catch(Exception e) {
System.out.println("Error."+e);
//出错,打印出错信息
}
String line;
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket对象得到输入流,并构造相应的BufferedReader对象
PrintWriter os=new PrintWriter(socket.getOutputStream());
//由Socket对象得到输出流,并构造PrintWriter对象
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系统标准输入设备构造BufferedReader对象
System.out.println("Client:"+is.readLine());
//在标准输出上打印从客户端读入的字符串
line=sin.readLine();
//从标准输入读入一字符串
while(!line.equals("bye")){
//如果该字符串为 "bye",则停止循环
os.println(line);
//向客户端输出该字符串
os.flush();
//刷新输出流,使Client马上收到该字符串
System.out.println("Server:"+line);
//在系统标准输出上打印读入的字符串
System.out.println("Client:"+is.readLine());
//从Client读入一字符串,并打印到标准输出上
line=sin.readLine();
//从系统标准输入读入一字符串
} //继续循环
os.close(); //关闭Socket输出流
is.close(); //关闭Socket输入流
socket.close(); //关闭Socket
server.close(); //关闭ServerSocket
}catch(Exception e){
System.out.println("Error:"+e);
//出错,打印出错信息
}
}
}
3. 客户端的Socket
类
① 创建Socket
对象,指明需要连接的服务器的地址和端口号
② 连接建立后,通过输出流想服务器端发送请求信息
③ 通过输入流获取服务器响应的信息
④ 关闭响应资源
import java.io.*;
import java.net.*;
public class TalkClient {
public static void main(String args[]) {
try{
Socket socket=new Socket("127.0.0.1",4700);
//向本机的4700端口发出客户请求
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系统标准输入设备构造BufferedReader对象
PrintWriter os=new PrintWriter(socket.getOutputStream());
//由Socket对象得到输出流,并构造PrintWriter对象
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket对象得到输入流,并构造相应的BufferedReader对象
String readline;
readline=sin.readLine(); //从系统标准输入读入一字符串
while(!readline.equals("bye")){
//若从标准输入读入的字符串为 "bye"则停止循环
os.println(readline);
//将从系统标准输入读入的字符串输出到Server
os.flush();
//刷新输出流,使Server马上收到该字符串
System.out.println("Client:"+readline);
//在系统标准输出上打印读入的字符串
System.out.println("Server:"+is.readLine());
//从Server读入一字符串,并打印到标准输出上
readline=sin.readLine(); //从系统标准输入读入一字符串
} //继续循环
os.close(); //关闭Socket输出流
is.close(); //关闭Socket输入流
socket.close(); //关闭Socket
}catch(Exception e) {
System.out.println("Error"+e); //出错,则打印出错信息
}
}
}
4.2 应用多线程实现服务器与多客户端之间的通信
① 服务器端创建ServerSocket
,循环调用accept()
等待客户端连接
② 客户端创建一个socket
并请求和服务器端连接
③ 服务器端接受客户端请求,创建socket
与该客户建立专线连接
④ 建立连接的两个socket
在一个单独的线程上对话
⑤ 服务器端继续等待新的连接
//服务器线程处理
//和本线程相关的socket
Socket socket =null;
//
public serverThread(Socket socket){
this.socket = socket;
}
publicvoid run(){
//服务器处理代码
}
//============================================
//服务器代码
ServerSocket serverSocket =new ServerSocket(10086);
Socket socket =null;
int count =0;//记录客户端的数量
while(true){
socket = serverScoket.accept();
ServerThread serverThread =newServerThread(socket);
serverThread.start();
count++;
System.out.println("客户端连接的数量:"+count);
}
5. UDP编程
UDP协议(用户数据报协议)是无连接的、不可靠的、无序的,速度快
进行数据传输时,首先将要传输的数据定义成数据报(Datagram),大小限制在64k,在数据报中指明数据索要达到的Socket(主机地址和端口号),然后再将数据报发送出去
由于是基于数据报的,一切以包裹为中心,在传输的时候不使用IO流而是将每个数据发送单元统一封装成数据包的方式,发送方将数据包发送到网络中,数据包在网络中寻找他的目的地
- DatagramPacket类: 表示数据报包
- DatagramSocket类: 进行端到端通信的类
5.1 服务器端实现步骤
① 创建DatagramSocket,指定自己的端口号,创建接收端,被人发要往这个上面发
② 准备容器 封装成DatagramPacket包裹
③ 阻塞式接受客户端发送的数据信息
④ 读取数据
//服务器端,实现基于UDP的用户登录
//1. 创建服务器端DatagramSocket,指定端口
DatagramSocket socket =new datagramSocket(10010);
//2. 创建数据报,用于接受客户端发送的数据
byte[] data =new byte[1024];//1k
//2. 准备容器 封装成DatagramPacket包裹
DatagramPacket packet =newDatagramPacket(data,data.length);
//3. 接受客户端发送的数据
socket.receive(packet);//此方法在接受数据报之前会一致阻塞
//4. 读取数据
String info =new String(data,0,data.length);
System.out.println("我是服务器,客户端告诉我"+info);
//=========================================================
//向客户端响应数据
//1、定义客户端的地址、端口号、数据
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎您!".geyBytes();
//发生方需要指定发送地址
//2、创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(data2,data2.length,address,port);
//3、响应客户端
socket.send(packet2);
//4、关闭资源
socket.close();
5.2 客户端实现步骤
① 定义发送信息
② 创建DatagramPacket,封装字节数组,包含将要发送的信息,指定对方的ip和端口
③ 创建DatagramSocket,创建发送端
④ 发送数据
//客户端
//1、定义服务器的地址、端口号、数据
InetAddress address =InetAddress.getByName("localhost");
int port =10010;
byte[] data ="用户名:admin;密码:123".getBytes();//数据一定要转为字符数组再打包
//2、创建数据报,包含发送的数据信息,要发送的地址
DatagramPacket packet = new DatagramPacket(data,data.length,address,port);
//3、创建DatagramSocket对象
DatagramSocket socket =new DatagramSocket();
//4、向服务器发送数据
socket.send(packet);
//接受服务器端响应数据
//======================================
//1、创建数据报,用于接受服务器端响应数据
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2,data2.length);
//2、接受服务器响应的数据
socket.receive(packet2);
String raply = new String(data2,0,packet2.getLenth());
System.out.println("我是客户端,服务器说:"+reply);
//4、关闭资源
socket.close();