网络编程基础学习与整理
网络编程
1网络通信的要素
1.1网络模型整理
1.2 IP
- ip定义:Internet Protocol,IP只为主机提供一种无连接、不可靠的、尽力而为的数据包传输服务,可以唯一地定位一台网络计算机。(127.0.0.1为本机的localhost)
- IP地址分类:
- ipv4 : 由四个字节组成,例如 127.0.0.1 网络号+主机号
- ipv6 : 128位,8组,每组4个16进制数,例如 2001:0db8:85a3:08d3:1319:8a2e:0370:7344,如果0000则省略
- 公网(互联网)-私网(局域网)
- 192.168.xx.xx属于局域网,专门给组织内部使用
- ABCDE类地址根据网络号划分,如图所示,其中D类供组播使用,E类供科研使用
1.3 端口
- 端口表示计算机上一个程序的进程
- 规定限制在2^16 0~65535之间
- TCP UDP协议端口相隔离,但在单个协议下端口不能重复
- 端口分类:
- 公有端口:0~1023
- HTTP:80
- HTTPS:443
- FTP:21
- Telnet:23
- 程序注册端口:1024~49151,程序或者用户使用
- Tomcat:8080
- MySQL:3306
- Oracle:1521
- Redis:6379
- 公有端口:0~1023
- 动态端口、私有端口:49152~65535 不建议使用
1.4 传输层通信协议
- TCP/UDP对比:
- TCP:用户传输协议(打电话)
- 连接、稳定
- 三次握手、四次挥手
三次握手: 第一次握手:客户端传输SYN=i给服务端,状态置为SYN_SEND; 第二次握手:服务端接受SYN=i后,返回ACK=i+1且返回SYN=j,状态置为SYN_RECV; 第三次握手:客户端接受ACK后状态置为ESTABLISHED,发送ACK=j+1,服务端接受后状态置为ESTABLISHED; 四次挥手: (1)客户端发送FIN,状态置为FIN_WAIT1,接受ACK后改为FIN_WAIT2 (2)服务端接受FIN,返回ACK,状态置为CLOSE_WAIT (3)服务端数据传输完毕,返回FIN,状态改为LAST_ACK,接受客户端ACK后改为CLOSED (4)客户端接受FIN后状态置为TIME_WAIT,2MS后置为CLOSED 中间状态: CLOSED:初始状态 LISTEN:监听中 SYN_SENT:发送SYN=i给服务端后 SYN_RCVD: 接受客户端的SYN=i后 ESTABLISHED:已连接 FIN_WAIT_1:ESTABLISHED状态下发送FIN=i给客户端后 FIN_WAIT_2:ESTABLISHED状态下发送FIN=i给客户端后接收到ACK返回则为2 CLOSE_WAIT:FIN接受方,收到后返回ACK并置为CLOSE_WAIT,数据传输完毕后返回FIN且状态改为LAST_ACK TIME_WAIT:FIN发起方,收到对方返回的FIN后发送ACK,置为TIME_WAIT,2MS后会改为CLOSED LAST_ACK:CLOSE_WAIT数据传输完毕后返回FIN且状态改为LAST_ACK CLOSING:双方同时发起FIN
- UDP:用户数据报协议(发短信)
- 不连接、不稳定
- 没明确的客户端服务端区分
1.5 TCP通信连接
- 客户端:
1.建立Socket连接服务器Socket
2.发送消息public static void main(String[] args) { Socket socket = null; OutputStream outputStream = null; try { //获取服务端地址和端口 InetAddress serverIp = InetAddress.getByName("localhost"); int port = 9999; //创建Socket连接 socket = new Socket(serverIp, port); //输出流输出信息 outputStream = socket.getOutputStream(); outputStream.write("hello".getBytes()); } catch (Exception e) { e.printStackTrace(); } finally { //关闭资源 } }
- 服务端
1.建立ServerSocket
2.等待客户端连接
3.接受信息并处理
public static void main(String[] args) {
try {
//等待Socket
java.net.ServerSocket serverSocket = new java.net.ServerSocket(9999);
while (true) {
//等待客户端连接
Socket clientSocket = serverSocket.accept();
//读取
InputStream inputStream = clientSocket.getInputStream();
//管道流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
System.out.println(bos.toString());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
}
}
1.6 TCP文件传输
- 客户端:
public static void main(String[] args) {
try {
InetAddress inetAddress = InetAddress.getByName("localhost");
int port = 8888;
Socket socket = new Socket(inetAddress, port);
OutputStream outputStream = socket.getOutputStream();
FileInputStream fileInputStream = new FileInputStream(new File("test.jpg"));
int len;
byte[] buffer = new byte[1024];
while ((len = fileInputStream.read(buffer)) != -1) {
outputStream.write(buffer,0,len);
}
//通知服务端输出关闭
socket.shutdownOutput();
int len2;
byte[] buffer2 = new byte[1024];
//接受服务端返回信息并打印
InputStream inputStream = socket.getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while ((len2 = inputStream.read(buffer2)) != -1) {
byteArrayOutputStream.write(buffer2, 0, len2);
}
System.out.println(byteArrayOutputStream.toString());
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭资源
}
}
- 服务端:
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream("new.jpg");
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer,0,len);
}
//给客户端返回结束信息
OutputStream outputStream = accept.getOutputStream();
outputStream.write("end transfer".getBytes());
//结束后需要关闭套接字,否则会导致客户端读取中断抛出异常
accept.close();
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭资源
}
}
1.7 UDP发送信息
- 定义:不用建立稳定连接,往指定IP 端口发送信息,不管有没有接受成功
- 发送端:
public static void main(String[] args) throws Exception {
InetAddress localhost = InetAddress.getByName("localhost");
int port = 9090;
//1.建立socket
DatagramSocket datagramSocket = new DatagramSocket();
//2.建立数据包
String msg = "message";
DatagramPacket datagramPacket = new DatagramPacket(msg.getBytes(), 0,
msg.getBytes().length, localhost, port);
//3.发送包
datagramSocket.send(datagramPacket);
//4.关闭流
datagramSocket.close();
}
- 接受端:
public static void main(String[] args) throws Exception {
//开放端口
DatagramSocket datagramSocket = new DatagramSocket(9090);
//接受数据包
byte[] buffer = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(buffer, 0, buffer.length);
datagramSocket.receive(datagramPacket);
System.out.println(datagramPacket.getAddress());
System.out.println(new String(datagramPacket.getData(),0,datagramPacket.getLength()));
//关闭连接
datagramSocket.close();
}
1.8 UDP实现简易聊天
//聊天工具类,构造方法记录本端端口及目标端口,新起线程持续监听控制台输入并传输
//到信息到目标端口处,本端线程持续监听本端IP,并打印接受到的信息
public class TalkSendUtil {
DatagramSocket targetSocket = null;
DatagramSocket receiveSocket = null;
DatagramPacket receivePacket = null;
DatagramPacket sendPacket = null;
private int toPort;
public TalkSendUtil(int toPort, int localPort) throws Exception {
this.toPort = toPort;
targetSocket = new DatagramSocket();
receiveSocket = new DatagramSocket(localPort);
}
public void invoke() throws Exception {
new Thread(() -> {
while (true) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
try {
String data = bufferedReader.readLine();
sendPacket = new DatagramPacket(data.getBytes(), 0, data.getBytes().length, InetAddress.getByName("localhost"), toPort);
targetSocket.send(sendPacket);
if (data.equals("bye")) {
targetSocket.close();
receiveSocket.close();
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
while (true) {
byte[] buffer = new byte[1024];
receivePacket = new DatagramPacket(buffer, 0, buffer.length);
try{
receiveSocket.receive(receivePacket);
}catch(SocketException exception){
System.out.println("聊天已关闭");
return;
}
String receive = new String(receivePacket.getData(), 0, receivePacket.getLength());
System.out.println(receivePacket.getPort() + ":" + receive);
if (receive.equals("bye")) {
receiveSocket.close();
break;
}
}
}
}
聊天者1:
public class UDPChat1 {
public static void main(String[] args) throws Exception {
TalkSendUtil talkSendUtil = new TalkSendUtil(1112,1111);
talkSendUtil.invoke();
}
}
聊天者2:
public class UDPChat2 {
public static void main(String[] args) throws Exception {
TalkSendUtil talkSendUtil = new TalkSendUtil(1111,1112);
talkSendUtil.invoke();
}
}
效果图: