TCP 通信同 UDP 通信一样, 都能实现两台计算机之间的通信, 通信的两端都需要创建 socket 对象.
区别在于, UDP 中只有发送端和接收端, 不区分客户端与服务器端, 计算机之间可以任意地发送数据.
而 TCP 通信是严格区分客户端与服务器端的, 在通信时, 必须先由客户端去连接服务器端才能实现通信, 服务器端不可以主动连接客户端, 并且服务器端程序需要事先启动, 等待客户端的连接.
在 JDK 中提供了两个类用于实现 TCP 程序, 一个是 ServerSocket 类, 用于表示服务器端, 一个是 Socket 类, 用于表示客户端.
通信时, 首先创建代表服务器端的 ServerSocket 对象, 该对象相当于开启一个服务, 并等待客户端的连接, 然后创建代表客户端的 Socket 对象向服务器端发出连接请求, 服务器端响应请求, 两者建立连接开始通信.
1.1 ServerSocket
通过前面的学习知道, 在开发 TCP 程序时, 首先需要创建服务器端程序. JDK 的 java.net 包中提供了一个 ServerSocket 类, 该类的实例对象可以实现一个服务器段的程序. 通过查阅 API 文档可知, ServerSocket 类提供了多种构造方法, 接下来就对 ServerSocket 的构造方法进行逐一地讲解.
使用该构造方法在创建 ServerSocket 对象时, 就可以将其绑定到一个指定的端口号上 (参数 port 就是端口号).
接下来学习一下 ServerSocket 的常用方法, 如表所示.
ServerSocket 对象负责监听某台计算机的某个端口号, 在创建 ServerSocket 对象后, 需要继续调用该对象的 accept() 方法, 接收来自客户端的请求. 当执行了 accept() 方法之后, 服务器端程序会发生阻塞, 直到客户端发出连接请求, accept() 方法才会返回一个 Scoket 对象用于和客户端实现通信, 程序才能继续向下执行.
1.2 Socket
讲解了 ServerSocket 对象可以实现服务端程序, 但只实现服务器端程序还不能完成通信, 此时还需要一个客户端程序与之交互, 为此 JDK 提供了一个 Socket 类, 用于实现 TCP 客户端程序.
通过查阅 API 文档可知 Socket 类同样提供了多种构造方法, 接下来就对 Socket 的常用构造方法进行详细讲解.
使用该构造方法在创建 Socket 对象时, 会根据参数去连接在指定地址和端口上运行的服务器程序, 其中参数 host 接收的是一个字符串类型的 IP 地址.
该方法在使用上与第二个构造方法类似, 参数 address 用于接收一个 InetAddress 类型的对象, 该对象用于封装一个 IP 地址.
在以上 Socket 的构造方法中, 最常用的是第一个构造方法.
接下来学习一下 Socket 的常用方法, 如表所示.
方法声明
功能描述
int getPort()
该方法返回一个 int 类型对象, 该对象是 Socket 对象与服务器端连接的端口号
InetAddress getLocalAddress()
该方法用于获取 Socket 对象绑定的本地 IP 地址, 并将 IP 地址封装成 InetAddress 类型的对象返回
void close()
该方法用于关闭 Socket 连接, 结束本次通信. 在关闭 socket 之前, 应将与 socket 相关的所有的输入 / 输出流全部关闭, 这是因为一个良好的程序应该在执行完毕时释放所有的资源
InputStream getInputStream()
该方法返回一个 InputStream 类型的输入流对象, 如果该对象是由服务器端的 Socket 返回, 就用于读取客户端发送的数据, 反之, 用于读取服务器端发送的数据
OutputStream getOutputStream()
该方法返回一个 OutputStream 类型的输出流对象, 如果该对象是由服务器端的 Socket 返回, 就用于向客户端发送数据, 反之, 用于向服务器端发送数据
在 Socket 类的常用方法中, getInputStream() 和 getOutStream() 方法分别用于获取输入流和输出流. 当客户端和服务端建立连接后, 数据是以 IO 流的形式进行交互的, 从而实现通信.
接下来通过一张图来描述服务器端和客户端的数据传输, 如下图所示.
1.3 TCP 协议实现 1.3.1 案例代码三:
- package com.itheima_04;
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.InetAddress;
- import java.net.Socket;
- /*
- * 使用 TCP 协议发送数据
- 创建发送端 Socket 对象 (创建连接)
- 获取输出流对象
- 发送数据
- 释放资源
- Socket(InetAddress address, int port)
- Exception in thread "main" java.net.ConnectException: Connection refused: connect
- */
- public class ClientDemo {
- public static void main(String[] args) throws IOException {
- // 创建发送端 Socket 对象 (创建连接)
- Socket s = new Socket(InetAddress.getByName("itheima"),10086);
- // 获取输出流对象
- OutputStream os = s.getOutputStream();
- // 发送数据
- String str = "hello tcp,im comming!!!";
- os.write(str.getBytes());
- // 释放资源
- //os.close();
- s.close();
- }
- }
- package com.itheima_04;
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.InetAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- /*
- * 使用 TCP 协议接收数据
- 创建接收端 Socket 对象
- 监听 (阻塞)
- 获取输入流对象
- 获取数据
- 输出数据
- 释放资源
- ServerSocket: 接收端, 服务端 Socket
- ServerSocket(int port)
- Socket accept()
- */
- public class ServerDemo {
- public static void main(String[] args) throws IOException {
- // 创建接收端 Socket 对象
- ServerSocket ss = new ServerSocket(10086);
- // 监听 (阻塞)
- Socket s = ss.accept();
- // 获取输入流对象
- InputStream is = s.getInputStream();
- // 获取数据
- byte[] bys = new byte[1024];
- int len;// 用于存储读到的字节个数
- len = is.read(bys);
- // 输出数据
- InetAddress address = s.getInetAddress();
- System.out.println("client --->" + address.getHostName());
- System.out.println(new String(bys,0,len));
- // 释放资源
- s.close();
- //ss.close();
- }
- }
1.4 TCP 相关案例 1.4.1 案例代码四:
使用 TCP 协议发送数据, 服务端将接收到的数据转换成大写返回给客户端
package com.itheima_05;[/font][/align] [font = 微软雅黑]
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.InetAddress;
- import java.net.Socket;
- /*
- 需求: 使用 TCP 协议发送数据, 并将接收到的数据转换成大写返回
- 客户端发出数据
- 服务端接收数据
- 服务端转换数据
- 服务端发出数据
- 客户端接收数据
- */
- public class ClientDemo {
- public static void main(String[] args) throws IOException {
- // 创建客户端 Socket 对象
- Socket s = new Socket(InetAddress.getByName("itheima"),10010);
- // 获取输出流对象
- OutputStream os = s.getOutputStream();
- // 发出数据
- os.write("tcp,im comming again!!!".getBytes());
- // 获取输入流对象
- InputStream is = s.getInputStream();
- byte[] bys = new byte[1024];
- int len;// 用于存储读取到的字节个数
- // 接收数据
- len = is.read(bys);
- // 输出数据
- System.out.println(new String(bys,0,len));
- // 释放资源
- s.close();
- }
- }
- package com.itheima_05;
- 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 {
- // 创建服务端 Socket 对象
- ServerSocket ss = new ServerSocket(10010);
- // 监听
- Socket s = ss.accept();
- // 获取输入流对象
- 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);
- // 转换数据
- String upperStr = str.toUpperCase();
- // 获取输出流对象
- OutputStream os = s.getOutputStream();
- // 返回数据 (发出数据)
- os.write(upperStr.getBytes());
- // 释放资源
- s.close();
- //ss.close();// 服务端一般不关闭
- }
- }
1.4.2 案例代码五:
客户端:
1. 提示用户输入用户名和密码, 将用户输入的用户名和密码发送给服务端
2. 接收服务端验证完用户名和密码的结果
服务端:
1. 接收客户端发送过来的用户名和密码
2. 如果用户名不是 itheima 或者 密码不是 123456, 就向客户端写入 "登录失败"
否则向客户端写入登录成功
- package com.itheima_06;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.net.Socket;
- /*
- * 模拟用户登录
- */
- public class ClientTest {
- public static void main(String[] args) throws IOException {
- // 创建客户端 Socket 对象
- //Socket s = new Socket(InetAddress.getByName("itheima"),8888);
- Socket s = new Socket("itheima",8888);
- // 获取用户名和密码
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- System.out.println("请输入用户名:");
- String username = br.readLine();
- System.out.println("请输入密码:");
- String password = br.readLine();
- // 获取输出流对象
- //BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- // 写出数据
- out.println(username);
- out.println(password);
- // 获取输入流对象
- BufferedReader serverBr = new BufferedReader(new InputStreamReader(s.getInputStream()));
- // 获取服务器返回的数据
- String result = serverBr.readLine();
- System.out.println(result);
- // 释放资源
- s.close();
- }
- }
1.4.3 案例代码六:
将用户名和密码封装到一个 User 类中, 提供对应的构造方法和 getter/setter 方法
新建一个 UserDB 类里面定义一个集合, 在集合中添加以下 User 对象
- new User("zhangsan","123456");
- new User("lisi","654321");
- new User("itheima","itheima");
- new User("admin","password");
客户端:
1. 提示用户输入用户名和密码, 将用户输入的用户名和密码发送给服务端
2. 接收服务端验证完用户名和密码的结果
服务端:
服务端将客户端发送过来的用户名密码封装成 User 对象
集合中如果包括这个 User 对象, 想客户端写入 "登录成功"
否则向客户端写入 "登录失败"
package com.itheima_06;[/font][/align] [font = 微软雅黑]
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.net.ServerSocket;
- import java.net.Socket;
- public class ServerTest {
- public static void main(String[] args) throws IOException {
- // 创建服务器端 Socket 对象
- ServerSocket ss = new ServerSocket(8888);
- // 监听
- Socket s = ss.accept();
- // 获取输入流对象
- BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
- // 获取用户名和密码
- String username = br.readLine();
- String password = br.readLine();
- // 判断用户名和密码是否正确
- boolean flag = false;
- if("itheima".equals(username) && "123456".equals(password)) {
- flag = true;
- }
- // 获取输出流对象
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- // 返回判断信息
- if(flag) {
- out.println("登陆成功");
- }
- else {
- out.println("登陆失败");
- }
- // 释放资源
- s.close();
- //ss.close();// 服务器一般不关闭
- }
- }
- package com.itheima_07;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.net.Socket;
- /*
- * 模拟用户登录改写 (面向对象版本)
- */
- public class ClientTest {
- public static void main(String[] args) throws IOException {
- // 创建客户端 Socket 对象
- Socket s = new Socket("itheima",8888);
- // 获取用户名和密码
- BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
- System.out.println("请输入用户名:");
- String username = br.readLine();
- System.out.println("请输入密码:");
- String password = br.readLine();
- // 获取输出流对象
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- // 写出数据
- out.println(username);
- out.println(password);
- // 获取输入流对象
- BufferedReader serverBr = new BufferedReader(new InputStreamReader(s.getInputStream()));
- // 获取服务器返回的数据
- String result = serverBr.readLine();
- System.out.println(result);
- // 释放资源
- s.close();
- }
- }
- package com.itheima_07;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.PrintWriter;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.List;
- public class ServerTest {
- public static void main(String[] args) throws IOException {
- // 创建服务器端 Socket 对象
- ServerSocket ss = new ServerSocket(8888);
- // 监听
- Socket s = ss.accept();
- // 获取输入流对象
- BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
- // 获取用户名和密码
- String username = br.readLine();
- String password = br.readLine();
- // 判断用户名和密码是否正确
- boolean flag = false;
- /*if("itheima".equals(username) && "123456".equals(password)) {
- flag = true;
- }*/
- List<User> users = UserDB.getUsers();
- User user = new User(username,password);
- if(users.contains(user)) {
- // 匹配成功
- flag = true;
- }
- // 获取输出流对象
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- // 返回判断信息
- if(flag) {
- out.println("登陆成功");
- }
- else {
- out.println("登陆失败");
- }
- // 释放资源
- s.close();
- //ss.close();// 服务器一般不关闭
- }
- }
来源: http://www.bubuko.com/infodetail-2573045.html