网络编程
概述
- 在网络通信协议下,不同计算机上运行的程序,可以进行数据传输
三要素
- IP地址:设备在网络中的地址,是唯一标识;
- 端口:应用程序在设备中的唯一标识;
- 协议:数据在网络传输的规则,常见的协议有UDP协议和TCP协议。
- UDP协议
- 用户数据报协议
- UDP是面向无连接通信协议,速度快,有大小限制一次最多发送64k,数据不安全,易丢失数据。
- TCP协议
- 传输控制协议
- TCP协议是面向连接的通信协议,速度慢,没有大小限制,数据安全。
IP地址
常用命令:
- ipconfig:查看本机IP地址
- ping IP地址:检查网络是否连通
特殊IP地址:
- 127.0.0.1:是会送地址也称本地回环地址,可以代表本机的IP地址,一般用来测试使用
InetAddress
- static InetAddress getByName(String host) :确定主机名称的IP地址,主机名称可以是机器名称,也可以是IP地址
- String getHostName() :获取此IP地址的主机名
- String getHostAddress() :返回文本显示中的IP地址字符串
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressTest {
public static void main(String[] args) throws UnknownHostException {
InetAddress address = InetAddress.getByName("我也会飞啦");
String hostName = address.getHostName();
System.out.println("主机名为:"+hostName);
String hostAddress = address.getHostAddress();
System.out.println("IP为:"+hostAddress);
}
}
端口
端口号:用两个字节表示的整数,它的取值范围是065535。其中01023之间的端口号用于一些知名的网络服务或者应用。我们自己使用1024以上的端口号就可以了。一个端口号只能被一个应用程序使用。
UDP发送数据
举个栗子:发送礼物
- 找菜鸟驿站-----------创建发送端的DatagramSocket对象
- 打包礼物-----------创建数据,并把数据打包(DatagramPacket)
- 由驿站发送包裹------------调用DatagramSocket对象的方法发送数据
- 付钱走人-------------释放资源
import java.io.IOException;
import java.net.*;
public class ClientTest {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
String s = "送给挚爱的人的礼物";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 8888;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
ds.send(dp);
ds.close();
}
}
UDP接收数据
- 找接收点-------------创建接收端的DatagramSocket对象
- 找新箱子-------------创建一个箱子用于接收数据
- 接收礼物并放回箱子中--------------调用DatagramSocket的方法接收数据并放入箱子中
- 从箱子里获取礼物---------------解析数据包,并把数据放在控制台显示
- 拿完走人---------------释放资源
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class ServerTest {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(8888);
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
ds.receive(dp);
int length = dp.getLength();
System.out.println(new String(bytes,0,length));
ds.close();
}
}
UDP通信程序练习
按照下面的要求实现程序
- UDP发送数据:数据来自于键盘录入,直到输入的数据是回见,发送数据结束
- UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
发送端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;
public class ClientTest {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
DatagramSocket ds = new DatagramSocket();
while (true) {
String s = sc.nextLine();
if(s.equals("回见")){
break;
}
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("127.0.0.1");
int port = 8888;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
ds.send(dp);
}
ds.close();
}
}
接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ServerTest {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(8888);
while (true) {
byte[] bytes = new byte[1024];
DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
ds.receive(dp);
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
}
}
}
UDP的三种通信方式
- 单播:点对点通信
- 组播:一个发送端通过路由器将数据发送给一组接收端
- 广播:一个发送端通过路由器将数据发送给局域网中的所有接收端
UDP通信组播
组播地址:224.0.0.0239.255.255.255,其中224.0.0.0224.0.0.255位预留的组播地址
发送端
import java.io.IOException;
import java.net.*;
public class clientTest {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
String s = "hello组播";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("224.0.2.0");
int port = 8888;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
ds.send(dp);
ds.close();
}
}
接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
public class ServerTest {
public static void main(String[] args) throws IOException {
MulticastSocket ms = new MulticastSocket(8888);
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
ms.joinGroup(InetAddress.getByName("224.0.2.0"));
ms.receive(dp);
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
ms.close();
}
}
UDP通信广播
广播地址:255.255.255.255
发送端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class ClientTest {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket();
String s = "嘘!";
byte[] bytes = s.getBytes();
InetAddress address = InetAddress.getByName("255.255.255.255");
int port = 8888;
DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
ds.send(dp);
ds.close();
}
}
接收端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ServerTest {
public static void main(String[] args) throws IOException {
DatagramSocket ds = new DatagramSocket(8888);
DatagramPacket dp = new DatagramPacket(new byte[1024],1024);
ds.receive(dp);
byte[] data = dp.getData();
int length = dp.getLength();
System.out.println(new String(data,0,length));
ds.close();
}
}
TCP通信原理
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象;
通信之前要保证连接已经建立,通过Socket产生IO流来进行网络通信。
TCP发送数据的步骤
- 创建客户端的Socket对象(Socket)与指定服务端连接 Socket(String host,int port)
- 获取输出流,写数据OutputStream getOutputStream()
- 释放资源void close()
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class ClientTest {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
os.write("hello".getBytes());
os.close();
socket.close();
}
}
TCP接收数据
- 创建服务器端的Socket对象(ServerSocket) ServerSocket(int port)
- 监听客户端连接,返回一个Socket对象 Socket accept()
- 获取输入流,读数据,并把数据显示在控制台 InputStream getInputStream()
- 释放资源 void close()
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
Socket accept = ss.accept();
InputStream is = accept.getInputStream();
int b;
while ((b = is.read()) != -1){
System.out.println((char) b);
}
is.close();
ss.close();
}
}
注意
- accept方法是阻塞的,作用就是等待客户端连接;
- 客户端创建对象并连接服务器,此时是通过三次握手协议保证跟服务器之间的连接;
- 针对客户端来讲是往外写的,所以是输出流;针对服务器来讲是往里读的,所以是输入流;
- read方法也是阻塞的;
- 在关流的时候,还多了一个网服务器写结束标记的动作;
- 最后一次断开连接,通过四次挥手协议保证连接终止;
三此握手
四次挥手
TCP练习1
- 客户端:发送数据,接收服务器反馈
- 服务器:接收数据,给出反馈
发送端
import java.io.*;
import java.net.Socket;
public class ClientTest {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8888);
OutputStream os = socket.getOutputStream();
os.write("hello".getBytes());
socket.shutdownOutput();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
br.close();
os.close();
socket.close();
}
}
接收端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
Socket accept = ss.accept();
InputStream is = accept.getInputStream();
int b;
while ((b = is.read()) != -1){
System.out.println((char) b);
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("你是谁?");
bw.newLine();
bw.flush();
bw.close();
is.close();
accept.close();
ss.close();
}
}
TCP练习2
- 客户端:将本地文件上传到服务器,接收服务器的反馈
- 服务器:接收客户端上传的文件,上传完毕之后给出反馈。
客户端
import java.io.*;
import java.net.Socket;
public class ClientTest {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8888);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day24\\1.gif"));
OutputStream os = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);
int b;
while ((b = bis.read())!=-1){
bos.write(b);
}
socket.shutdownOutput();
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line;
while ((line = br.readLine())!=null){
System.out.println(line);
}
bis.close();
socket.close();
}
}
服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
Socket accept = ss.accept();
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day24\\2.gif"));
int b;
while ((b = bis.read())!=-1){
bos.write(b);
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
bos.close();
accept.close();
ss.close();
}
}
UUID
该类可以生成唯一且随机的字符串;
import java.util.UUID;
public class UUIDTest {
public static void main(String[] args) {
UUID uuid = UUID.randomUUID();
String s = uuid.toString().replace("-","");
System.out.println(s);
}
}
利用循环和UUID对练习2进行优化
只改动服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
Socket accept = null;
while (true) {
accept = ss.accept();
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day24\\"+ UUID.randomUUID().toString().replace("-","")+".gif"));
int b;
while ((b = bis.read())!=-1){
bos.write(b);
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
bos.close();
accept.close();
}
}
多线程+线程池进一步优化练习2
线程:
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class ThreadSockt implements Runnable {
private Socket accept;
public ThreadSockt(Socket accept) {
this.accept = accept;
}
@Override
public void run() {
BufferedOutputStream bos = null;
try {
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
bos = new BufferedOutputStream(new FileOutputStream("day24\\"+ UUID.randomUUID().toString().replace("-","")+".gif"));
int b;
while ((b = bis.read())!=-1){
bos.write(b);
}
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
bw.write("上传成功");
bw.newLine();
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bos !=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (accept != null){
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
只改进服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ServerTest {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,8,60, TimeUnit.SECONDS,new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
Socket accept = null;
while (true) {
accept = ss.accept();
ThreadSockt ts = new ThreadSockt(accept);
threadPoolExecutor.submit(ts);
}
}
}
|