前言
最近在做一个项目的时候,因为项目要求跨域连接。所以,使用了Okhttp框架。其内部原理是基于 socket 网络编程的。因为自己在这方面比较薄弱,所以写这一篇文章进行相关的总结。
基础知识(参考 图解TCP/IP 与 深入理解计算机系统)
1、TCP/IP 参考模型
这位大佬写的很详细---点击即看
2、socket 套接字
每个套接字都是连接的一个端点,有相应的套接字地址。由一个IP地址与16位的整数端口组成.一个连接由两端的套接字地址唯一确定。叫套接字对。
如:(cliaddr:cliport, servaddr:servport)
端口号分为:
标准既定的端口号: 0~49151. 其中知名端口号由 0~1023 ** 组成。FTP 一般使用 21号端口号,HTTP 通信一般使用 80 号端口号。
动态分配的端口号: 49152~65535. 操作系统为之分配不同的端口号。然后应用程序使用时,由操作系统将连接建立。
3、java 中的网络编程类
InetAddress:用于标识网络上的硬件资源,主要是IP地址
URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据
Sockets:使用TCP协议实现的网络通信Socket相关的类
Datagram:使用UDP协议,将数据保存在用户数据报中,通过网络进行通信。UDP协议中,使用 数据报 为传输单位。
java 网络编程类介绍
1. InetAddress
InetAddress类用于标识网络上的硬件资源,标识互联网协议(IP)地址。
//获取本机的InetAddress实例
InetAddress address =InetAddress.getLocalHost();
//获取计算机名
address.getHostName();
//获取IP地址
address.getHostAddress();
//获取字节数组形式的IP地址,以点分隔的四部分
byte[] bytes = address.getAddress();
//获取其他主机的InetAddress实例
InetAddress address2 =InetAddress.getByName("其他主机名");
InetAddress address3 =InetAddress.getByName("IP地址");
2. URL
URL(Uniform Resource Locator)统一资源定位符,表示Internet上某一资源的地址,协议名:资源名称
- 基础使用
//创建一个URL的实例
URL myBlog =new URL("https://3dot141.cn");
URL url =new URL(myBlog,"/blogs/33521.html?username=3dot141#test");//?表示参数,#表示锚点
url.getProtocol();//获取协议
url.getHost();//获取主机
url.getPort();//如果没有指定端口号,根据协议不同使用默认端口。此时getPort()方法的返回值为 -1
url.getPath();//获取文件路径
url.getFile();//文件名,包括文件路径+参数
url.getRef();//相对路径,就是锚点,即#号后面的内容
url.getQuery();//查询字符串,即参数
- 读取网页内容
//使用URL读取网页内容
//创建一个URL实例
URL url =new URL("http://www.baidu.com");
InputStream is = url.openStream();//通过openStream方法获取资源的字节输入流
InputStreamReader isr =newInputStreamReader(is,"UTF-8");//将字节输入流转换为字符输入流,如果不指定编码,中文可能会出现乱码
BufferedReader br =newBufferedReader(isr);//为字符输入流添加缓冲,提高读取效率
String data = br.readLine();//读取数据
while(data!=null){
System.out.println(data);//输出数据
data = br.readerLine();
}
br.close();
isr.colose();
is.close();
3. Socket
首先介绍下关于 linux 下的套接字连接原理,帮助理解
下面介绍java 下 Socket的使用
1.Socket 的构造方法
(1)Socket()
(2)Socket(InetAddress address, int port)throws UnknownHostException,IOException
// 设定远程服务器地址与客户端地址
(3)Socket(InetAddress address, int port, InetAddress localAddr, int localPort)throws IOException
(4)Socket(String host, int port) throws UnknownHostException,IOException
// 设定远程服务器地址与客户端地址
(5)Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException
2.获取Socket信息
1. getInetAddress():获得远程服务器的IP地址。
2. getPort():获得远程服务器的端口。
3. getLocalAddress():获得客户本地的IP地址。
4. getLocalPort():获得客户本地的端口。
5. getInputStream():获得输入流。如果Socket还没有连接,或者已经关闭,或者已经通过shutdownInput()方法关闭输入流,那么此方法会抛出IOException。
6. getOutputStream():获得输出流。如果Socket还没有连接,或者已经关闭,或者已经通过shutdownOutput()方法关闭输出流,那么此方法会抛出IOException。
3.Socket 状态
- 关闭状态
1. close()
// 状态测试方法
1. isClosed()
2. IsConnected()
3. isBound()
- 半关闭状态
1. shutdownInput()
2. shutdownOutput()
// 状态测试方法
1. isInputShutDown()
2. isOutputShutdown()
4.Socket 使用实例
以上就是 Socket 类的基本方法。 下面让我们进入实战,来看一下,Socket 类如何使用
- 服务器端
/**
* 基于TCP协议的Socket通信,实现用户登录,服务端
*/
//1、创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
ServerSocket serverSocket =newServerSocket(33521);//1024-65535的某个端口
//2、调用accept()方法开始监听,等待客户端的连接
Socket socket = serverSocket.accept();
//3、获取输入流,并读取客户端信息
InputStream is = socket.getInputStream();
InputStreamReader isr =newInputStreamReader(is);
BufferedReader br =newBufferedReader(isr);
String info =null;
while((info=br.readLine())!=null){
System.out.println("我是服务器,客户端说:"+info);
}
socket.shutdownInput();//关闭输入流
//4、获取输出流,响应客户端的请求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("欢迎您!");
pw.flush();
//5、关闭资源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
- 客户端
//客户端
//1、创建客户端Socket,指定服务器地址和端口
Socket socket =newSocket("localhost",33521);
//2、获取输出流,向服务器端发送信息
OutputStream os = socket.getOutputStream();//字节输出流
PrintWriter pw =newPrintWriter(os);//将输出流包装成打印流
pw.write("用户名:3dot141;密码:hahah");
pw.flush();
socket.shutdownOutput();
//3、获取输入流,并读取服务器端的响应信息
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while((info=br.readLine())!null){
System.out.println("我是客户端,服务器说:"+info);
}
//4、关闭资源
br.close();
is.close();
pw.close();
os.close();
socket.close();
- 结果
我是服务器,客户端说:用户名:3dot141;密码:hahah
我是客户端,服务器说:欢迎您!
-
多线程中的运用
- 服务器端创建ServerSocket,使用while(true)循环调用accept()等待客户端连接
- 客户端创建一个socket并请求和服务器端连接
- 服务器端接受请求,创建socket与该客户建立专线连接
- 建立连接的两个socket在一个单独的线程上对话
- 服务器端继续等待新的连接
public class ServerThread implements runnable{
//服务器线程处理
//和本线程相关的socket
Socket socket =null;
//
public ServerThread(Socket socket){
this.socket = socket;
}
publicvoid run(){
//服务器处理代码
}
}
//服务器代码
ServerSocket serverSocket =newServerSocket(33521);
Socket socket =null;
int count =0;//记录客户端的数量
while(true){
socket = serverScoket.accept();
ServerThread serverThread =new ServerThread(socket);
serverThread.start();
count++;
System.out.println("客户端连接的数量:"+count);
}
4. UDP 编程
1. 简单介绍
UDP 是面向无连接的协议,反应迅速,适用于适时场景,但是丢包后不能发现。
用于 直播等网速要求较高的应用
DatagramSocket
端到端的通信类.
//本机地址
// 随机
DatagramSocket()
// 指定
DatagramSocket(int port, InetAddress)
// 发送与接收
send(DatagramPacket)
receive(DatagramPacket)
DatagramPacket
数据报, 为 IP 和 UDP 等网络层以上的包的单位 。虽然这些都是包,但不同的层拥有不同的称呼。数据链路层中 叫 帧 , TCP 则表示 为 段 .
方法 :
// 构造方法
// 接收时
DatagramPacket(byte[] buf, int length);
// 发送时
DatagramPacket(byte[] buf, int length, InetAddress iAdrr, int Port);
// 使用方法
// 用于服务器获得 客户端地址
getAddress()
// 用于服务器获得 客户端接口
getPort()
2. 基本使用
- 服务器端
//服务器端,实现基于UDP的用户登录
//1、创建服务器端DatagramSocket,指定端口
DatagramSocket socket =new datagramSocket(33521);
//2、创建数据报,用于接受客户端发送的数据
byte[] data =newbyte[1024];//
DatagramPacket packet =newDatagramPacket(data,data.length);
//3、接受客户端发送的数据
socket.receive(packet);//此方法在接受数据报之前会一致阻塞
//4、读取数据
String info =newString(data,o,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();
- 客户端
//客户端
//1、定义服务器的地址、端口号、数据
InetAddress address =InetAddress.getByName("localhost");
int port =33521;
byte[] data ="用户名:3dot141;密码:hahah".getBytes();
//2、创建数据报,包含发送的数据信息
DatagramPacket packet = newDatagramPacket(data,data,length,address,port);
//3、创建DatagramSocket对象
DatagramSocket socket =newDatagramSocket();
//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();
OkHttp 框架
在项目中,我对 OkHttp 进行了简单的封装,基本满足我在项目中的需要。
下面贴上我的 工具类
public class OkhttpUtil {
public static final MediaType JSON = MediaType.parse("application/json;charset=UTF-8");
public static String doGet(String url) throws IOException {
OkHttpClient client = new OkHttpClient();
Request get = new Request.Builder().url(url).build();
Response response = client.newCall(get).execute();
return response.body().string();
}
public static String doGet(String url, Map<String, String> map) throws IOException {
OkHttpClient client = new OkHttpClient();
String newUrl = url;
if (map != null) {
int loop = 0;
for (String key : map.keySet()) {
if (loop == 0) {
newUrl = newUrl + "?" + key + "=" + map.get(key);
} else {
newUrl = newUrl + "&" + key + "=" + map.get(key);
}
loop = 1;
}
}
Request get = new Request.Builder().url(newUrl).build();
Response response = client.newCall(get).execute();
return response.body().string();
}
public static String doPost(String url, String requestBody) throws IOException {
OkHttpClient client = new OkHttpClient();
Request post = new Request.Builder().url(url).post(RequestBody.create(JSON, requestBody)).build();
Response response = client.newCall(post).execute();
if (!response.isSuccessful()) {
throw new IOException("没能得到数据" + response);
}
return response.body().string();
}
}
如果有对 okhttp 框架感兴趣的,可以参阅下面的网址。我就不献丑了。
okhttp 源码解析
okhttp 使用教程
结语
路漫漫其修远兮,吾将上下而求索。
在程序员的道路上,我还只是一个刚上路的小学生,怀着对代码世界的向往,砥砺前行。
stay hungry, stay foolish
与诸君共勉。
您的每一次点赞,关注都是对我的一种激励。
我的个人博客 -- killCode
谢谢。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。