Java网络编程超详细笔记
引言:本文主要分享了在Java的网络编程,首先简单的说了两种网络模型以及网络编程的三要素(IP地址、端口号、协议),之后分享了InetAddress类(获取ip、主机名称的方法),重点是UDP和TCP的传输,并分享了若干案例(客户端键盘录入服务端输出文件去案例、服务端给出反馈案例、传输图片案例等等);
@[toc]
1. 计算机网络
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来实现资源共享和信息传递的计算机系统。
- 互联网:(Inter)点与点相连;能进入互联网的都是点;
- 万维网:(WWW-World Wide Web)端与端相连;移动端、服务端、虚拟主机;
- 物联网:(IoT-Internet of things)物与物相连;
- 网络编程:让计算机与计算机之间建立连接、进行通信;
2. 网络模型
网络模型就是研究计算机网络之间以何种规则进行通信的问题。
2.1 OSI 模型
OSI(Oper System Interconnedtion)开放式系统互联
- MAC地址唯一的
2.2 TCP/IP模型
一组用于实现网络互联的通讯协议,协议分为四层;
3. 网络编程三要素
网络编程三要素是IP地址、端口号、协议;
3.1 IP地址
IP协议:Internet Protocol Address互联网协议地址/网际协议地址;分配给互联网设备的数字标签(唯一标识)
IP地址有两种
IPV4:
- 4字节32位整数,分为4段8位二进制数,每8位之间用原点隔开,每8位整数可转换为一个0~255的十进制整数;255.255.255.255
IPV6:
- 16字节128位整数,分为8段十六进制数,没16位之间用原点隔开,每16位整数可转换为一个0~65535的十进制数;
IPV4的应用分类:
3.2 端口号
端口号:在通信实体上进行网络通信的程序的唯一标识;
端口号分类:
公认端口:0~1023
注册端口:1024~49151
动态或者私有端口:49152~65535
常用端口:
MySQL:3306
Oracle:1521
Tomcat:8080
SMTP:25
Web服务器:80
FTP服务器:21
3.3 协议
协议一般分为TCP协议和UDP协议
User Datagram Protocol用户数据报协议,将数据源和目的封装成数据包中;
- 一种无需连接的传输层协议;
- 每个数据报的大小在限制在64k;
- 无连接是不可靠协议,不需要建立连接速度快;
- 提供面向事务的简单不可靠的信息传送服务;
Transmission Control Protocol传输控制协议,建立连接形成传输数据的通道;
- 一种面向连接的、可靠的、基于字节流的传输层通讯协议;
- 数据的大小无限制,越大越慢;
- 建立连接时需三次握手(success),断开连接时需四次挥手;
- 必须建立连接效率会稍低;
4. InetAddress类
表示互联网协议(IP)地址对象,封装了与该IP地址相关的所有信息,并提供获取信息的常用方法;
无法直接创建对象,构造方法私有化;需要通过getXXX()方法来获得;
- public static InetAddress getLocalHost():获得本地主机地址对象
- public static InetAddress getByName(String host):通过主机名称获取ip地址对象
- public static InetAddress[] getAllByName(String host):获取所有相关的地址对象
- public String getHostAddress(): 返回ip的字符串形式(文本形式)
- public String getHostName():获取ip地址的主机名称
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//通过主机名获取ip地址对象
//InetAddress inetAddress = InetAddress.getByName("卡卡");
InetAddress inetAddress = InetAddress.getLocalHost();
//返回ip的字符串形式(文本形式)
String ip = inetAddress.getHostAddress();
//获取ip地址的主机名称
String name = inetAddress.getHostName();
System.out.println(ip + "\t" + name);
}
}
5. Socket套接字
Socket(套接字:两台机器间通信的端点)是网络中的一个通讯节点;通信要求:IP地址+端口号,
客户端套接字Socket与服务器套接字ServerSocket;
Socket原理
- 通信的两端都有Socket
- 网络通信其实就是Socket间的通信
- 数据在两个Socket间通过IO流传输
开发步骤
建立通信连接(会话):
- 创建ServerSocket,指定端口号;
- 调用accept等待客户端接入;
客户端请求服务器:
- 创建Socket,指定服务器IP+端口号;
- 使用输出流发送请求数据给服务器;
- 使用输入流接收响应数据到客户端(等待)
服务器响应客户端:
- 使用输入流接收请求数据到服务器(等待)
- 使用输出流发送响应数据给客户端
6. UDP传输
建立发送端,接收端;属于不可靠连接,建立数据报包,调用Socket的发送接收的方法;
DatagramSocket与DatagramPacket
6.1 UDP接收端
接收步骤:
- 创建Socket套接字对象 DatagramSocket(int port)
- 创建数据报包(接收容器)接收数据DatagramPacket(byte[] buf, int length)
- 接收数据报包中的实际数据 receive(DatagramPacket p)
- 将发送数据的信息解析出来 (解析的实际发送端数据)
- 展示数据
- 关闭资源
public DatagramSocket(int port) :绑定端口
public DatagramPacket(byte[] buf, int length)
参数1:自定义的缓冲区
参数2:自定义缓冲大小的长度
public void receive(DatagramPacket p):接收数据报包中的实际数据
DatagramPacket:数据报包中 public byte[] getData():获取实际缓冲区
public int getLength() :获取数据的实际长度
public InetAddress getAddress() :将IP地址解析出来
InetAddress 里面的getHostAddress()中IP是字符串形式
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建Socket套接字对象,绑定端口
DatagramSocket ds = new DatagramSocket(10086);
//创建数据包接收数据
byte[] bytes = new byte[1024];
int length = bytes.length;
DatagramPacket dp = new DatagramPacket(bytes, length);
//接收数据
ds.receive(dp);
//将发送的数据信息解析出来
byte[] bytes2 = dp.getData();
int length2 = dp.getLength();
String s = new String(bytes2,0,length2);
//解析ip
InetAddress address = dp.getAddress();
String ip = address.getHostAddress();
System.out.println("发送端的ip地址是:"+ip+",发送的数据是:"+s);
//关闭资源
ds.close();
}
}
6.2 UDP发送端
发送步骤:
- 创建一个Socket套接字对象DatagramSocket()
- 创建数据报包对象(DatagramPacket) :包含:数据信息,包含ip地址,包含端口
- 使用DatagramSocket套接字对象发送数据报包
- 关闭资源
- public DatagramPacket(byte[] buf, int length, InetAddress address,int port):创建数据报包对象(DatagramPacket) :包含:数据信息,包含ip地址,包含端口
- public void send(DatagramPacket p):使用DatagramSocket套接字对象发送数据报包
public class SendDemo {
public static void main(String[] args) throws IOException {
//创建一个Socket对象
DatagramSocket ds = new DatagramSocket();
//实际字节数组:存储需要被发送的内容
byte[] bytes = "你好!".getBytes();
//实际长度
int length = bytes.length;
//ip地址对象
InetAddress inetAddress = InetAddress.getByName("10.12.151.138");
//定义端口号
int port = 10086;
//创建数据报包
DatagramPacket dp = new DatagramPacket(bytes, length, inetAddress, port);
//使用DatagramSocket套接字对象发送数据报包
ds.send(dp);
// 4)关闭资源
ds.close();
}
}
- 先开启接收端,在发送数据
- 接收端不要运行多次:否则就出现:java.net.BindException: Address already in use: Cannot bind 端口号冲突
6.3 代码优化(一步实现)
//发送端
public class SendDemo {
public static void main(String[] args) throws IOException {
//创建DatagramSocket
DatagramSocket ds = new DatagramSocket();
//创建数据包对象
byte[] bytes = "nihao!".getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length,InetAddress.getByName("10.12.151.138"),10086);
//发送数据
ds.send(dp);
//关闭资源
ds.close();
}
}
//接收端
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建DatagramSocket
DatagramSocket ds = new DatagramSocket(10086) ;
//创建数据报包:接收容器
byte[] bytes = new byte[1024] ;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length) ;
//3)接收
ds.receive(dp);
//解析数据
String s = new String(dp.getData(),0,dp.getLength()) ;
//获取ip地址
String ip = dp.getAddress().getHostAddress() ;
System.out.println("IP:"+ip+"发送的内容是:"+s);
//关闭资源
ds.close();
}
}
6.4 案例_无限发送
需求:
- 发送端发送数改为"键盘录入"使用BufferedReader流的方式,
- 自定义一个结束条件"byt bye /886"
- 接收端不断的去接收消息不关闭
//发送端
public class DemoSend {
public static void main(String[] args) throws IOException {
//创建发送端的DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//发送端不断的键盘录入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while((line = br.readLine())!= null){
if("886".equals(line)){
break;
}
//创建数据报包对象
byte[] bytes = line.getBytes();
DatagramPacket dp = new DatagramPacket(bytes, bytes.length,InetAddress.getByName("10.12.151.138"),10086);
//发送数据
ds.send(dp);
}
//关闭资源
ds.close();
}
}
//接收端
public class DemoReceived {
public static void main(String[] args) throws IOException {
//创建接收的socket对象
DatagramSocket ds = new DatagramSocket(10086);
while(true){
//创建数据接收包,接收容器
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
//接收数据
ds.receive(dp);
//解析数据
String s = new String(dp.getData(),0,dp.getLength());
//解析IP
String ip = dp.getAddress().getHostAddress();
System.err.println("from:"+ip +"data is :" +s);
}
}
}
6.5 案例_多线程同一窗口
需求:
将UDP协议 发送端和接收端 在一个窗口下进行聊天
步骤:
- 开启两个子线程:分别实现Runnable接口并且重写run方法
- sendDemo:发送端
- ReceiveDemo:接收端
- 开启两个子线程 :Thread(Runnable target)
//接收端
public class ReceiveDemo implements Runnable{
private DatagramSocket ds ;
public ReceiveDemo(DatagramSocket ds) {
this.ds = ds ;
}
@Override
public void run() {
//不断的接收数据
//创建数据报包
try {
while(true) {
byte[] bytes = new byte[1024] ;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length) ;
//接收
ds.receive(dp);
//解析数据
String s = new String(dp.getData(),0,dp.getLength()) ;
String ip = dp.getAddress().getHostAddress();
System.out.println("ip is:"+ip+"data is:"+s);
}
} catch (IOException e) {
e.printStackTrace();
}
//接收端模拟真实场景,不需要关闭
}
}
//发送端
public class SendDemo implements Runnable {
private DatagramSocket ds ;
public SendDemo(DatagramSocket ds) {
this.ds = ds ;
}
@Override
public void run() {
//键盘录入数据
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)) ;
String line = null ;
while((line=br.readLine())!=null) {
//自定义结束条件
if("886".equals(line)) {
break ;
}
//创建数据报包对象
byte[] bytes = line.getBytes() ;
DatagramPacket dp = new DatagramPacket(bytes, bytes.length,InetAddress.getByName("10.12.151.138"), 12345) ;
//发送数据
ds.send(dp);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
//释放资源
if(ds!=null) {
ds.close();
}
}
}
}
//主方法
public class ChatRoom {
//主线程
public static void main(String[] args) {
try {
//创建一个DataSocket对象
DatagramSocket ds = new DatagramSocket() ;
DatagramSocket ds2 = new DatagramSocket(12345) ;
//创建发送端资源类对象
SendDemo sd = new SendDemo(ds) ;
//创建接收端资源类对象
ReceiveDemo rd = new ReceiveDemo(ds2);
Thread t1 = new Thread(sd) ;
Thread t2 = new Thread(rd) ;
//开启线程
t1.start();
t2.start();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
7. TCP传输
建立客户端和服务器端,属于安全连接;建立连接后,通过Socket中的IO流进行数据的传输;
Socket和ServerSocket
7.1 TCP客户端
步骤:
- 创建Socket套接字对象 Socket(String host,int port)
- 获取连接通道内的字节输出流对象 public OutputStream getOutputStream()
- 通过该字节输出流对象写数据 write(byte[] bytes)
- 释放资源
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的流套接字对象
Socket socket = new Socket("10.12.151.138",10086);
//获取通道内的字节输出流对象
OutputStream out = socket.getOutputStream();
//写数据
out.write("你好Java".getBytes());
//关闭资源
socket.close();
}
}
7.2 TCP服务端
步骤:
- 创建ServerSocket套接字对象 ServerSocket(int port)
- 侦听客户端连接 public Socket accept() 阻塞式方法
- 获取通道内字节输入流对象 public InputStream getInputStream()
- 一次读取一个字节数组public int read(byte[] bytes)
- 展示数据 public InetAddress getInetAddress():获取ip地址对象
- 释放资源
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建ServerSocket服务套接字
ServerSocket ss = new ServerSocket(10086);
//监听客户端连接:阻塞式方法
//返回的就是当前客户端对象
Socket socket = ss.accept();
//获取通道内字节输入流对象
InputStream in = socket.getInputStream();
//读取通道流的数据
byte[] bytes = new byte[1024];
int len = in.read(bytes);
String str = new String(bytes,0,len);
//展示数据
String ip = socket.getInetAddress().getHostAddress();
System.out.println("ip地址是:"+ip+",发送的数据是:"+str);
//关闭资源
ss.close();
}
}
7.3 案例_服务端反馈客户端
需求:客户端发消息服务端给出反馈
//客户端
public class ClientTest {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket s = new Socket("10.12.151.138",11111);
//获取通过的内存输出流对象
OutputStream out = s.getOutputStream();
out.write("你好!".getBytes());
//客户端的接收服务器的反馈
//获取通道内的输入流对象
InputStream in = s.getInputStream();
//一次读取一个字节
byte[] bytes = new byte[1024];
int len = in.read(bytes);
String serverMsg = new String(bytes,0,len);
System.out.println(serverMsg);
//释放资源
out.close();
}
}
//服务端
public class ServerTest {
public static void main(String[] args) throws IOException {
//创建服务端的Socket对象
ServerSocket ss = new ServerSocket(11111);
//监听客户端连接
Socket socket = ss.accept();
//获取通道内的输入流对象
InputStream in = socket.getInputStream();
//一次读取一个字节数组
byte[] bytes = new byte[1024];
int len = in.read(bytes);
String str = new String(bytes,0,len);
//展示客户端发送的数据
String ip = socket.getInetAddress().getHostAddress();
System.out.println("客户端的ip:"+ip+",发送的内容:"+str);
//服务器反馈
//获取当前绑定的端口为11111的客户端通道内的字节输出流
OutputStream out = socket.getOutputStream();
out.write("收到信息".getBytes());
//释放资源
ss.close();
}
}
7.4 案例_无限发送
需求:客户端不断键盘录入数据,(BufferedReader),客户端一旦录入"886"结束,服务器端将客户端录入的数据展示在控制台上!
//客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket s = new Socket("10.12.151.138",33333);
//创建BufferedReader字符缓冲输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//将通过内的流封装成BufferedWriter
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String line = null;
while((line = br.readLine())!= null){
if("886".equals(line)){
break;
}
//字符输入流读取一行
//就给当前通过内的流写一行
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
s.close();
bw.close();
}
}
//服务端
public class ServerDemo {
public static void main(String[] args) throws IOException{
//创建服务器端的Socket对象
ServerSocket ss = new ServerSocket(33333);
//监听客户端
Socket socket = ss.accept();
//封装通过通道内的字节输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//不断读数据,在控制台显示
String line = null;
while((line = br.readLine())!= null){
System.out.println(line);
}
}
}
7.5 案例_客户端键盘录入服务端输出文件
需求:客户端不断键盘录入(使用BufferReader流的方式),自定义结束条件,服务器端输出一个文本文件
//客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket socket = new Socket("10.12.151.138",22222);
//键盘录入
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//封装通道内的字节输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;
String line = null ;
while((line=br.readLine())!=null) {
if("over".equals(line)) {
break ;
}
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
bw.close();
socket.close();
}
}
//服务端
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务端的Socket对象
ServerSocket ss = new ServerSocket(22222);
//监听客户端的连接
Socket socket = ss.accept();
//封装通道内的字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//创建字符缓冲输出流对象
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
//一次读取一行
String line = null;
while((line = br.readLine())!= null){
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
bw.close();
ss.close();
}
}
7.6 案例_客户端读取文件服务端输出在控制台
需求:客户端读取文本文件,服务器将文本文件的内容输出到控制台,优先使用字符流
//客户端
public class ClientTest {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket socket = new Socket("192.168.1.110",22222);
//封装文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装通道内的字节输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) ;
//一次读一行数据(文件)
String line = null;
while((line = br.readLine())!= null){
bw.write(line);
bw.newLine();
bw.flush();
}
//关闭资源
br.close();
socket.close();
}
}
//服务端
public class ServerTest {
public static void main(String[] args) throws IOException {
//创建服务器ServerSocket对象
ServerSocket ss = new ServerSocket(22222);
//监听客户端连接
Socket socket = ss.accept();
//封装通道内的字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//一次读取一行
String line = null;
while((line = br.readLine())!= null){
System.out.println(line);
}
ss.close();
}
}
7.7 案例_客户端读入文件服务端复制该文件
需求:客户端文本文件,服务器端输出一个文本文件(将客户端的文本文件中的内容复制到该文件中)
//客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建Socket对象
Socket s = new Socket("10.12.151.138",11111);
//封装文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装通过的内字节流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//一次读一行
String line = null;
while((line = br.readLine())!= null){
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
br.close();
s.close();
}
}
//服务端
public class ServerDemo {
public static void main(String[] args) throws IOException {
//创建服务器端Socket
ServerSocket ss = new ServerSocket(11111);
//侦听客户端
Socket s = ss.accept();
//封装通道内字节流输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//封装文本文件
BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt"));
//读一行,在copy中写一行
String line = null;
while((line = br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
//释放资源
bw.close();
s.close();
}
}
7.8 案例_客户端读入文件服务端输出文件并给出反馈
需求:客户端的文本文件,服务器端复制文本文件的内容到Copy.java中并且服务器加入反馈操作
readLine()方法通过返回是否null只是判断文件是否读完,服务器端通过内流的不知道文件是否已经读完了那么就会相互等待
解决:
- 在客户端告诉服务器端,没有数据了,自定义一个"字符串"作为结束标记
- public void shutdownOutput():告诉服务器,客户端没有数据了
//客户端
public class ClientDemo {
public static void main(String[] args) throws IOException {
//创建客户端的Socket对象
Socket s = new Socket("10.12.151.138",11111);
//封装文本文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//封装通过的内字节输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//读内容,写进流
String line = null;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
//自定义一个结束标记,作为客户端给服务器通知,文件已经读完了
/*bw.write("over");
bw.newLine();
bw.flush();*/
//告诉服务器端客户端没有数据了
s.shutdownOutput();
//接收服务器端的反馈信息
//获取通过内的字节输入流,封装成字符流
BufferedReader clientBr = new BufferedReader(new InputStreamReader(s.getInputStream()));
//利用readLine
String serverMsg = clientBr.readLine();
System.out.println("serverMsg:"+serverMsg);
//关闭
br.close();
s.close();
}
}
//服务端
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(11111);
//监听
Socket s = ss.accept();
//封装通过的内字节输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//封装被输出的文本文件Copt.txt
BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt"));
//读取通过的流的内容,写的copy中
String line = null;
while((line = br.readLine())!= null){
/*//加入一个判断
if("over".equals(line)) {
break ;//结束
}*/
bw.write(line);
bw.newLine();
bw.flush();
}
//给客户端发送反馈信息
//服务器端获取通过的字节输出流对象(封装成字符流)
BufferedWriter bw2 = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())) ;
bw2.write("数据已经收到了");
bw2.newLine();
bw2.flush();
//关闭
bw.close();
s.close();
}
}
7.9 客户端读取图片服务器复制并且反馈
需求:客户端图片文件服务器将图片文件进行复制操作并且反馈
- public void flush():强制将此输出流中图片文件的缓存信息刷新出来
//客户端
public class UploadClient {
public static void main(String[] args) throws IOException{
//创建Socket
Socket s = new Socket("10.12.151.138",55555);
//封装指定的路径文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\photo.jpg"));
//封装通过内的字节输出流对象
BufferedOutputStream bos = new BufferedOutputStream(s.getOutputStream());
//一次读取一个字节数组
byte[] bytes = new byte[1024];
int len = 0;
while((len = bis.read(bytes))!=-1){
bos.write(bytes,0,len);
//强制刷新
bos.flush();
}
//告诉服务器客户端文件读取完毕
s.shutdownInput();
//接收服务端的反馈
//获取通过内的字节输入流
InputStream in = s.getInputStream();
//一次读取一个字节数组
byte[] bytes2 = new byte[1024];
int len2 = in.read(bytes2);
String serverMsg = new String(bytes2,0,len2);
System.out.println("serMsg:" + serverMsg);
//释放资源
bis.close();
s.close();
}
}
//服务端
public class UploadServer {
public static void main(String[] args) throws IOException{
ServerSocket ss = new ServerSocket(55555);
//监听
Socket s = ss.accept();
//封装通过内的字节输入流
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
//封装图片地址
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("photocopy.jpg"));
//一次读取一个字节数组
byte[] bytes = new byte[1024];
int len = 0;
while((len = bis.read(bytes))!= -1){
bos.write(bytes,0,len);
//强制刷新流
bos.flush();
}
//发送反馈信息给 客户端
OutputStream out = s.getOutputStream();
out.write("已经收到".getBytes());
//关闭
bos.close();
s.close();
}
}
- 一定要注意写端口号时避免冲突,否则会抛出java.net.BindException: Address already in use: JVM_Bind 端口号冲突异常
8. TCP和UDP的区别
UDP协议:发送数据包到接收端
- 面向无连接(不需要建立连接通道)
- 不可靠连接协议,只管发送不管对方是否接收
- 发送和接收数据的执行效率高
- 发送数据大小有限制的不超过64kb
TCP协议:发送数据: 通过通过内的字节输出流OutputStream 写数据到服务器端
- 面向连接(需要建立连接通道)
- 可靠连接协议(安全的)
- 执行效率低
- 发送数据的大小无限制