Java网络编程

一级目录

二级目录

三级目录

1.网络编程基础

  • 什么是网络?:把分布在不同区域的计算机使用专业通信线路连接起来,实现资源数据共享

  • 网络编程的目的:通过通讯协议实现数据传输

  • 什么是www?:

    • 全球信息网(WWW,World Wide Web)
  • 网络通信的要素:

    1. ip和端口(也就是所谓的Socket)

    2. 协议

通信协议三部分:
语义部分:用于决定双方对话的类型
语法部分:用于决定双方对话的格式
变换规则:用于决定通信双方的应答关系

参考模型:

OSI参考模型:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层 TCP/IP分层模型:应用层、传输层、网络层、物理+数据链路层

在这里插入图片描述

1.1IP和端口号

1.1.1IP地址

IP地址:用于唯一标识网络中的一个通信实体(主机、打印机、路由器的端口)

在这里插入图片描述

IP地址的特性:

​ 32位的整数(32bit),将这32位分为4个8位二进制数,每个8位二进制整数转换为十进制整数的取值是0~255

​ 同一个域中IP地址不允许重复

​ 数据传输过程中IP地址不允许改变

​ IP地址分为ABCDE五类

  • A类:10.0.0.0~10.255.255.255

  • B类:172.16.0.0~172.31.255.255

  • C类:192.168.0.0~192.168.255.255

1.1.2端口

端口:当源IP将数据包发送到目的IP的时候,常常是通过程序接收的,但是一个通信实体可以有多个通信程序可以提供网络服务,端口号就是为了唯一确定一个通信程序的

端口的特性:

​ 端口是应用程序与外界交流的出入口

​ 同一台机器上不能存在两个程序使用同一个端口号

​ 端口号可以从0~65535,通常分为三类:

  • 公认端口:从0~1023,紧密绑定一些特殊服务

  • 注册端口:从1024~49151,应用程序通常应该使用这个范围的端口

  • 动态和/或私有端口:从49152~65535,这些端口是应用程序使用的动态端口,但是应用程序一般不会主动使用这些端口

1.2通讯协议

1.2.1 TCP/IP协议

TCP/IP通信协议是一种可靠的网络协议,作用在传输层(TCP)和网络层(IP);使用该协议在通信时需要先建立一个Socket,Socket负责进行IP+端口的连接,使两台主机之间能够进行连接,通过该协议就能进行数据传输了

TCP报文格式:

在这里插入图片描述

TCP报文字段解析:

  • 序号:Seq序号,占用32位,用来标识从TCP源主机向目标主机发送的字节流,由源主机发送数据时进行标识

  • 确认号:Ack序号,占32位,用来标识目标主机是否接收到数据,由目标主机确认是否生效,如果Ack=Seq+1,且ACK标志位=1,则确认连接成功

  • 标志位(Flags):URG、ACK、PSH、RST、SYN、FIN

    • ACK:确认号标志位,ACK=1时确认序号才生效

    • URG:紧急指针

    • PSH:接收方应尽快将这个报文交给应用层

    • RST:重置连接

    • SYN:发起一个新的连接

    • FIN:释放一个连接

三次握手(建立TCP连接):

在这里插入图片描述

三次握手可以是客户端发起,也可以是服务端发起,但是一般是客户端发起的请求

  1. 客户端向服务端发送一段TCP报文(我可以发数据给你吗)

    • 此时SYN生效–>请求建立新连接
    • 序号Seq=X(X一般=1)
    • 客户端进入SYN-SENT阶段
  2. 服务端接收客户端的TCP报文(可以)

    • 获取SYN和Seq后,表示服务端能正常接收客户端的数据,告诉客户端同意创建连接
    • Seq=Y(将服务器发送的TCP报文的序号赋值给Seq)
    • 确认号Ack=X+1(将客户端发送过来的序号+1作为确认号,表示确认接收到了序号)
    • ACK标志位=1(确认号有效)
    • 服务端进入SYN-RCVD阶段
  3. 客户端接收到服务器的确认收到数据的TCP报文(准备发送数据)

    • 确认服务端发送的ACK标志位为1(同意发送的信号),并把ACK=1发送给服务端(我马上就开始发送数据了)
    • 序号=Ack=X+1,将服务器发送过来的Ack作为序号发送给服务端
    • 确认号Ack=Y+1,将服务端发送的序号+1作为自己的确认号发送给服务端
    • 随后客户端和服务端进入ESTAB-LISHED阶段–数据就可以开始传输了

在这里插入图片描述

四次挥手(TCP连接的释放):

在这里插入图片描述

  1. 首先客户准备释放连接,向服务端发送一段TCP报文
  • FIN=1表示:请求释放连接

  • 序号为seq=u;

  • 随后客户端进入FIN-WART-1阶段—等待请求(客户端可以接收服务端的数据,但是客户端不再发送数据到服务端–TCP报文除外)

  1. 服务端接收到客户端发出的TCP报文后,确认客户端的释放连接需求,随后结束ESTAB-LISHED阶段,进入CLOSE-WAIT,并返回一段TCP报文

    • ACK=1表示:接收到客户端发送的释放连接请求
    • 序号为seq=v
    • 确认号为Ack=u+1,表示:将序号seq值+1作为本段报文的确认号Ack的值
    • 服务器开始准备释放服务端到客户端的连接
    • 此时客户端结束了FIN-WAIT-1阶段,进入FIN-WAIT-2阶段
  2. 当服务器确认可以关闭服务器到客户端的连接(经历了CLOSED-WAIT阶段)后(准备就绪),发送一段TCP报文给客户端

    • 标记位为FIN,ACK,表示:已经准备好释放连接了,
    • 序号为seq=w
    • 确认号为Ack=u+1,表示:在收到客户端报文的基础上,将其seq值+1作为本段报文的确认号Ack的值
    • 随后服务器结束ClOSE-WAIT阶段,进入LAST-ACK阶段–停止服务端向客户端发送数据
  3. 客户端接收到服务端发出的TCP报文,确认了服务端已做好释放连接的准备,立即就会结束FIN-WAIT-2阶段,进入TIME-WAIT阶段,并向服务端发送一段报文

    • 标记位ACK,表示:接收到服务端准备释放连接的信号
    • 序号为seq=u+1,表示:收到服务端的报文基础上,将其确认号Ack的值作为本段报文的序号值
    • 确认号为Ack=w+1,表示:在收到服务端报文的基础上,将其序号seq的值作为本段报文的确认号的值
    • 随后客户端来时在TIME-WAIT阶段等待2MSL(Maximum Segment Lifetime),服务端在接收到报文后进入CLOSED阶段
    • 客户端等待2MSL后结束TIME-WAIT阶段进入CLOSED阶段完成四次挥手

Java实现TCP发送数据步骤

TCP发送数据的步骤:
    1.创建客户端的Socket对象(Socket)
    2.获取输出流,写数据
    3.释放资源

测试示例

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class ClientDemo {
  public static void main(String[] args) throws IOException, IOException {
  //创建客户端的Socket对象(Socket)
    Socket s=new Socket(InetAddress.getByName("192.168.0.66"),10000);
    //获取输出流,写数据
    OutputStream os=s.getOutputStream();
    os.write("hello,tcp,我来了".getBytes());
    //释放资源
    s.close();
  }
}
TCP接收数据的步骤:
    1.创建服务器端的Socket对象(ServerSocket)
    2.监听客户端连接,返回一个Socket对象
    3.获取输入流,读数据,并显示在控制台
    4.释放资源

测试示例

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
  public static void main(String[] args) throws IOException {
    // TODO Auto-generated method stub
    //创建服务器的Socket对象(ServerSocket)
    ServerSocket ss=new ServerSocket(10000);
    //侦听要连接到此套接字并接受它
    Socket s=ss.accept();
    //获取输入流,读数据,并显示在控制台
    InputStream is=s.getInputStream();
    byte[] bys=new byte[1024];
    int len=is.read(bys);
    String data=new String(bys,0,len);
    System.out.println("数据是:"+data);
    //释放资源
    s.close();
    ss.close();
  }  
}

1.2.2 UDP协议

UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket对象只是发送和接收数据
因此对于基于UDP协议的通信双方而言没有所谓的客户端和服务器的概念
Java提供了DatagramSocket类作为基于UDP协议的Socket

TCP:传输控制协议UDP:用户数据报协议
必须建立连接,形成传输通道将数据,源,目的封装成数据包,不是必须建立连接
传输前,采用“三次握手”,是可靠的数据包大小是64K
TCP通信位于两个进程(C/S)因无需连接,不可靠
可以传输大量数据传完数据无需释放资源,效率高
传输需要释放已建立连接,效率低

UDP发送数据的步骤:
1.创建发送端的Socket对象(DatagramSocket)
2.创建数据,把数据打包
3.调用DatagramSocket对象的方法发送数据
4.关闭发送端

测试示例

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class SendDemo {
//public class DatagramSocket//用于发送和接收数据
//extends Object
//implements Closeable
  public static void main(String[] args) throws IOException {
    //****创建发送端的Socket对象DatagramSocket
    DatagramSocket ds=new DatagramSocket();
    //****创建数据,并把数据打包
    //DatagramPacket(byte[] buf,int length,InetAddress address,int port)
    //****构造一个数据包,发送长度为length的数据包到指定主机上的指定端口号
//    byte[] bys="hello,udp,我来了".getBytes();
//    int len=bys.length;
//    InetAddress address=InetAddress.getByName("127.0.0.1");
//    int port=10086;
//    DatagramPacket dp=new DatagramPacket(bys,len,address,port);
    byte[] bys="hello,udp,我来了".getBytes();
    DatagramPacket dp=new DatagramPacket(bys,bys.length,InetAddress.getByName("127.0.0.1"),10086);
    //****调用DatagramSocket对象的方法发送数据
    //void send(DatagramSocket p)从此套接字发送数据报包
    ds.send(dp);
    //****关闭发送端
    ds.close();
  }
}

UDP接收数据的步骤:

  1. 创建接收端的Socket对象(DatagramSocket)
  2. 创建一个数据包,用于接收数据
  3. 调用DatagramSocket对象的方法用于接收数据
  4. 解析数据包,并把数据输出在控制台上
  5. 关闭数据

测试示例

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class ReceiveDemo {
  public static void main(String[] args) throws IOException {
    //1.创建接收端的Socket对象(DatagramSocket)
    //DatagramSocket(int port)构造数据包套接字,并将其绑定在本地主机指定的端口上
    DatagramSocket ds=new DatagramSocket(10086);
    //2.创建一个数据包,用于接收数据
    //DatagramPacket(byte[] buf,int length)构造一个
    byte[] bys=new byte[1024];
    DatagramPacket dp=new DatagramPacket(bys, bys.length);
//  3.调用DatagramSocket对象的方法用于接收数据
    ds.receive(dp);
//  4.解析数据包,并把数据输出在控制台上
//  byte[] getData()返回数据缓冲区
    byte[] datas=dp.getData();//得到的是数据缓冲区
    int len=dp.getLength();//用于获取数据长度,不使用此方法,则输出的数据后面会跟上一条空格
    String dataString=new String(datas,0,len);
    System.out.println("数据是:"+dataString);
//  5.关闭数据
    ds.close();
  }
}

注意:必须先启动服务器再启动客户端

  • @date 2019年8月15日下午1:11:14 描述:UDP发送数据:数据来源于键盘录入,直到输入的数据是886,发送数据结束 UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环

1.3 HTTP和HTTPS

http:超文本传输协议,是通过TCP/IP通信协议来传递数据的,使用的是80端口

https:超文本传输安全协议,在HTTP协议的基础上通过传输加密和身份认证(TLS/SSL)保证了传输过程的安全性,使用的是443端口

2.Java中的网络支持

java.net包中为网络支持提供了URL和URLConnection等类以编程方式访问web服务的功能

URLDecoder和URLEncoder提供了为普通字符串和网络编码(application/x-www-from-urlencoded MIME)字符串相互转换的静态方法

2.1 InetAddress

和file类类似,讲一个Host主机地址传递给InetAddress类,这样的一个InetAddress的实例就指代一个IP地址
在这里插入图片描述

常用方法:
1.InetAddress getByName(String host):根据主机获取对应的InetAdress对象实例
2.InetAddress getByAddress(byte[] addr):byte数组中存储4个IP段,IP段值不能超过127,byte不能存储,只适用于部分A类和部分B类
3.String getCanonicalHostName():获取此IP地址的全限定域名--对应的是Hosts文件中的信息
4.String getHostAddress():返回该InetAddress对象对应的IP地址字符串(以字符串形式)
5.String getHostName():获取此IP的主机名
6.static InetAddress getLocalHost():获取本机IP地址对应的InetAddress实例
7.boolean isReachable(long timeout):连接该IP地址,超出timeout时间,认为连接超时,返回false,连接成功返回true
//方法测试
//本机地址:172.23.127.177
InetAddress ip = InetAddress.getByName("172.23.127.175");//true
//这个只能使用本机回环地址,很呆不建议使用
InetAddress ip2 = InetAddress.getByAddress(new byte[]{127,0,0,1});//true
//是否可达
System.out.println(ip.isReachable(2000));
System.out.println(ip2.isReachable(2000));
//返回此IP的全限定域名
String canonicalHostName = ip2.getCanonicalHostName();//activate.navicat.com:这个地方是因为host文件中标注了本机回环地址的域名
System.out.println(canonicalHostName);
System.out.println(InetAddress.getLocalHost());//DESKTOP-KHT7LHO/172.23.127.177

URLDecoder和URLEncoder

encode()编码

decode()解码

try {
    String encode = URLEncoder.encode("这是一封给未来的信", "UTF-8");
    String decode = URLDecoder.decode(encode, "UTF-8");
    System.out.println("编码结果:"+encode+"解码结果:"+decode);
    //%E8%BF%99%E6%98%AF%E4%B8%80%E5%B0%81%E7%BB%99%E6%9C%AA%E6%9D%A5%E7%9A%84%E4%BF%A1
    //这是一封给未来的信
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

2.2 URL以及涉及的类

URL:统一资源定位符,是指向网络资源的指针,资源可以是文件、目录,也可以是复杂对象的引用(如数据库或搜索引擎的查询)

URL由协议名、主机、端口和资源组成(<传输协议>://<主机号>:<端口号 http 默认 80>/<文件名>)

protocol://host:port/resourceName

例如:https://www.bilibili.com/video

2.2.1 URL类

URL设计成一个类,表示URL的对象

String getFile():获取该URL的资源名
String getHost():获取该URL的主机名
String getPath():获取该URL的路径部分
int getPort():获取该URL的端口号
String getQuery():获取该URL的查询字符串部分
URLConnection openConnection():返回一个URLConnection对象,它代表了与URL所引用的远程对象的连接
InputStream openStream():打开与此URL的连接,并返回一个用于读取该URL资源的InputStream
2.2.2 URLConnection和HttpConnection

URLConnection:表示应用程序与URL之间的通信连接

HttpURLConnection:表示应用程序与URL之间的HTTP连接

程序可以通过URLConnection对象实例向该URL发送请求、读取URL引用的资源

创建一个URL的连接,步骤如下:

  1. 通过调用URL对象的openConnection()方法创建URLConnection对象
  2. 设置URLConnection的参数和普通请求属性
  3. 如果只是发送GET请求,则使用connect()方法建立和远程资源直接的实际连接即可
    如果需要发送POST请求,则需要获取URLConnection实例对应的输出流来发送请求参数。
  4. 远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源数据
setAllowUserInteraction():设置该URLConnection的allowUserInteraction请求头字段的值
setDoInput():设置该URLConnection的doInput请求头字段的值
setDoOutput():设置该URLConnection的doOutput请求头字段的值
setIfModifiedSince():设置该URLConnection的ifModifiedSince请求头字段的值
setUseCaches():设置该URLConnection的useCaches请求头字段的值
setRequestProperty(String key,String value):设置该URLConnection的key请求头字段的值为value
addRequesrProperty(String key,String value):为该URLConnection的key请求头字段增加value值,该方法不会覆盖,而是追加
Object getContent():获取该URLConnection的内容
String getHeaderFiled(String name):获取指定响应头字段的值
getInputStream():获取该URLConnection对应的输入流,用于获取URLConnection中的响应内容
getOutputStream():获取该URLConnection对应的输出流,用于向URLConnection中发送请求参数


//针对getHeaderFiled()新增了如下方法:
getContentEncoding():获取content的连接编码值
getContentLength():获取content长度的响应头字段的值
getContentType():获取连接类型的响应头字段的值
getDate():获取date响应头字段信息
getExpiration():获取expires响应头字段信息
getLastModified():获取最后修改记录的响应头字段信息
package com.carl.tool;

import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * @author :Carl_蔡先生
 * @version :JDK1.8
 * @description:TODO
 * @date :2021/8/26 10:14
 * @month_name :8月
 * @week :
 */
public class MultithreadDownload {
    //定义下载资源的路径
    private String srcPath;
    //指定下载文件存放的目标路径
    private String destPath;
    //指定线程数
    private int threadNum;
    //定义下载的线程对象
    private DownThread[] threads;
    //指定可下载文件的总大小
    private int fileSize;
    
    public MultithreadDownload(String srcPath, String destPath, int threadNum) {
        this.srcPath = srcPath;
        this.destPath = destPath;
        this.threadNum = threadNum;
        threads=new DownThread[threadNum];
    }
    //下载
    public void download() throws IOException {
          //1.创建URL对象
            URL url=new URL(srcPath);
          //2.开启该URL的远程连接
            HttpURLConnection conn=(HttpURLConnection) url.openConnection();
          //连接时间超过五秒--超时连接--连接失败
            conn.setConnectTimeout(5*1000);
          //GET
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Accept", "image/gif,image/jpeg,image/pjpeg,image/png,"
                    +"application/x-shockwave-flash,application/xaml+xml,"
                    +"application/vnd.ms-xpsdocument,application/x-ms-xbap,"
                    +"application/x-ms-application,application/vnd.ms-excel,"
                    +"application/vnd.ms-powerpoint,application/msword,*/*");
            conn.setRequestProperty("Accept-Language", "zh-CN");
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("Connection", "Keep-Alive");
          //获取文件大小
            fileSize= conn.getContentLength();
            conn.disconnect();
            int currentPartSize=fileSize/threadNum+1;
          //3.创建本地文件
            RandomAccessFile file = new RandomAccessFile(destPath, "rw");
          //设置本地文件大小
            file.setLength(fileSize);
            file.close();
          //4.定位每个线程的下载位置,依次创建启动线程
            for (int i = 0; i < threadNum; i++) {
                //计算每个线程下载的开始位置
                int startPos=i*currentPartSize;
                //每个线程都需要使用RandomAccessFile对象进行下载
                RandomAccessFile currentPart=new RandomAccessFile(destPath, "rw");
                //定位该线程的下载位置
                currentPart.seek(startPos);
                //创建线程开始下载
                threads[i]=new DownThread(startPos, currentPartSize, currentPart);
                Thread t=new Thread(threads[i]);
                t.start();
            }
    }
    public double getCompleteRate(){
        int sumSize=0;
        for (int i = 0; i < threadNum; i++) {
            sumSize+=threads[i].length;
        }

        return sumSize*1.0/fileSize;
    }
    private class DownThread implements Runnable{
        //当前线程的下载位置
        private int startPos;
        //定义当前线程需要下载的文件大小
        private int currentPartSize;
        //定义该线程需要下载的文件块
        private RandomAccessFile currentPart;
        //定义该线程已下载的字节数
        public int length;

        public DownThread(int startPos, int currentPartSize, RandomAccessFile currentPart) {
            this.startPos = startPos;
            this.currentPartSize = currentPartSize;
            this.currentPart = currentPart;
        }

        @Override
        public void run() {
            InputStream is= null;
            try {
                URL url=new URL(srcPath);
                HttpURLConnection conn=(HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(5*1000);
                conn.setRequestMethod("GET");
                conn.setRequestProperty("Accept", "image/gif,image/jpeg,image/pjpeg,image/png,"
                +"application/x-shockwave-flash,application/xaml+xml,"
                +"application/vnd.ms-xpsdocument,application/x-ms-xbap,"
                +"application/x-ms-application,application/vnd.ms-excel,"
                +"application/vnd.ms-powerpoint,application/msword,*/*");
                conn.setRequestProperty("Accept-Language", "zh-CN");
                conn.setRequestProperty("Charset", "UTF-8");
                is = conn.getInputStream();
                is.skip(this.startPos);
                byte[] b=new byte[1024];
                int len=0;
                while(length<currentPartSize&&(len=is.read(b))!=-1){
                    currentPart.write(b,0,len);
                    length+=len;
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    currentPart.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
MultithreadDownload md=new MultithreadDownload(
"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic%2Fa2%2Fdd%2F26%2Fa2dd26bd29e5191065d08c84770bef17.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1632538380&t=247ca694c9ecaf62fac0c702eb0fb71b",
        "E:\\tempBy\\a.jpg", 5);
try {
    md.download();
    new Thread(()->{while(md.getCompleteRate()<1){
        System.out.println("已完成"+md.getCompleteRate());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }}).start();
    md.getCompleteRate();
} catch (IOException e) {
    e.printStackTrace();
}

代码流程

  1. 创建URL对象
  2. 获取指定URL对象的连接openConncetion()
  3. 获取连接的资源大小
  4. 在本地磁盘创建一个相同大小的空文件
  5. 计算每个线程下载的开始-结束位置
  6. 依次创建、启动多个线程下载网络资源
    注意:这里只是核心代码,还需一个配置文件来记录每个线程的执行位置,网络断开后,只要配置文件有记录,下次打开网络依旧可以从当前位置继续执行

如何向web端发送GET和POST请求?

get:参数是放在URL中进行传递的 post:参数是放在Request body中传递的

  1. get请求在浏览器回退到上一个节点时是无害的,post会再一次提交请求返回上一个节点

  2. get请求会被浏览器主动cache,post不会

  3. get请求只允许URL编码,而post允许多种编码格式

  4. get的请求参数会被完整的保存至浏览器历史记录中,post不会

  5. get请求的参数有长度限制,post没有

  6. get参数只能接受ASCII字符,post无限制

  7. post相对于get更安全,路径不会暴露给用户

public void getTest() throws IOException {
    URL url=new URL("https://baike.baidu.com/item/txt/1217330?fr=aladdin");
    HttpURLConnection huc =(HttpURLConnection) url.openConnection();
    //设置通用请求属性
    huc.setRequestProperty("accept", "*/*");
    huc.setRequestProperty("connection", "Keep-Alive");
    huc.setRequestProperty("user-agent", "Mozilla/4.0(compatible;MSIE 6.0;Windows NT 5.1;SV1)");
    //建立实际连接
    huc.connect();
    Map<String, List<String>> headerFields = huc.getHeaderFields();
    for (String s : headerFields.keySet()) {
        System.out.println(s+"---->"+headerFields.get(s));
    }
    BufferedReader in = new BufferedReader(new InputStreamReader(huc.getInputStream(),"UTF-8"));
    BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:/Documents/a.html"),"UTF-8"));
    String line;
    String result="";
    while((line=in.readLine())!=null){
        result+="\n"+line;
        bw.write(result);
        //System.out.println(result);
    }
}
public void postTest() throws IOException {
    URL url=new URL("https://baike.baidu.com/item/txt/1217330?fr=aladdin");
    HttpURLConnection huc =(HttpURLConnection) url.openConnection();
    //设置通用请求属性
    huc.setRequestProperty("accept", "*/*");
    huc.setRequestProperty("connection", "Keep-Alive");
    huc.setRequestProperty("user-agent", "Mozilla/4.0(compatible;MSIE 6.0;Windows NT 5.1;SV1)");
    //发送POST请求
    huc.setDoOutput(true);
    huc.setDoInput(true);
    PrintWriter out = new PrintWriter(huc.getOutputStream());
    out.println("name=crazyit.org&pass=leegang");
    out.flush();
    BufferedReader in = new BufferedReader(new InputStreamReader(huc.getInputStream(),"UTF-8"));
    String result="";
    String line;
    while((line=in.readLine())!=null){
        result+="\n"+line;
        //System.out.println(result);
    }
}

3.Socket

ip地址和端口组合socket套接字 网络通信其实就是Socket间的通信,以流的方式进行传输

Socket:客户端可以使用Socket类的构造器来连接到指定的服务器

Socket(InetAddress address,int port):服务端的IP和端口号
getIntputStream():返回此套接字的输入流
getOutputStream():返回此套接字的输出流

ServerSocket:服务端可以使用ServerSocket类接收其他通信实体的连接请求

ServerSocket(int port):绑定到客户端的端口号
Socket s=accpet();侦听要连接到此的套接字,并接收它
getIntputStream():返回此套接字的输入流
getOutputStream():返回此套接字的输出流

Socket常用构造器及方法

//两个构造器都是用于创建连接到指定服务器(IP和端口)的Socket,该构造器没有指定本地主机ip和本地端口,使用默认IP地址和系统动态分配端口
Socket(InetAddress remoteAddress,int port)
Socket(String remoteAddress,int port)
//两个构造器都是用于创建连接指定服务器的Socket,指定了本地IP和端口,这样适用于本地主机多IP地址情况
Socket(InetAddress remoteAddress,int port,InetAddress localAddress,int localPort)
Socket(String remoteAddress,int port,InetAddress localAddress,int localPort)
InputStream getInputSream()//返回该Socket对象对应的输入流,让程序通过该输入流从Socket中取出数据
OutputStream getOutputStream()//返回该Socket对象对应的输出流,让程序通过该输出流向Socket中输出数据

ServerSocket常用方法

ServerSocket(int port)//创建ServerSocket并开启监听端口,用于接收其他通信主机向此端口发送的数据
ServerSocket(int port,int backlog)//增加了一个用于改变连接队列长度的参数backlog
ServerSocket(int port,int backlog,InetAddress localAddr)//在机器存在多个IP地址的情况下,允许通过localAddr参数来指定将ServerSocket绑定到指定的IP地址

Socket accpet()//接收一个客户端Socket的连接请求,该方法将返回一个与客户端Socket对应的Socket,如果持续未得到客户端的连接请求,则会一直处于等待状态,线程也会被阻塞
客户端发送信息->服务器
/**
 * @Description:模拟客户端向服务器发送信息--客户端
 *              1.创建Socket--指定IP和端口
 *              2.获取输出流--将数据输出到服务器中
 *              3.操作输出流
 *              4.关闭连接--包括Socket、输出流
 * @Param:
 * @return: void
 */
public void  client(){
    Socket s= null;
    OutputStream os= null;
    try {
        //1.给客户端的Socket指定服务器端端的IP和端口号--第一要素
        InetAddress ias=InetAddress.getByName("192.168.31.164");//这一步是多余的,这里为了表示InetAddress这个类的实例指向的是一个实际IP
        s = new Socket(ias,8894);
        //2.连接Socket后,需要获取客户端的输出流进行数据的输出操作--获取输出流后其他操作就是和IO流的操作一样了
        os = s.getOutputStream();
        System.out.println("正在向服务器发送信息........");
        Thread.sleep(10000);
        os.write("你好,我是客户端".getBytes());
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    } finally {
        //3.关闭流
        try {
            if (s != null) {
                s.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
@Test
/**
 * @Description: 模拟客户端向服务器发送信息--服务端
 *                  1.创建ServerSocket对象--开放服务端的端口用于接收客户端的信息(与客户端设置的端口一致)
 *                  2.调用accept()方法用于接收客户端的Socket对象
 *                  3.获取该Socket对象的输入流--将数据输入到服务器中
 *                  4.操作该输入流读取数据
 *                  5.关闭流--ServerSocket连接、输入流、Socket连接
 * @Param:
 * @return: void
 */
public void server() {
    ServerSocket serverSocket = null;
    Socket s = null;
    InputStream is = null;
    InputStreamReader isr=null;
    try {
        //1.服务端需要启动服务端口供客户端访问(ServerSocket)
        serverSocket = new ServerSocket(8894);
        //2.接收该端口的请求信息--可能会有若干个客户端发送信息--这里只有一个
        s = serverSocket.accept();
        //3.获取服务端的输入流,将客户端发送的数据读入到服务端
        is = s.getInputStream();
        //进行一次转换流操作--避免中文出现乱码
        isr=new InputStreamReader(is);
        char[] cbuf=new char[1024];
        int len;
        while((len=isr.read(cbuf))!=-1){
            String s1 = new String(cbuf, 0, len);
            System.out.println(s1);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //4.关闭流
        try {
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            isr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
客户端发送文件->服务器
public void client1(){
    Socket socket = null;
    OutputStream os = null;
    FileInputStream fis= null;
    InputStream is =null;
    InputStreamReader isr=null;
    try {
        socket = new Socket("192.168.31.164", 9085);
        os = socket.getOutputStream();
        fis = new FileInputStream(new File("E:\\学习文档\\文档\\ppt模板\\功能实现.pptx"));
        byte[] buffer=new byte[1024];
        int len=0;
        while((len=fis.read(buffer))!=-1){
            os.write(buffer,0,len);
        }
        socket.shutdownOutput();
        is = socket.getInputStream();
        isr=new InputStreamReader(is);
        char[] cbuf=new char[10];
        int len1=0;
        while((len1=isr.read(cbuf))!=-1){
            System.out.println(new String(cbuf,0,len1));
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (socket != null) {
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (fis != null) {
                fis.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (is != null) {
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (isr != null) {
                isr.close();
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

public void server1(){
    ServerSocket ss = null;
    Socket socket = null;
    InputStream is = null;
    FileOutputStream fos= null;
    OutputStream os =null;
    try {
        ss = new ServerSocket(9085);
        socket = ss.accept();
        is = socket.getInputStream();
        fos = new FileOutputStream(new File("E:\\学习文档\\文档\\ppt模板\\功能实现1.pptx"));
        byte[] buffer=new byte[1024];
        int len=0;
        while((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }
        System.out.println("接收成功");
        os = socket.getOutputStream();
        os.write("文件传输成功!".getBytes());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (ss != null) {
                ss.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (socket == null) {
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (fos == null) {
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (is == null) {
                is.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (os != null) {
                os.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 28
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Carl·杰尼龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值