欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程资源 > 编程问答 >内容正文

编程问答

小小聊天室,慢慢的回忆啊!(TCP 通信实现)

发布时间:2025/3/20 编程问答 49 豆豆
生活随笔 收集整理的这篇文章主要介绍了 小小聊天室,慢慢的回忆啊!(TCP 通信实现) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

文章目录

  • 前言
  • 一、TCP 协议
  • 二、 TCP 通信 的实现
    • “ 请求- 响应 ” 模式:
    • 通过 Socket 的编程顺序:
    • 基于TCP协议的Socket编程,实现单向通讯
    • 通过数据流改进代码
    • 序列化涉及的类和接口(io流知识回顾一下)
    • 基于TCP协议的Socket编程,实现双向通讯(通过多线程模拟多个用户请求登录)
  • つづく


前言

之前学网络编程这块,就是感觉云里雾里,就想对老师说一句:你说啥呢?这两天学框架,遇到个bug怎么也是解决不了了;这两天还赶上元旦,老师们都放假;进行不下去就回来复习复习吧!看看最头疼的网络编程;重新在看一遍,还真有种知新而温故的感觉!废话不多说,我们看代码;


一、TCP 协议

TCP(Transfer Control Protocol)是面向连接的,所谓面向连接,就是当计算机双方通信时必需经过先建立连接,然后传送数据,最后拆除连接三个过程。

二、 TCP 通信 的实现

在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。

“ 请求- 响应 ” 模式:

Socket 类:发送 TCP 消息。
ServerSocket 类:创建服务器。

通过 Socket 的编程顺序:

  • 创建服务器 ServerSocket,在创建时,定义 ServerSocket 的监听端口(在这个端口接收客户端发来的消息)。
  • ServerSocket 调用 accept()方法,(accept():侦听要连接到此套接字并接受它。)使之处于阻塞状态。
  • 创建客户端 Socket,并设置服务器的 IP 及端口。
  • 客户端发出连接请求,建立连接。
  • 分别取得服务器和客户端 Socket 的 InputStream 和 OutputStream。
  • 利用 Socket 和 ServerSocket 进行数据传输。
  • 关闭流及 Socket。
  • 基于TCP协议的Socket编程,实现单向通讯

    代码如下:

    服务端

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket;/*** 服务器类* */ public class ServerTest {public static void main(String[] args) throws IOException { // 创建serverSocket对象,并指定端口;ServerSocket ss = new ServerSocket(9999); // 利用accept()方法,进行监听 返回一个Socket对象Socket client = ss.accept(); // 接收数据(获取输入流)由于接收到是字节(也就是数字)需要转换成字符read:从输入流读取数据的下一个字节。 InputStream is = client.getInputStream();System.out.println((char)is.read()); // 发出响应(获取输出流)write():将 b.length字节从指定的字节数组写入此输出流。OutputStream os = client.getOutputStream();os.write("收到".getBytes()); // 关闭流if(os!=null) {os.close();}if(is!=null) {is.close();}if(client!=null) {client.close();}}}

    客户端

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket;/*** 客户类* */ public class ClientTest {public static void main(String[] args) throws IOException { // 创建Socket对象Socket socket = new Socket("192.168.10.102",9999); // 输出数据OutputStream os = socket.getOutputStream(); // write()中的参数只能是,字符类型的os.write('a'); // 接收响应InputStream is = socket.getInputStream();byte[] b = new byte[1024];//中转站,存储读到的数据int len = 0;//读到的个数,当个数为-1时,证明没有数据;if((len=is.read(b))!=-1) { // 利用String的构造方法,将读到的数据输出到控制台System.out.println(new String(b,0,len));} // 关闭流if(is!=null) {is.close();}if(os!=null) {os.close();}if(socket!=null) {socket.close();}}}

    输出效果

    通过数据流改进代码

    代码如下:

    服务端

    import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket;/*** 服务器类* */ public class ServerTest2 {public static void main(String[] args) throws IOException { // 创建serverSocket对象,并指定端口;ServerSocket ss = new ServerSocket(9999); // 利用accept()方法,进行监听 返回一个Socket对象Socket client = ss.accept(); // 接收数据 获取数据输入流 数据流的参数需要一个字节流DataInputStream dis = new DataInputStream(client.getInputStream());System.out.println(dis.readUTF()); // 发出响应(获取数据输出流)DataOutputStream dos = new DataOutputStream(client.getOutputStream());dos.writeUTF("收到"); // 关闭流if(dos!=null) {dos.close();}if(dis!=null) {dis.close();}if(client!=null) {client.close();}if(ss!=null) {ss.close();}}}

    客户端

    import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket;/*** 客户类* */ public class ClientTest2 {public static void main(String[] args) throws IOException { // 创建Socket对象Socket socket = new Socket("192.168.10.102",9999); // 输出数据 使用数据流进行数据的传输 参数需要一个字节流DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); // writeUTF()是写入的方法;dos.writeUTF("hello world"); // 接收响应DataInputStream dis = new DataInputStream(socket.getInputStream()); // readUTF()是读取数据的方法System.out.println(dis.readUTF()); // 关闭流if(dis!=null) {dis.close();}if(dos!=null) {dos.close();}if(socket!=null) {socket.close();}}}

    运行效果

    序列化涉及的类和接口(io流知识回顾一下)

  • ObjectOutputStream 代表对象输出流,它的 writeObject(Object obj)方法可对参数指定的obj 对象进行序列化,把得到的字节序列写到一个目标输出流中。
  • ObjectInputStream 代表对象输入流,它的 readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
  • 只有实现了 Serializable 接口的类的对象才能被序列化。Serializable 接口是一个空接口,只起到标记作用。
  • 不要忘了给实现类添加序列化编号。(为了防止读和写的序列化 ID 不一致,一般指定一个固定的序列化 ID。)
  • 基于TCP协议的Socket编程,实现双向通讯(通过多线程模拟多个用户请求登录)

    创建两个项目(服务器项目、客户端项目)


    服务器项目


    User类

    import java.io.Serializable;public class User implements Serializable{ // 必须要继承Serializable接口 , 否则不能被序列化//为了防止读写的序列化id不同,一定要指定一个固定的序列id 最好不要选择默认的idprivate static final long serialVersionUID = 8251855228567334134L;private String name;private String password;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public static long getSerialversionuid() {return serialVersionUID;}public User(String name, String password) {super();this.name = name;this.password = password;}public User() {super();}}

    服务器类

    import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import com.bjsxt.thread.ServerThread;/*** 服务器类* */ public class Server {public static void main(String[] args) throws IOException, ClassNotFoundException{ // 常见ServerSocket对象; 并指定端口号ServerSocket ss = new ServerSocket(10000); // 监听客户端 accept()方法进行监听 并返回一个Socket对象 // 需要使用循环来监听,不能只监听一次while(true) {Socket client = ss.accept(); // 创建线程对象ServerThread st = new ServerThread(client); // 开启线程new Thread(st).start();}} }

    添加线程类ServerThread

    import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket; import com.bjsxt.pojo.User;/*** 将获取每一个客户端都要执行的代码,封装到该类中,并实现Runnable接口,完成多线程* */public class ServerThread implements Runnable{private Socket client;public ServerThread(Socket client) {super();this.client = client;} // 线程中的异常必须处理掉,不能抛出;@Overridepublic void run() { // 想要看到谁在请求登录必须要在线程类的方法中添加 ,如果在服务其中添加会出现main请求登录的字样System.out.println(Thread.currentThread().getName()+"正在请求登录");// 获取对象输入流 需要一个字节流ObjectInputStream ois = null;// 获取数据输出流 响应是字符串 所以用数据输出流 参数需要一个字节流DataOutputStream dos = null;try {ois = new ObjectInputStream(client.getInputStream());// 获取信息 返回一个object对象,将其转换为user对象User user = (User)ois.readObject();// 通过getInetAddress()方法获取InetAddress对象,并获取客户端信息;System.out.println(client.getInetAddress().getHostAddress()+"请求登录"+"\t用户名:"+user.getName()+"\t密码:"+user.getPassword());dos = new DataOutputStream(client.getOutputStream());// 产生响应 判断用户名与密码是否正确String str = null;if("miwa".equals(user.getName())&&"miwa".equals(user.getPassword())) {str = "登录成功";}else {str = "用户名或密码错误";} dos.writeUTF(str);} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} // 关闭流 倒着关if(dos!=null) {try {dos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if(ois!=null) {try {ois.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if(client!=null) {try {client.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

    客户端项目


    User类

    import java.io.Serializable;public class User implements Serializable{ // 必须要继承Serializable接口 , 否则不能被序列化//为了防止读写的序列化id不同,一定要指定一个固定的序列id 最好不要选择默认的idprivate static final long serialVersionUID = 8251855228567334134L;private String name;private String password;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public static long getSerialversionuid() {return serialVersionUID;}public User(String name, String password) {super();this.name = name;this.password = password;}public User() {super();}}

    客户端类

    import java.io.DataInputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import java.util.Scanner;import com.bjsxt.pojo.User;/*** 客户端* */ public class Client {public static void main(String[] args) throws IOException { // 获取Socket对象 并给定服务器地址,以及端口号Socket socket = new Socket("192.168.10.102",10000); // 获取 对象输出流 为了实现登录 ,需要将登录信息封装成对象 参数需要一个字节流对象ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); // 创建用户对象User user = getUser(); // 发送信息 oos.writeObject(user); // 接收响应 响应是字符串类型,所以用数据流接收 参数需要一个字节流DataInputStream dis = new DataInputStream(socket.getInputStream()); // 将响应输出到控制台 readUTF()方法直接返回一个String类型,可以直接输出;System.out.println(dis.readUTF()); // 关闭流if(dis!=null) {dis.close();}if(oos!=null) {oos.close();}if(socket!=null) {socket.close();}}public static User getUser() { // 获取键盘输入对象 Scanner s = new Scanner(System.in);System.out.println("请输入用户名:");String username = s.next();System.out.println("请输入密码:");String password = s.next();return new User(username,password);}}

    运行效果


    つづく

    回首看看之前感觉很难的题目;其实也不过如此; 加油,每一个拼搏的你; 谢谢观看; 最后会以一个小小的聊天室项目结束;敬请期待……

    总结

    以上是生活随笔为你收集整理的小小聊天室,慢慢的回忆啊!(TCP 通信实现)的全部内容,希望文章能够帮你解决所遇到的问题。

    如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。