网络编程
一、网站编程和网络编程
1.网站编程:更基于应用的概念,通过web服务器(tomcat,jetty)封装好了网络编程的内容,只关注业务和数据
2.网络编程:更底层的,本质上接近于IO,网络端到网络端的连接和数据传递。
二、网络相关概念:
1.网络模型:
(1)七层模型
(2)TCP/IP模型
2.网络协议:
(1)TCP:建立连接,形成传输数据的通道;在连接中进行大数据量传输;通过三次握手完成连接,是可靠协议;必须建立连接,效率会稍低。
(2)UDP:将数据源和目的封装成数据包中,不需要建立连接;每个数据报的大小限制在64k;因为无需连接,是不可靠协议,不需要建立连接,速度快。
(3)IP:IPV4和IPV6
(4)HTTP/HTTPS
3.相关命令:
ipcoonfig
ping IP
netstat
…
IP:互联网上让每台 主机有一个唯一的IP地址
常见的端口号:
FTP:21
mySQL:3306
oracle:1521
web:80
tomcat:8080
二、套接字Socket:
网络端与网络端的连接,连接的管道其实也是封装好的对象,管道的端其实就是一个地址。套接字就是决定网络端的地址,根据套接字可以找到对应的网络端。
IP:找到对应的唯一的主机。
端口:找到对应的主机上的进程(应用),虚拟的,针对主机上运行的对外通信的应用的一个编号。
三、网络编程的模型
1.服务器端和客户端
多个客户端可以跟一个服务器端建立联系
IO中建立管道,就是一个对象(两端)
网络编程建立管道:客户端对象,服务器端对象
客户端对象:Socket
服务器端对象:ServerSocket
2.网络编程的第一层模型
客户端设定IP和端口,就能将请求发送到对应的IP主机上的对应端口上。
服务器端server监听设置的端口,当有请求到达的时候,接收请求。
3.网络编程的第二层模型:
当有客户端请求到达服务器端的时候,server
能够监听到,生成一个新的socket对象,处理客户端请求,并且server可以继续监听新的请求。
4.网络编程的第三层模型:
通过socket,得到输入输出流,真正建立数据传输的通道。
客户端socket:输入就是向服务器端发送数据,输出就是获得服务器端数据。
服务器端socket:输入就是接收客户端数据,输出就是向客户端发送数据。
四、使用UDP协议实现网络通信:
1.InetAddress的概述和测试
InetAdress是用来表示IP地址的类。
import java.net.InetAddress;
import java.net.UnknownHostException;
/*
* InetAddress:此类表示互联网协议(IP)地址。
*/
public class InetAddressDemo {
public static void main(String[] args) throws UnknownHostException {
//static InetAddress getByName(String host);
//InetAddress address = InetAddress.getByName("LAPTOP-43VV9JPS");//发送给当前设备
InetAddress address = InetAddress.getByName("192.168.1.107");//发送给当前设备
System.out.println(address);
String hostAddress = address.getHostName();//获取主机名
String hostName = address.getHostAddress();//获取IP地址
System.out.println(hostName);
System.out.println(hostAddress);
}
}
1.使用UDP协议发送数据
UDP是无需连接,不可靠传输协议,因此不管有没有接收端,发送端都可以直接运行。
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/*
* 使用UDP协议发送数据
* 1.创建发送端的Socket对象、
* 2.创建数据并打包
* 3.发送数据
* 4.释放资源
*
* DatagramSocket:此类表示用来发送和接收收据,基于UDP协议的
* DAtagramSocket():创建Socket对象,并随机分配端口号。
* DAtagramSocket(int port):创建Socket对象,并指定口号。
*/
public class SendDemo {
public static void main(String[] args) throws SocketException, IOException {
//* 1.创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket();
//* 2.创建数据并打包
/*
* DatagramPacket:此类表示数据包
* 数据byte[]
* 设备地址:IP
* 进程的地址:端口号
* DatagramPacket(byte[] buf, int length, InetAddress address, int port)
*/
String s = "hello UDP";
byte[] bys = s.getBytes();
int length = bys.length;
InetAddress address = InetAddress.getByName("LAPTOP-43VV9JPS");//发送给当前设备
int port = 8888;
//打包
DatagramPacket dp = new DatagramPacket(bys, length, address,port) ;
//* 3.发送数据
ds.send(dp);
//* 4.释放资源b
ds.close();
}
}
3.使用UDP协议接收数据
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/*
* 使用UDP协议接收数据
* 1.创建接收端的Socket对象、
* 2.接收数据并解析
* 3.输出数据
* 4.释放资源
*
* DatagramSocket:此类表示用来发送和接收收据,基于UDP协议的
* DAtagramSocket():创建Socket对象,并随机分配端口号。
* DAtagramSocket(int port):创建Socket对象,并指定口号。
*/
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
// 1.创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(8888);
// 2.接收数据并解析
//DatagramPacket(byte[] buf, int length) 构造一个 DatagramPacket用于接收长度的数据包 length 。
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys,bys.length);
System.out.println(1);
ds.receive(dp);//阻塞
System.out.println(2);
//解析数据
//InetAddress getAddress() 返回该数据报发送或接收数据报的计算机的IP地址。
InetAddress address = dp.getAddress();
//byte[] getData()返回数据缓冲区。
byte[] data = dp.getData();
//int getLength() 返回要发送的数据的长度或接收到的数据的长度
int length = dp.getLength() ;
// 3.输出数据
System.out.println("send---->"+address.getHostAddress());
System.out.println(new String(data,0,length));
// 4.释放资源
ds.close();
}
}
注意事项:
a.端口号错误,数据可以正常发出,不会出现异常,但是收不到数据。
b.端口号不能不能重复使用,否则会报异常(端口号已经绑定了)
五、使用TCP协议实现网络通信(客户端服务端):
TCP需要建立连接,是可靠传输协议。
1.客户端代码:
/*
* 通过socket获得IO管道,纯IO操作。
*/
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientTest1 {
public static void main(String[] args) throws UnknownHostException, IOException {
// 1.建立连接到服务器端的socket
Socket clientSocket = new Socket("192.168.1.107",1000);
//2.客户端向服务器端发送消息:通过socket建立管道
OutputStream os = clientSocket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("hello");
pw.flush();
pw.close();
//3.关闭socket连接
clientSocket.close();
}
}
2.服务器端代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest1 {
public static void main(String[] args) throws IOException {
// 1.建立serverSocket,监听对应端口
ServerSocket server = new ServerSocket(1000);
while(true) {
//2.server监听端口,监听成功,生成Socket
Socket socket = server.accept();
//3.socket实现纯IO
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String str = br.readLine();
br.close();
System.out.println(str);
//4.本次请求的socket的关闭
socket.close();
}
}
}
记录一个自己踩的小坑
我是先运行客户端代码,然后再运行服务的代码的,就报错了
Exception in thread “main” java.net.ConnectException: Connection refused: connect
解决办法:
客户端和服务器应该分别启动两个JVM,eclipse一个时刻只能运行一个程序
,可以启动两个eclipse分别运行这两个程序(两次双击eclipse图标就可以,不过第二个要创建一个新的工作区workspace)。一个eclipse也可以,但要先运行服务器端代码,然后启动客户端代码(运行前要换一个ServerSocket端口号,程序结束后上次运行程序使用的端口不会立即释放)。
3.案例练习:使用TCP协议发送数据,并将接收到的数据转换成大写返回。
客户端发出数据
服务端接收数据
服务端转换数据
服务端发出数据
客户端接收数据
(1)客户端代码
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class ClienDemo {
/*
* 使用TCP协议发送数据,并将接收到的数据转换成大写返回。
* 1.创建发送端(客户端)Socket对象
* 2.获取输出流对象
* 3.发送数据
* 4.释放资源
*/
public static void main(String[] args) throws IOException {
//* 1.创建发送端(客户端)Socket对象
Socket s = new Socket(InetAddress.getByName("LAPTOP-43VV9JPS"),10010);
//* 2.获取输出流对象
OutputStream os = s.getOutputStream();
//* 3.发送数据
os.write("tcp,im coming".getBytes());
//接收数据
//获取输入流对象
InputStream is = s.getInputStream();
//获取数据
byte[] bys = new byte[1024];
int len;
len = is.read(bys);
//输出数据
String str = new String(bys,0,len);
System.out.println(str);
//* 4.释放资源
s.close();
}
}
(2)服务端代码
//1.创建服务端Socket对象
//2.监听
//3.获取输入流对象
//4.获取数据
//5.输出数据
//6.释放资源
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static void main(String[] args) throws IOException {
//1.创建服务端Socket对象
ServerSocket ss = new ServerSocket(10010);
//2.监听
Socket s = ss.accept();
//3.获取输入流对象
InputStream is = s.getInputStream();
//4.获取数据
byte[] bys = new byte[1024];
int len;
len = is.read(bys);
//5.输出数据
String str = new String(bys,0,len);
System.out.println(str);
//转换数据
String supperstr = str.toUpperCase();
//获取输出流对象
OutputStream os =s.getOutputStream();
//发出数据
os.write(supperstr.getBytes());
//6.释放资源
s.close();
// ss.close();//服务端一般不关闭
// is.close();
}
}