资讯专栏INFORMATION COLUMN

网络编程 -- 从 Socket 编程 到 OkHttp 框架

BWrong / 1922人阅读

摘要:如端口号分为标准既定的端口号其中知名端口号由组成。协议中,使用数据报为传输单位。用于直播等网速要求较高的应用端到端的通信类本机地址随机指定发送与接收数据报为和等网络层以上的包的单位。

前言

最近在做一个项目的时候,因为项目要求跨域连接。所以,使用了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 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
谢谢。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70466.html

相关文章

  • Android网络编程8之源码解析OkHttp中篇[复用连接池]

    摘要:构造函数默认空闲的最大连接数为个,的时间为秒通过构造函数可以看出默认的空闲的最大连接数为个,的时间为秒。实例化实例化是在实例化时进行的在的构造函数中调用了省略省略缓存操作提供对进行操作的方法分别为和几个操作。 1.引子 在了解OkHttp的复用连接池之前,我们首先要了解几个概念。 TCP三次握手 通常我们进行HTTP连接网络的时候我们会进行TCP的三次握手,然后传输数据,然后再释放连接...

    fasss 评论0 收藏0
  • 网络编程 - 收藏集 - 掘金

    摘要:个高级多线程面试题及回答后端掘金在任何面试当中多线程和并发方面的问题都是必不可少的一部分。目前在生产环基于的技术问答网站系统实现后端掘金这一篇博客将详细介绍一个基于的问答网站的实现,有详细的代码。 15 个高级 Java 多线程面试题及回答 - 后端 - 掘金在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多...

    justCoding 评论0 收藏0
  • 网络编程 - 收藏集 - 掘金

    摘要:个高级多线程面试题及回答后端掘金在任何面试当中多线程和并发方面的问题都是必不可少的一部分。目前在生产环基于的技术问答网站系统实现后端掘金这一篇博客将详细介绍一个基于的问答网站的实现,有详细的代码。 15 个高级 Java 多线程面试题及回答 - 后端 - 掘金在任何Java面试当中多线程和并发方面的问题都是必不可少的一部分。如果你想获得任何股票投资银行的前台资讯职位,那么你应该准备很多...

    selfimpr 评论0 收藏0

发表评论

0条评论

BWrong

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<