概论
- 计算机网络:
- 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
- 网络编程的目的:数据交换,通信,传播交流信息
- 想要达到最高效果需要什么:
- 如何准确的定位到网络上的一台主机:192.168.1.100 : 端口,端口用来定位到计算机上的某个资源
- 找到了这个主机,如何传输数据?TCP/UDP
网络通信的要素
- 如何实现网络通信
- 得到通信双方地址:
- IP
- 端口号
- 规则:网络通信协议
- HTTP、FTP、SMTP、TCP、UDP
- 得到通信双方地址:
- 小结:
- 网络编程中有两个主要问题
- 如何准确的定位到网络上的一台或者多台主机
- 找到主机之后如何进行通信
- 网络编程中的要素
- IP和端口号
- 网络通信协议
- 网络编程中有两个主要问题
IP
-
IP地址类:InetAddress
- 唯一定位一台网络上的计算机
- 127.0.0.1:本机localhost
- IP地址的分类
- IPv4 / IPv6
- 公网 / 私网
- 域名:IP记不住,用域名表示IP
-
测试
// 测试IP public static void main(String[] args) { try { InetAddress inetAddress1 = InetAddress.getByName("127.0.0.1"); System.out.println(inetAddress1); // 查询本地主机地址 InetAddress inetAddress2 = InetAddress.getByName("localhost"); // 本机环路地址 InetAddress localHost = InetAddress.getLocalHost(); // 本机局域网地址 System.out.println(inetAddress2); System.out.println(localHost); // 查询网站IP地址 InetAddress inetAddress3 = InetAddress.getByName("www.baidu.com"); System.out.println(inetAddress3); // 常用方法 System.out.println(Arrays.toString(inetAddress2.getAddress())); // 返回byte[]形式的原始ip地址 System.out.println(inetAddress2.getCanonicalHostName()); // 规范的名字 System.out.println(inetAddress2.getHostAddress()); // ip System.out.println(inetAddress2.getHostName()); // 域名,或者自己电脑的名字 } catch (UnknownHostException e) { e.printStackTrace(); } }
端口
-
端口表示计算机上的一个程序的进程
-
不同的进程有不同的端口号
-
端口范围:0~65535,不同协议端口不冲突,如TCP和UDP可以同时使用各自的80端口
-
端口分类
- 公有端口0~1023
- HTTP:80
- HTTPS:443
- FTP:21
- 程序注册端口:1024~49151,分配用户或者程序
- Tomcat:8080
- MySQL:3306
- Oracle:1521
- 动态、私有端口:49152~65535
netstat -ano # 查看所有使用中的端口 netstat -ano|findstr "端口号" # 查看具体端口
- 公有端口0~1023
-
-
测试
public static void main(String[] args) { InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080); System.out.println(socketAddress.getAddress()); // ip System.out.println(socketAddress.getHostName()); // 主机名 System.out.println(socketAddress.getPort()); // 端口 }
通信协议
- 协议是一种约定,用于规定通信方式
- 网络通信协议:速率、传输码率、代码结构、传输控制…
- TCP / IP 协议簇
- TCP:用户传输协议
- UDP:用户数据报协议
TCP
-
客户端
- 连接服务器
Socket
- 发送消息
// 客户端 public class TcpClientDemo1 { public static void main(String[] args) { try { // 1. 要知道服务器的地址,端口号 InetAddress serverIP = InetAddress.getByName("127.0.0.1"); int port = 9999; try ( // 2. 创建一个socket连接 Socket socket = new Socket(serverIP, port); // 3. 发送消息IO流 OutputStream os = socket.getOutputStream(); ) { os.write("你好,世界".getBytes()); } } catch (Exception e) { e.printStackTrace(); } } }
- 连接服务器
-
服务器
- 建立服务的端口
ServerSocket
- 等待用户的连接
accept
- 接受用户的消息
// 服务端 public class TcpServerDemo1 { public static void main(String[] args) { try ( // 1. 得有一个地址 ServerSocket serverSocket = new ServerSocket(9999); // 2. 等待客户端连接 Socket socket = serverSocket.accept(); // 阻塞式监听,会一直等待客户端连接 // 3. 读取客户端的消息 InputStream is = socket.getInputStream(); // 管道流 ByteArrayOutputStream baos = new ByteArrayOutputStream() ) { byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { baos.write(buffer, 0, len); } System.out.println(baos.toString()); } catch (IOException e) { e.printStackTrace(); } } }
- 建立服务的端口
文件上传
-
根据文件流判断文件类型
public class TypeDict { /** 常用文件的文件头如下:(以前六位为准) JPEG (jpg),文件头:FFD8FF PNG (png),文件头:89504E47 GIF (gif),文件头:47494638 TIFF (tif),文件头:49492A00 Windows Bitmap (bmp),文件头:424D CAD (dwg),文件头:41433130 Adobe Photoshop (psd),文件头:38425053 Rich Text Format (rtf),文件头:7B5C727466 XML (xml),文件头:3C3F786D6C HTML (html),文件头:68746D6C3E Email [thorough only] (eml),文件头:44656C69766572792D646174653A Outlook Express (dbx),文件头:CFAD12FEC5FD746F Outlook (pst),文件头:2142444E MS Word/Excel (xls.or.doc),文件头:D0CF11E0 MS Access (mdb),文件头:5374616E64617264204A WordPerfect (wpd),文件头:FF575043 Postscript (eps.or.ps),文件头:252150532D41646F6265 Adobe Acrobat (pdf),文件头:255044462D312E Quicken (qdf),文件头:AC9EBD8F Windows Password (pwl),文件头:E3828596 ZIP Archive (zip),文件头:504B0304 RAR Archive (rar),文件头:52617221 Wave (wav),文件头:57415645 AVI (avi),文件头:41564920 Real Audio (ram),文件头:2E7261FD Real Media (rm),文件头:2E524D46 MPEG (mpg),文件头:000001BA MPEG (mpg),文件头:000001B3 Quicktime (mov),文件头:6D6F6F76 Windows Media (asf),文件头:3026B2758E66CF11 MIDI (mid),文件头:4D546864 */ public static String checkType(String xxxx) { switch (xxxx) { case "ffD8ff": return "jpg"; case "89504e": return "png"; case "474946": return "gif"; default: return "0000"; } } } // 读取文件流 public class FileType{ public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } public static void main(String[] args) throws Exception { FileInputStream is = new FileInputStream("123.jpg"); byte[] b = new byte[3]; // 新建3字节缓存区 is.read(b, 0, b.length); // 读取3字节数据 String xxx = bytesToHexString(b); // bytes转16进制 // xxx = xxx.toUpperCase(); // 字母大写 System.out.println("头文件是:" + xxx); String ooo = TypeDict.checkType(xxx); System.out.println("后缀名是:" + ooo); } }
-
客户端
public class TcpClientDemo2 { public static void main(String[] args) { try ( // 1. 创建一个Socket连接 Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9000); // 2. 创建一个输出流 OutputStream os = socket.getOutputStream(); // 3. 读取文件 FileInputStream fis = new FileInputStream(new File("123.jpg")) ) { // 4. 写出文件 byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } // 5. 通知服务器传输已结束 socket.shutdownOutput(); } catch (IOException e) { e.printStackTrace(); } } }
-
服务端
public class TcpServerDemo2 { public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } for (byte b : src) { int v = b & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); } public static String checkType(String str) { switch (str) { case "ffd8ff": return ".jpg"; case "89504e": return ".png"; case "474946": return ".gif"; default: return ".txt"; } } public static void main(String[] args) { try ( // 1. 创建服务 ServerSocket serverSocket = new ServerSocket(9000); // 2. 监听客户端的连接 Socket socket = serverSocket.accept(); // 阻塞式监听,会一直等待客户端连接 // 3. 获取输入流 InputStream is = socket.getInputStream(); ) { byte[] buffer = new byte[1024]; byte[] b = new byte[3]; int len; if (is.read(b, 0, 3) == 0) { return; } // bytesToHexString将bytes转化为16进制,checkType根据16进制数判断格式 String prefix = checkType(bytesToHexString(b)); try ( // 4. 文件输出 FileOutputStream fos = new FileOutputStream(new File("receive" + prefix)) ) { fos.write(b, 0, 3); while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } } } catch (IOException e) { e.printStackTrace(); } } }
Tomcat
- 服务端
- 自定义 S
- Tomcat服务器 S
- 客户端
- 自定义 C
- 浏览器 B
UDP
-
不用连接,但需要知道对方的地址
-
发送方
// 不需要连接服务器,只管发 public class UdpDemo1 { public static void main(String[] args) { try ( // 1. 建立一个Socket DatagramSocket socket = new DatagramSocket(); ) { // 2. 发给谁 InetAddress localhost = InetAddress.getByName("localhost"); int port = 9090; // 3. 建个包 String msg = "你好,世界"; // 参数:数据,数据起始位置,数据长度,接收方地址,接收方端口 DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port); // 4. 发送包 socket.send(packet); } catch (Exception e) { e.printStackTrace(); } } }
-
接收方
public class UdpServerDemo1 { public static void main(String[] args) { try ( // 1. 开放端口 DatagramSocket socket = new DatagramSocket(9090); ) { // 2. 接收数据 byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); socket.receive(packet); // 阻塞接收 System.out.println(new String(packet.getData(), 0, packet.getLength())); } catch (Exception e) { e.printStackTrace(); } } }
循环发送
-
发送方
public class UdpSender { public static void main(String[] args) { try (DatagramSocket socket = new DatagramSocket(8888)) { InetAddress localhost = InetAddress.getByName("localhost"); int port = 8000; String msg; Scanner scanner = new Scanner(System.in); do { msg = scanner.nextLine(); DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, localhost, port); socket.send(packet); } while (!"bye".equals(msg)); } catch (Exception e) { e.printStackTrace(); } } }
-
接收方
public class UdpReceive { public static void main(String[] args) { try (DatagramSocket socket = new DatagramSocket(8000)) { byte[] buffer = new byte[1024]; while (true) { DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); socket.receive(packet); String receiveData = new String(packet.getData(), 0, packet.getLength()); System.out.println(receiveData); if ("bye".equals(receiveData)) { break; } } } catch (Exception e) { e.printStackTrace(); } } }
聊天实现
-
两个人都可以是发送方,也都可以是接收方
-
使用多线程实现
-
消息发送类
public class UdpTalkSender implements Runnable { private DatagramSocket socket = null; private Scanner scanner = null; private InetSocketAddress socketAddress = null; // 参数:发送消息端口,接收地址,接收端口 public UdpTalkSender(int port, String toIP, int toPort) { try { socket = new DatagramSocket(port); scanner = new Scanner(System.in); socketAddress = new InetSocketAddress(toIP, toPort); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { String msg; do { msg = scanner.nextLine(); DatagramPacket packet = new DatagramPacket(msg.getBytes(), 0, msg.getBytes().length, socketAddress); try { socket.send(packet); } catch (IOException e) { e.printStackTrace(); } } while (!"bye".equals(msg)); socket.close(); } }
-
消息接收类
public class UdpTalkReceive implements Runnable { private DatagramSocket socket = null; private String msgFrom; // 监听端口,消息发送方名称 public UdpTalkReceive(int port, String msgFrom) { this.msgFrom = msgFrom; try { socket = new DatagramSocket(port); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { byte[] buffer = new byte[1024]; while (true) { DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length); try { socket.receive(packet); } catch (IOException e) { e.printStackTrace(); } String receiveData = new String(packet.getData(), 0, packet.getLength()); System.out.println(msgFrom + "说:" + receiveData); if ("bye".equals(receiveData)) { break; } } } }
-
用户1:学生
public class TalkUser1 { public static void main(String[] args) { // 开启两个线程 new Thread(new UdpTalkSender(6000, "localhost", 8888)).start(); // 6000为发送端口,发送信息给localhost的8888端口 new Thread(new UdpTalkReceive(8000, "老师")).start(); // 8000为接收老师消息的端口 } }
-
用户2:老师
public class TalkUser2 { public static void main(String[] args) { // 开启两个线程 new Thread(new UdpTalkSender(6666, "localhost", 8000)).start(); // 6666为发送端口,发送信息给localhost的8000端口 new Thread(new UdpTalkReceive(8888, "学生")).start(); // 8888为接收学生消息的端口 } }
URL
-
统一资源定位符:定位资源用的,定位互联网上的某一个资源
协议://ip地址或者域名:端口号/虚拟目录/资源
-
URL类
public static void main(String[] args) { try { URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=cbc&password=123"); System.out.println(url.getProtocol()); // 协议 System.out.println(url.getHost()); // 主机ip System.out.println(url.getPort()); // 端口 System.out.println(url.getPath()); // 全路径 System.out.println(url.getQuery()); // 参数 System.out.println(url.getFile()); // 带参数全路径 } catch (MalformedURLException e) { e.printStackTrace(); } }
-
使用URL下载资源
public class URLDownload { public static void main(String[] args) { // 1. 下载地址 URL url = null; // 2. 连接到这个资源 HTTP HttpURLConnection urlConnection = null; InputStream is = null; FileOutputStream fos = null; try { url = new URL("http://localhost:8080/cbc/security.txt"); urlConnection = (HttpURLConnection) url.openConnection(); // 建立连接 is = urlConnection.getInputStream(); // 获取输入流 fos = new FileOutputStream("cbc.txt"); // 文件输出流 byte[] buffer = new byte[1024]; // 缓冲区 int len; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); // 写出数据 } } catch (Exception e) { e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } if (urlConnection != null) { urlConnection.disconnect(); } } } }