Java TCP编程之Scoket
作者:OlaiolaiO
一、什么是Scoket
Socket 是一个抽象概念,一个应用程序通过一个 Socket 来建立一个远程连接,而 Socket 内部通 过 TCP/IP 协议把数据传输到网络。
┌───────────┐ ┌───────────┐
│Application│ │Application│
├───────────┤ ├───────────┤
│ Socket │ │ Socket │
├───────────┤ ├───────────┤
│ TCP │ │ TCP │
├───────────┤ ┌──────┐ ┌──────┐ ├───────────┤
│ IP │<────>│Router│<─────>│Router│<────>│ IP │
└───────────┘ └──────┘ └──────┘ └───────────┘
Socket 、 TCP 和部分 IP 的功能都是由操作系统提供的,不同的编程语言只是提供了对操作系统调 用的简单的封装。例如, Java 提供的几个 Socket 相关的类就封装了操作系统提供的接口: Server Socket 类、 Socket 类。
为什么需要 Socket 进行网络通信?因为仅仅通过 IP 地址进行通信是不够的,同 一台计算机同一时间会运行多个网络应用程序,例如浏览器、QQ、邮件客户端等。当 操作系统接收到一个数据包的时候,如果只有 IP 地址,它没法判断应该发给哪个应用 程序,所以,操作系统抽象出 Socket 接口,每个应用程序需要各自对应到不同的 S ocket ,数据包才能根据 Socket 正确地发到对应的应用程序。
一个 Socket 就是由IP地址和端口号(范围是0~65535)组成,可以把 Socket 简单理解为 IP 地址加端口号。端口号总是由操作系统分配,它是一个 0 ~ 65535 之间的数字,其中,小于 1024 的端口属于特权端口,需要管理员权限,大于 1024 的端口可以由任意用户的应用程序打开。
使用 Socket 进行网络编程时,本质上就是两个进程之间的网络通信。其中一个进 程必须充当服务器端,它会主动监听某个指定的端口,另一个进程必须充当客户端,它 必须主动连接服务器的 IP 地址和指定端口,如果连接成功,服务器端和客户端就成功 地建立了一个 TCP 连接,双方后续就可以随时发送和接收数据。
当 Socket 连接成功地在服务器端和客户端之间建立后:
- 对服务器端来说,它的 Socket 是指定的 IP 地址和指定的端口号;
- 对客户端来说,它的 Socket 是它所在计算机的 IP 地址和一个由操作系统分配的随机端口号。
二、服务器端
package com.ljl.tcp.demo2; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; public class ChatServer { public static void main(String[] args) { Map<String, String> chatMap = new HashMap<String, String>(){ { put("你好", "你好呀"); put("hi", "hi~"); put("hello", "哈喽"); put("吃了吗", "没呢,你呢"); put("孤勇者", "爱你孤身走暗巷"); put("有请潘周聃", "潘周聃,今年29岁,苏黎世理工大学....."); put("很高兴认识你", "我也是哦"); } }; try (ServerSocket server = new ServerSocket(9966)) { while(true) { //客户端连接 Socket client = server.accept(); //获取客户端IP地址 String clientIP = client.getInetAddress().getHostAddress(); try( BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));){ //获取客户端提问 String question = reader.readLine(); System.out.println("服务器来自客户端"+clientIP+"的提问:"+question); //获取问题答案 String answer = chatMap.get(question); answer = answer == null?"我不知道你在说啥":answer; //发送答案至客户端 writer.write(answer); } } } catch (IOException e) { e.printStackTrace(); } } }
三、客户端
package com.ljl.tcp.demo2; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; import java.util.Scanner; public class ChatClient { public static void main(String[] args) { Scanner input = new Scanner(System.in); while(true) { try (//创建Socket Socket client = new Socket("192.168.254.163",9966); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream())); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); ){ //获取控制台的输入问题 String question = input.nextLine(); if(question.equals("退下")) { break; } //发送问题至服务器 writer.write(question); writer.flush(); //暂时结束本次输入 client.shutdownOutput(); //获取来自服务器的答案 String answer = reader.readLine(); System.out.println("服务器回答:"+answer); } catch (IOException e) { e.printStackTrace(); } } System.out.println("over"); } }
四、Socket流
当 Socket 连接创建成功后,无论是服务器端,还是客户端,我们都使用 Socket 实例进行网络通信。因为 T CP 是一种基于流的协议,因此, Java 标准库使用 InputStream 和 OutputStream 来封装 Socket 的数据流,这 样我们使用 Socket 的流,和普通 IO 流类似:
// 用于读取网络数据: InputStream in = sock.getInputStream(); // 用于写入网络数据: OutputStream out = sock.getOutputStream();
写入网络数据时,必须要调用 flush() 方法。如果不调用 flush() ,我们很可能会发现,客户端和服务器都 收不到数据,这并不是 Java 标准库的设计问题,而是我们以流的形式写入数据的时候,并不是一写入就立刻发送 到网络,而是先写入内存缓冲区,直到缓冲区满了以后,才会一次性真正发送到网络,这样设计的目的是为了提高 传输效率。如果缓冲区的数据很少,而我们又想强制把这些数据发送到网络,就必须调用 flush() 强制把缓冲区数 据发送出去。
五、总结
使用Java进行TCP编程时,需要使用 Socket 模型:
- 服务器端用 ServerSocket 监听指定端口;
- 客户端使用 Socket(InetAddress, port) 连接服务器;
- 服务器端用 accept() 接收连接并返回 Socket 实例;
- 双方通过 Socket 打开 InputStream / OutputStream 读写数据;
- 服务器端通常使用多线程同时处理多个客户端连接,利用线程池可大幅提升效率;
- flush() 方法用于强制输出缓冲区到网络。
到此这篇关于Java TCP编程之Scoket的文章就介绍到这了,更多相关Java Scoket内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!