目录
一级目录
二级目录
三级目录
1.网络编程基础
-
什么是网络?:把分布在不同区域的计算机使用专业通信线路连接起来,实现资源数据共享
-
网络编程的目的:通过通讯协议实现数据传输
-
什么是www?:
- 全球信息网(WWW,World Wide Web)
-
网络通信的要素:
-
ip和端口(也就是所谓的Socket)
-
协议
-
通信协议三部分:
语义部分:用于决定双方对话的类型
语法部分:用于决定双方对话的格式
变换规则:用于决定通信双方的应答关系
参考模型:
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连接):
三次握手可以是客户端发起,也可以是服务端发起,但是一般是客户端发起的请求
-
客户端向服务端发送一段TCP报文(我可以发数据给你吗)
- 此时SYN生效–>请求建立新连接
- 序号Seq=X(X一般=1)
- 客户端进入SYN-SENT阶段
-
服务端接收客户端的TCP报文(可以)
- 获取SYN和Seq后,表示服务端能正常接收客户端的数据,告诉客户端同意创建连接
- Seq=Y(将服务器发送的TCP报文的序号赋值给Seq)
- 确认号Ack=X+1(将客户端发送过来的序号+1作为确认号,表示确认接收到了序号)
- ACK标志位=1(确认号有效)
- 服务端进入SYN-RCVD阶段
-
客户端接收到服务器的确认收到数据的TCP报文(准备发送数据)
- 确认服务端发送的ACK标志位为1(同意发送的信号),并把ACK=1发送给服务端(我马上就开始发送数据了)
- 序号=Ack=X+1,将服务器发送过来的Ack作为序号发送给服务端
- 确认号Ack=Y+1,将服务端发送的序号+1作为自己的确认号发送给服务端
- 随后客户端和服务端进入ESTAB-LISHED阶段–数据就可以开始传输了
四次挥手(TCP连接的释放):
- 首先客户准备释放连接,向服务端发送一段TCP报文
-
FIN=1表示:请求释放连接
-
序号为seq=u;
-
随后客户端进入FIN-WART-1阶段—等待请求(客户端可以接收服务端的数据,但是客户端不再发送数据到服务端–TCP报文除外)
-
服务端接收到客户端发出的TCP报文后,确认客户端的释放连接需求,随后结束ESTAB-LISHED阶段,进入CLOSE-WAIT,并返回一段TCP报文
- ACK=1表示:接收到客户端发送的释放连接请求
- 序号为seq=v
- 确认号为Ack=u+1,表示:将序号seq值+1作为本段报文的确认号Ack的值
- 服务器开始准备释放服务端到客户端的连接
- 此时客户端结束了FIN-WAIT-1阶段,进入FIN-WAIT-2阶段
-
当服务器确认可以关闭服务器到客户端的连接(经历了CLOSED-WAIT阶段)后(准备就绪),发送一段TCP报文给客户端
- 标记位为FIN,ACK,表示:已经准备好释放连接了,
- 序号为seq=w
- 确认号为Ack=u+1,表示:在收到客户端报文的基础上,将其seq值+1作为本段报文的确认号Ack的值
- 随后服务器结束ClOSE-WAIT阶段,进入LAST-ACK阶段–停止服务端向客户端发送数据
-
客户端接收到服务端发出的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接收数据的步骤:
- 创建接收端的Socket对象(DatagramSocket)
- 创建一个数据包,用于接收数据
- 调用DatagramSocket对象的方法用于接收数据
- 解析数据包,并把数据输出在控制台上
- 关闭数据
测试示例
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的连接,步骤如下:
- 通过调用URL对象的openConnection()方法创建URLConnection对象
- 设置URLConnection的参数和普通请求属性
- 如果只是发送GET请求,则使用connect()方法建立和远程资源直接的实际连接即可
如果需要发送POST请求,则需要获取URLConnection实例对应的输出流来发送请求参数。 - 远程资源变为可用,程序可以访问远程资源的头字段或通过输入流读取远程资源数据
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();
}
代码流程
- 创建URL对象
- 获取指定URL对象的连接openConncetion()
- 获取连接的资源大小
- 在本地磁盘创建一个相同大小的空文件
- 计算每个线程下载的开始-结束位置
- 依次创建、启动多个线程下载网络资源
注意:这里只是核心代码,还需一个配置文件来记录每个线程的执行位置,网络断开后,只要配置文件有记录,下次打开网络依旧可以从当前位置继续执行
如何向web端发送GET和POST请求?
get:参数是放在URL中进行传递的 post:参数是放在Request body中传递的
-
get请求在浏览器回退到上一个节点时是无害的,post会再一次提交请求返回上一个节点
-
get请求会被浏览器主动cache,post不会
-
get请求只允许URL编码,而post允许多种编码格式
-
get的请求参数会被完整的保存至浏览器历史记录中,post不会
-
get请求的参数有长度限制,post没有
-
get参数只能接受ASCII字符,post无限制
-
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();
}
}
}