目录
指网络上的主机,通过不同的进程,以编程的方式实现网络通信(或称为网络数据传输)
即便是同一个主机,只要是不同进程,基于网络来传输数据, 也属于网络编程。
在一次网络数据传输时:
发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机
接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机
收发端:发送端和接收端两端,也简称为收发端
发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念
一般来说,获取一个网络资源,涉及到两次网络数据传输:
第一次:请求数据的发送
第二次:响应数据的发送
服务端:在常见的网络数据传输场景下,把提供服务的一方进程,称为服务端,可以提供对外服务
客户端:获取服务的一方进程,称为客户端。
客户端获取服务资源 ;客户端保存资源在服务端
Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基 于Socket套接字的网络程序开发就是网络编程
分为:流套接字、数据报套接字和原始套接字
使用传输层TCP协议(传输控制协议),传输层协议。
TCP: 有连接 可靠传输 面向字节流 有接收缓冲区,也有发送缓冲区 大小不限
DatagramSocket 是UDP Socket,用于发送和接收UDP数据报
实现 同一主机下的进程 -> 进程 ---- 请求响应模型
- import java.io.*;
- import java.net.*;
- import java.util.Arrays;
- import java.util.HashMap;
-
-
- public class Server {
- // 1. 写在文件中
- // 2. 写在数据库中
- private static final HashMap<String, String> map = new HashMap<>();
- static {
- map.put("apple", "苹果");
- map.put("banana", "香蕉");
- }
-
- public static void main(String[] args) throws IOException {
- Log.println("服务器 bind 在 8080 端口上");
- DatagramSocket socket = new DatagramSocket(8080);
- while (true) {
- // 读取请求
- byte[] buf = new byte[1024];
- DatagramPacket received = new DatagramPacket(buf, buf.length);
-
- Log.println("服务器准备接收数据");
- socket.receive(received); // 服务器会阻塞
- Log.println("服务器接收到了数据");
- // 处理
- // 对方的地址(ip)
- InetAddress address = received.getAddress();
- Log.printf("对方的地址是: %s\r\n", address);
- // 对方的端口(port)
- int port = received.getPort();
- Log.printf("对方的端口是: %d\r\n", port);
- // 真正收到的数据长度
- int n = received.getLength();
- Log.printf("一共收到的数据长度: %d\r\n", n);
- // 真正有用的数据
- byte[] actualData = Arrays.copyOf(buf, n);
- // byte[] + 字符集编码 -> String
- String request = new String(actualData, 0, actualData.length, "UTF-8");
- Log.printf("请求是: %s\r\n", request);
- // 分析请求
- if (!request.startsWith("我想进行翻译工作\r\n")) {
- // 不符合应用层的请求协议,不做处理
- Log.println("请求头不符合格式");
- continue;
- }
-
- if (!request.endsWith("\r\n")) {
- // 不符合应用层的请求协议,不做处理
- Log.println("请求尾不符合格式");
- continue;
- }
-
- String englishWord = request.substring("我想进行翻译工作\r\n".length(), request.length() - 2);
-
- Log.println("收到的英文单词是: " + englishWord);
- String chineseWord = map.getOrDefault(englishWord, "不认识");
- Log.println("翻译后的中文是: " + chineseWord);
-
- // 发送响应
- String response = String.format("好的\r\n%s\r\n", chineseWord);
- Log.println("响应是: " + response);
- // String + 字符集编码 -> byte[]
- byte[] bytes = response.getBytes("UTF-8");
-
- Log.println("准备发送响应");
- DatagramPacket sent = new DatagramPacket(
- bytes, 0, bytes.length,
- address,
- port
- );
- socket.send(sent);
-
- Log.println("响应发送成功");
- }
- }
- }
- import java.io.*;
- import java.net.DatagramPacket;
- import java.net.DatagramSocket;
- import java.net.InetAddress;
- import java.util.Scanner;
-
-
- public class Client {
- public static void main(String[] args) throws IOException {
- DatagramSocket socket = new DatagramSocket(9999);
- // 目前服务器在本机: 127.0.0.1
- // 目前服务器的端口是: 8080
- Scanner sc = new Scanner(System.in);
- while (sc.hasNextLine()) {
- String word = sc.nextLine();
-
- // 准备发送请求
- String request = "我想进行翻译工作\r\n" + word + "\r\n";
-
- byte[] bytes = request.getBytes("UTF-8");
- DatagramPacket sent = new DatagramPacket(
- bytes,
- 0,
- bytes.length,
- InetAddress.getLoopbackAddress(),
- 8080
- );
-
- socket.send(sent);
-
- byte[] buf = new byte[1024];
- DatagramPacket received = new DatagramPacket(buf, buf.length);
-
- socket.receive(received);
- int n = received.getLength();
- String response = new String(buf, 0, n, "UTF-8");
- System.out.println(response);
- }
- }
- }
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
-
-
- public class Log {
- public static void println(Object o) {
- Date date = new Date();
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- String now = format.format(date);
-
- System.out.println(now + ": " + o.toString());
- }
-
- public static void print(Object o) {
- Date date = new Date();
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- String now = format.format(date);
-
- System.out.print(now + ": " + o.toString());
- }
-
- public static void printf(String fmt, Object... args) {
- Date date = new Date();
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- String now = format.format(date);
-
- System.out.printf(now + ": " + fmt, args);
- }
-
- public static void main(String[] args) {
- print("Hello\r\n");
- println("你好");
- printf("%s %d", "Go", 13);
- }
- }
使用传输层UDP协议(用户数据报协议),传输层协议。
UDP: 无连接 不可靠传输 面向数据报 有接收缓冲区,无发送缓冲区 大小受限:一次最多传输64K
ServerSocket 是创建TCP服务端Socket的API
实现 同一主机下的进程 -> 进程 ---- 请求响应模型
- import java.io.*;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.HashMap;
- import java.util.Scanner;
-
-
- public class Server {
- private static final HashMap<String, String> map = new HashMap<>();
- static {
- map.put("apple", "苹果");
- map.put("banana", "香蕉");
- }
-
- public static void main(String[] args) throws IOException {
- // 1. 创建 socket
- Log.println("服务器启动,监听在 TCP:8080 端口");
- ServerSocket serverSocket = new ServerSocket(8080);
- // 进行循环
- while (true) {
- // 1. 接通连接(电话) —— accept
- Log.println("等待新的客户端连接上来");
- Socket socket = serverSocket.accept(); // 阻塞
- Log.println("有新的客户端连接上来");
-
- InetAddress inetAddress = socket.getInetAddress();
- Log.println("对方的地址: " + inetAddress);
- int port = socket.getPort();
- // 由于没有设定,会随机分配一个端口
- Log.println("对方的端口: " + port);
-
- // is: 用于读数据
- InputStream is = socket.getInputStream();
- // os: 用于写数据
- OutputStream os = socket.getOutputStream();
-
- // 2. 读取请求
- Scanner scanner = new Scanner(is, "UTF-8");
- String header = scanner.nextLine();
- // TODO: 做请求格式检查
- String englishWord = scanner.nextLine();
- Log.println("英文单词: "+ englishWord);
-
- // 3. 处理业务
- String chineseWord = map.getOrDefault(englishWord, "不认识");
- Log.println("中文单词: "+ chineseWord);
-
- // 4. 发送响应
- // 好的\r\n苹果\r\n
- // OutputStream封装 -> OutputStreamWriter封装 -> PrintWriter
- OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
- PrintWriter printWriter = new PrintWriter(writer);
-
- Log.println("服务器进行发送");
- printWriter.printf("好的\r\n%s\r\n", chineseWord);
- printWriter.flush();
- Log.println("服务器发送成功");
-
- socket.close();
- }
- }
- }
- import java.io.*;
- import java.net.Socket;
- import java.util.Scanner;
-
-
- public class Client {
- public static void main(String[] args) throws IOException {
- Socket socket = new Socket("127.0.0.1", 8080);
-
- InputStream is = socket.getInputStream();
- Scanner scanner = new Scanner(is, "UTF-8");
-
- OutputStream os = socket.getOutputStream();
- OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
- PrintWriter printWriter = new PrintWriter(writer);
-
- // 发送请求
- printWriter.printf("我想进行翻译工作\r\napple\r\n");
- printWriter.flush();
-
- // 读取响应
- String header = scanner.nextLine(); // 好的
- String word = scanner.nextLine(); // 苹果
- System.out.println(word);
-
- socket.close();
- }
- }
- import java.text.DateFormat;
- import java.text.SimpleDateFormat;
- import java.util.Date;
-
-
- public class Log {
- public static void println(Object o) {
- Date date = new Date();
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- String now = format.format(date);
-
- System.out.println(now + ": " + o.toString());
- }
-
- public static void print(Object o) {
- Date date = new Date();
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- String now = format.format(date);
-
- System.out.print(now + ": " + o.toString());
- }
-
- public static void printf(String fmt, Object... args) {
- Date date = new Date();
- DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
- String now = format.format(date);
-
- System.out.printf(now + ": " + fmt, args);
- }
-
- public static void main(String[] args) {
- print("Hello\r\n");
- println("你好");
- printf("%s %d", "Go", 13);
- }
- }
此时需要检查进程B绑定的是哪个端口,再查看该端口被哪个进程占用
如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B
如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口
TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:
短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以 多次收发数据。
- import java.io.*;
- import java.net.Socket;
- import java.util.Scanner;
-
- public class Client {
- public static void main(String[] args) throws IOException {
- while (true) {
- Socket socket = new Socket("127.0.0.1", 8080); // 拨号
-
- InputStream is = socket.getInputStream();
- Scanner scanner = new Scanner(is, "UTF-8");
-
- OutputStream os = socket.getOutputStream();
- OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
- PrintWriter printWriter = new PrintWriter(writer);
-
- // 发送请求
- printWriter.printf("我是Java19班的\r\napple\r\n");
- printWriter.flush();
-
- // 读取响应
- String header = scanner.nextLine(); // 好的
- String word = scanner.nextLine(); // 苹果
- System.out.println(word);
-
- socket.close(); // 挂断电话
- }
- }
- }
- import java.io.*;
- import java.net.Socket;
- import java.util.Scanner;
-
- public class Client {
- public static void main(String[] args) throws IOException {
- Socket socket = new Socket("127.0.0.1", 8080); // 拨号
-
- for (int i = 0; i < 3; i++) {
- InputStream is = socket.getInputStream();
- Scanner scanner = new Scanner(is, "UTF-8");
-
- OutputStream os = socket.getOutputStream();
- OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
- PrintWriter printWriter = new PrintWriter(writer);
-
- // 发送请求
- printWriter.printf("我是Java19班的\r\napple\r\n");
- printWriter.flush();
-
- // 读取响应
- String header = scanner.nextLine(); // 好的
- String word = scanner.nextLine(); // 苹果
- System.out.println(word);
-
- }
- //socket.close(); // 挂断电话
- }
- }
- import java.io.*;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.HashMap;
- import java.util.Scanner;
-
-
- public class Server {
- private static final HashMap<String, String> map = new HashMap<>();
- static {
- map.put("apple", "苹果");
- map.put("banana", "香蕉");
- }
-
- public static void main(String[] args) throws IOException {
- // 1. 创建 socket
- Log.println("服务器启动,监听在 TCP:8080 端口");
- ServerSocket serverSocket = new ServerSocket(8080);
- // 进行循环
- while (true) {
- // 1. 接通连接(电话) —— accept
- Log.println("等待新的客户端连接上来");
- Socket socket = serverSocket.accept(); // 阻塞
- Log.println("有新的客户端连接上来");
-
- InetAddress inetAddress = socket.getInetAddress();
- Log.println("对方的地址: " + inetAddress);
- int port = socket.getPort();
- Log.println("对方的端口: " + port);
-
- // is: 用于读数据
- InputStream is = socket.getInputStream();
- Scanner scanner = new Scanner(is, "UTF-8");
- // os: 用于写数据
- OutputStream os = socket.getOutputStream();
- // OutputStream -> OutputStreamWriter -> PrintWriter
- OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
- PrintWriter printWriter = new PrintWriter(writer);
-
- // client 挂断电话,表示本次连接的请求响应处理结束了
- // client 通过关闭连接的方式来体现
- // is 读到了 EOS(-1)
- // scanner hasNextLine()
- while (scanner.hasNextLine()) {
- // 2. 读取请求
- String header = scanner.nextLine(); // "我是 Java19班的" // 阻塞
- // TODO: 做请求格式检查
- String englishWord = scanner.nextLine();
- Log.println("英文单词: " + englishWord);
-
- // 3. 处理业务
- String chineseWord = map.getOrDefault(englishWord, "不认识");
- Log.println("中文单词: " + chineseWord);
-
- // 4. 发送响应
- // 好的\r\n苹果\r\n
-
- Log.println("服务器进行发送");
- printWriter.printf("好的\r\n%s\r\n", chineseWord);
- printWriter.flush(); // 不要忘记 flush
- Log.println("服务器发送成功");
- }
-
- socket.close();
- }
- }
- }
- import java.io.*;
- import java.net.Socket;
- import java.util.Scanner;
-
- public class Client {
- public static void main(String[] args) throws IOException {
- Log.println("准备建立连接");
- Socket socket = new Socket("182.254.132.183", 8080); // 拨号
- Log.println("连接已经建立");
- Scanner systemInScanner = new Scanner(System.in);
-
- while (systemInScanner.hasNextLine()) {
- String w = systemInScanner.nextLine();
-
- InputStream is = socket.getInputStream();
- Scanner socketScanner = new Scanner(is, "UTF-8");
-
- OutputStream os = socket.getOutputStream();
- OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");
- PrintWriter printWriter = new PrintWriter(writer);
-
- // 发送请求
- printWriter.printf("我是Java19班的\r\n%s\r\n", w);
- printWriter.flush();
-
- // 读取响应
- String header = socketScanner.nextLine(); // 好的
- String word = socketScanner.nextLine(); // 苹果
- System.out.println(word);
-
- }
- //socket.close(); // 挂断电话
- }
- }
长连接模式下,由于TCP面向的是字节流