JAVA网络编程
文章看起来很长,好吧,确实很长,不过占据主要
计算机网络概述:
- 把具有独立功能的多台计算机连接起来,实现资源共享和网络的传递
网络编程:
- 就是用来实现网络互联的不同计算机上运行的程序间可以进行数据的交换。
网络模型7层概述:
-
**物理层:**主要定义物理设备标准,如网线的接口类型,光纤的接口类型,各种传输介质的传输速率等。它的主要作用是传输比特流(就是由0,1转化为电流强弱来进行传输,到达目的地后再转化为0,1,也就是我们常说的数模转换和模数转换)这一层的数据叫做比特。
-
**数据链路层:**主要将从物理层接受的数据进行Mac地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧,在这一层工作的设备是交换机,数据通过交换机来传输。
-
**网络层:**主要将从下层接收到的数据进行IP地址(192.168.0.1)的封装与解封装。在这一层工作的设备叫做路由器,常把这一层的数据叫做包。
-
**传输层:**定义了一些传输数据的协议和端口号(www的端口号80等),如TCP(传输控制协议:传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议:与TCP特性相反,用于传输可靠性要求低,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。主要是将从下层接受的数据进行分段和传输,到达目的后再进行重组,。常常把这一层数据叫做段。
-
**会话层:**通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间要互相认识可以说IP地址也可以是Mac地址或主机名)
-
**表示层:**主要是对接收的数据进行解释,加密,解密,压缩于解压缩等(也就是把计算机能够是别的东西转化成人能够人别的东西(文字,图像,声音等))
-
**应用层:**主要是一些终端的应用,比如说FTP(各种文件的上传下载),WEB(IE浏览器),QQ之类的等电脑上的应用(可以理解为我们可以在电脑上看到的东西)。
网络应用程序:网络编程、IO流、多线程组成。
网络编程三要素:
- IP地址
- 端口
- 协议
- 端口
IP地址:
-
网络中计算机的唯一标识
-
计算机只能识别二进制的数据,所以说我们的IP地址应该是一个二进制的数据
但是,我们配置的IP地址却不是二进制的,为什么呢?
答:为了方便表示IP地址,我们就把IP地址的每一个字节上的数据换算成十进制,然后用"."分开(点分十进制),计算机找的时候,都会将IP地址转换为二进制,两个DOS命令:
- ipconfig:查看本机IP地址
- ping:后面跟IP地址,测试本机与指定的IP地址间的通信是否有问题。
127.0.0.1回环地址(表示本机)
端口
物理端口,网卡口
逻辑端口(我们这里指的就是逻辑端口):
- 每个网络程序都会至少有一个逻辑端口
- 用于标识进程的逻辑地址,不同进程的标识。
- 有效端口:0~65535, 其中1~1024为系统使用或保留端口。
端口号:正在运行的程序标识。
协议
- UDP
将数据源和目的封装成数据包,不需要建立连接,每个数据包的大小在限制在64k,因无连接,是不可靠协议,不需要建立连接,速度快。 - TCP
建立连接,形成传输数据的通道,在连接中进行大数据量传输,通过三次握手完成连接,是可靠协议,必须建立连接,效率会稍低。
Socket
Socket:网络套接字
Socket编程,网络编程,套接字编程
Socket包含了IP和端口
Socket原理机制:
- 通信的两端都有Socket。
- 网络通信其实就是Socket间的通信。
- 数据在两个Socket间通过IO传输。
UDP协议:
UDP协议接收数据:
- 创建接受端Socket对象
- 创建一个数据包(接受容器)
- 调用Socket对象的接受方法接受数据
- 解析数据包,并显示在控制台
- 释放资源
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
// 接收
public class ReveiceDemo {
public static void main(String[] args) {
try {
// 创建接收数据包套接字
DatagramSocket socket = new DatagramSocket(10086);
// 创建一个数据包容器
byte[] by = new byte[1024];
DatagramPacket dp = new DatagramPacket(by, by.length);
// 接受数据
socket.receive(dp);
// 解析数据
String ip = dp.getAddress().getHostAddress();
String s = new String(dp.getData(), 0, dp.getLength());
System.out.println(ip + "----" + s);
// 释放资源
socket.close();
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
UDP协议发送数据:
- 创建发送端的Socket对象
- 创建数据,并把数据打包
- 调用Socket对象的发送方法发送数据包
- 释放资源
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
// 发送
public class SendDemo {
public static void main(String[] args) throws IOException {
// 创建发送端对象
DatagramSocket socket = new DatagramSocket();
// 发送一个数据包,将要发送的数据封装进去
byte[] by = "Hello World JAVA".getBytes();
DatagramPacket dp = new DatagramPacket(by, by.length,InetAddress.getByName("192.168.1.5"),10086);
// 发送数据包
socket.send(dp);
// 释放资源
socket.close();
}
}
代码,还可以改进,发送端想要多次发送可以加while循环。为了节省空间防止水字数,所以就暂时不加了。
TCP协议
TCP协议发送数据
- 创建发送端的Socket对象
这一步如果成功,则说明连接建立成功了。 - 获取输出流,写数据
- 释放资源
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) {
try {
Socket sk = new Socket("192.168.1.5",8888);
OutputStream os = sk.getOutputStream();
os.write("Hello World JAVA".getBytes());
sk.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
TCP协议接受数据:
- 创建接收端的Socket对象(ServerSocket)
- 监听客户端接收,返回一个Socket对象
- 获取输入流,读取数据显示在控制台
- 释放资源
注意:
- ServerSocket里面并没有获取输入流的方法,不过ServerSocket里面有一个方法accept()方法,它的返回值是一个Socket对象,通过Socket对象获取输入流。
- 必须开启接收端(服务器端)才能开启发送端,直接开启发送端报错。
- Socket accept() 侦听要连接到此套接字并接受它。
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) {
try {
ServerSocket ss = new ServerSocket(8888);
Socket s = ss.accept();
InputStream i = s.getInputStream();
byte[] by = new byte[1024];
int read = i.read(by);
String str = new String(by,0,read);
System.out.println(str);
s.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
发送端发送数据,接受端反馈数据:
发送端代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class ClientDemo2 {
public static void main(String[] args) {
try {
Socket socket = new Socket("192.168.1.5",9999);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("收到了没".getBytes());
InputStream inputStream = socket.getInputStream();
byte[] by = new byte[1024];
int read = inputStream.read(by);
String s = new String(by,0,read);
System.out.println("服务器:"+s);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收端代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo2 {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
byte[] by = new byte[1024];
int read = inputStream.read(by);
String s = new String(by,0,read);
String ip = socket.getInetAddress().getHostAddress();
System.out.println(ip+": "+s);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("服务器收到".getBytes());
socket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
客户端发送数据,服务器接受数据并写到文本文件里面:
服务器端:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo4 {
public static void main(String[] args) {
try {
// 创建服务器端套接字
ServerSocket serverSocket = new ServerSocket(10086);
Socket socket = serverSocket.accept();
// socket获取的字节输入流,包装为字符缓冲流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 封装文本文件
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("a.txt"));
String line = null;
// 赋值,判断否为空,不为空则发送数据
while ((line = bufferedReader.readLine()) != null) {
// 将客户端传递过来的数据写到文本文件了
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
socket.close();
bufferedWriter.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
客户端:
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo4 {
public static void main(String[] args) {
try {
// 创建客户端socket对象
Socket socket = new Socket("192.168.1.5", 10086);
// 创建键盘录入
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
// 将字节输出流包装为字符缓冲流
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = null;
// 赋值,判断否为空,不为空则发送数据
while ((line = bufferedReader.readLine()) != null) {
// 输入787 跳出循环
if (line.equals("787")) {
break;
}
// 将数据传递给服务器端
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
socket.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
通过TCP协议上传文件:
注意:
在文本文件传输完成的时候,会出现相互等待现象,文件传完了,服务器却还没有返回信息为什么呢?
- 按照我们正常的想法加入反馈信息,结果却没有反应,出现了问题。为什么呢?
// 服务器端添加的反馈代码
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("文件上传成功");
bw.newLine();
bw.flush();
// 客户端添加的接受反馈的代码
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String s = br.readLine();
System.out.println(s);
- 这是因为,读取文本文件是可以null作为结束信息的,但是,通道内是不能这样结束信息的,所以服务器根本就不知道你结束了,而你还像让服务器给你反馈,所以,就相互等待了。
解决方法:
-
再多写一条数据,告诉服务器,读到这条数据说明我就结束了,你也就结束吧
这样虽然能解决问题,但是不好,假如文本文件里正好有一条语句和你的结束语句相同,则会提前结束。 -
Socket对象提供了一种解决方案:
public void shutdownOutput() throws IOException
禁用此套接字的输出流。 对于TCP套接字,任何先前写入的数据将被发送,随后是TCP的正常连接终止序列。 如果在套接字上调用shutdownOutput()之后写入套接字输出流,则流将抛出IOException。
发送:
import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientDemo5 {
public static void main(String[] args) {
try {
Socket socket = new Socket("192.168.1.5", 10088);
BufferedReader bufferedReader = new BufferedReader(new FileReader("src/swingdemo/lianxi/Test.java"));
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
bufferedReader.close();
socket.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
接收:
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo5 {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(10088);
Socket socket = serverSocket.accept();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("one.java"));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line);
bufferedWriter.newLine();
bufferedWriter.flush();
}
// 当文件上传成功时,给客户端一个返回
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
bw.write("文件上传成功");
bw.newLine();
bw.flush();
bufferedWriter.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
实现多个客户端访问服务器端,多线程实现:
代码基本上没有大的改变,所以后面代码就没有加注释。
服务器端:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo7 {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(10086);
while (true) {
Socket socket = serverSocket.accept();
new Thread(new ServerThreadDemo(socket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.*;
import java.net.Socket;
public class ServerThreadDemo implements Runnable {
private Socket s;
public ServerThreadDemo(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
BufferedInputStream bis = new BufferedInputStream(s.getInputStream());
// 用当前毫秒值为图片命名
String name = System.currentTimeMillis()+".jpg";
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(name));
byte[] by = new byte[1024];
int len = 0;
while ((len = bis.read(by)) != -1) {
bos.write(by,0,len);
bos.flush();
}
OutputStream outputStream = s.getOutputStream();
outputStream.write("图片上传成功".getBytes());
s.close();
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
import java.io.*;
import java.net.Socket;
public class ClientDemo7 {
public static void main(String[] args) {
try {
Socket socket = new Socket("192.168.1.5",10086);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:\\Users\\shaoxiong\\Pictures\\Saved Pictures\\星空.jpg"));
byte[] by = new byte[1024];
int len = 0;
while((len = bis.read(by))!=-1){
bos.write(by,0,len);
bos.flush();
}
socket.shutdownOutput();
InputStream inputStream = socket.getInputStream();
byte[] byy = new byte[1024];
int read = inputStream.read(byy);
String s = new String(byy,0,read);
System.out.println(s);
socket.close();
bis.close();
}catch (IOException e){
e.printStackTrace();
}
}
}