tcp 拆包 java tcp socket java_tcp 拆包 java

目录

一、Socket类和ServerSocket类

1. Socket类

 2. ServerSocket类

二、 使用TCP的 Socket编程实现登录功能

1. 实现单用户登录

2. 实现多客户端用户登录

3. InetAddress类

一、Socket类和ServerSocket类

  TCP具有很好的安全性能

         速度较慢

(1)java.net包的两个类Socket和ServerSocket,分别用来实现双向安全连接的客户端和服务器端,它们是基于TCP协议进行工作的,工作过程如同打电话的过程,只有双方都接通了,才能开始通话。

(2)进行网络通信时,Socket需要借助数据流来完成数据的传递工作。

(3)一个应用程序要通过网络向另一个应用程序发送数据,只要简单地创建Socket,然后将数据写入到与该Socket关联的输出流即可。对应的,接收方的应用程序创建Socket,从相关联的输入流读取数据即可。

(4)注意:2个端点在基于TCP协议的Socket编程中,经常一个作为客户端,一个作为服务器端,也就是遵循client-server模型。

1. Socket类

Socket对象在客户端和服务器之间建立连接。可用Socket类的构造方法创建套接字,并将此套接字连接至指定的主机和端口。

(1)构造方法

-->第一种构造方法以主机名和端口号作为参数来创建一个Socket对象。创建对象时可能抛出UnknownHostException或IOException异常,必须捕获它们。

Socket s = new Socket(hostName,port);

-->第二种构造方法以InetAddress对象和端口号作为参数来创建一个Socket对象。构造方法可能抛出IOException或UnknownHostException异常,必须捕获并处理它们。

Socket s = new Socket(address,port);

(2)常用方法

tcp 拆包 java tcp socket java_客户端_02

 2. ServerSocket类

ServerSocket对象等待客户端建立连接,连接建立以后进行通信。

(1)构造方法

-->第一种构造方法接受端口号作为参数创建ServerSocket对象,创建此对象时可能抛出IOException异常,必须捕获和处理它。

ServerSocket ss = new ServerSocket(port);

-->第二种构造方法接受端口号和最大队列长度作为参数,队列长度表示系统在拒绝连接前可以拥有的最大客户端连接数。

ServerSocket ss = new ServerSocket(port,maxqu);

(2)常用方法

-->Socket类中列出的方法也适用于ServerSocket类。

-->ServerSocket类具有accept()方法,此方法用于等待客户端发起通信,这样Socket对象就可用于进一步的数据传输。

二、 使用TCP的 Socket编程实现登录功能

1. 实现单用户登录

-->Socket网络编程一般分成如下4个步骤进行:

(1)建立连接。

(2)打开Socket关联的输入/输出流。

(3)从数据流中写入信息和读取信息。

(4)关闭所有的数据流和Socket。

-->使用两个类模拟实现用户登录的功能,实现客户端向服务器端发送用户登录信息,服务器端显示这些信息。

    客户端实现步骤:

    1)建立连接,连接指向服务器及端口。

    2)打开Socket关联的输入/输出流。

    3)向输出流中写入信息。

    4)从输入流中读取响应信息。

    5)关闭所有的数据流和Socket。

    服务器端实现步骤:

    1)建立连接,监听端口。

    2)使用accept()方法等待客户端发起通信

    3)打开Socket关联的输入/输出流。

    4)从输入流中读取请求信息。

    5)向输出流中写入信息。

    6)关闭所有的数据流和Socket。

-->客户端和服务器端的交互,采用一问一答的模式,先启动服务器进入监听状态,等待客户端的连接请求,连接成功以后,客户端先“发言”,服务器给予“回应”。

1.1 客户端

package demo02;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 示例02:升级演示示例01,实现传递对象信息。
 */
public class LoginClient {

	/*
	 * 示例02:升级演示示例01,实现传递对象信息。
	 */
	
	public static void main(String[] args) {
		
		Socket socket = null;
		OutputStream os = null;
		ObjectOutputStream oos = null;
		InputStream is = null;
		BufferedReader br = null;
		
		try {
			
			// 建立客户端Socket连接,指定服务器的位置为本机以及端口为8800
			socket = new Socket("localhost", 8800);
			
			// 打开输出流
			os = socket.getOutputStream();
			
			// 对象序列化
			oos = new ObjectOutputStream(os);
			
			// 发送客户端信息,即向输出流中写入信息
			User user = new User("Tom", "123456");
			oos.writeObject(user);
			socket.shutdownOutput();

			// 接收服务器端的响应,即从输入流中读取信息
			is = socket.getInputStream();
			br = new BufferedReader(new InputStreamReader(is));
			
			String reply;
			while ((reply = br.readLine()) != null) {
				System.out.println("我是客户端,服务器的响应为:" + reply);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				
				//关闭遵循 先开后关 后开先关的原则
				br.close();
				is.close();
				oos.close();
				os.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

}

 1.2   服务器端

package demo02;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 示例02:升级演示示例01,实现传递对象信息。
 * 
 */
public class LoginServer {

	public static void main(String[] args) {
		
		ServerSocket serverSocket = null;
		Socket socket = null;
		InputStream is = null;
		ObjectInputStream ois = null;
		OutputStream os = null;
		
		try {
			
			// 建立一个服务器Socket(ServerSocket),指定端口8800并开始监听
			serverSocket = new ServerSocket(8800);
			
			// 使用accept()方法等待客户端发起通信
			socket = serverSocket.accept();
			
			// 打开输入流
			is = socket.getInputStream();
			
			// 反序列化
			ois = new ObjectInputStream(is);
			
			// 获取客户端信息,即从输入流读取信息
			User user = (User) ois.readObject();
			if (user != null) {
				System.out.println("我是服务器,客户登录信息为:" + user.getLoginName() + ","
						+ user.getPwd());
			}

			// 给客户端一个响应,即向输出流中写入信息
			String reply = "欢迎你,登录成功";
			os = socket.getOutputStream();
			os.write(reply.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			// 关闭资源
			try {
				os.close();
				ois.close();
				is.close();
				socket.close();
				serverSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}

	}

}

1.3 定义类   实现实例化  接口

package demo02;

import java.io.Serializable;

/**
 * 示例02:升级演示示例01,实现传递对象信息
 * 
 */
public class User implements Serializable {

	private static final long serialVersionUID = 1L;
	/** 用户名 */
	private String loginName;
	/** 用户密码 */
	private String pwd;

	public User() {
		super();
	}

	public User(String loginName, String pwd) {
		super();
		this.loginName = loginName;
		this.pwd = pwd;
	}

	public String getLoginName() {
		return loginName;
	}

	public void setLoginName(String loginName) {
		this.loginName = loginName;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

}

 

tcp 拆包 java tcp socket java_java_03

2. 实现多客户端用户登录

-->一问一答的模式在现实中显然不是人们想要的。一个服务器不可能只针对一个客户端服务,一般是面向很多的客户端同时提供服务的,但是单线程实现必然是这样的结果。

-->解决这个问题的办法是采用多线程的方式,可以在服务器端创建一个专门负责监听的应用主服务程序、一个专门负责响应的线程程序。这样可以利用多线程处理多个请求。

->客户端实现步骤:

1)建立连接,连接指向服务器及端口。

2)打开Socket关联的输入/输出流。

3)向输出流中写入信息。

4)从输入流中读取响应信息。

5)关闭所有的数据流和Socket。

-->服务器端实现步骤:

1)创建服务器线程类,run()方法中实现对一个请求的响应处理。

2)修改服务器端代码,让服务器端Socket一直处于监听状态。

3)服务器端每监听到一个请求,创建一个线程对象并启动。

2.1 ->多个客户端*3    表示三个IP   昵称不同的三个用户   

package demo03;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 示例03:升级示例02,实现多客户端的响应处理
 * 
 
 * 
 */
public class LoginClient01 {
	/*
	 * 客户端通过输出流向服务器端发送请求信息
	 * 服务器侦听客户端的请求得到一个Socket对象,将这个Socket对象传递给线程类
	 * 线程类通过输入流获取客户端的请求并通过输出流向客户端发送响应信息
	 * 客户端通过输入流读取服务器发送的响应信息
	 * 
	 */

	/*
	 * 示例03:升级演示示例02,实现多客户端的响应处理
	 */
	public static void main(String[] args) {
		
		Socket socket = null;
		OutputStream os = null;
		ObjectOutputStream oos = null;
		InputStream is = null;
		BufferedReader br = null;
		try {
			// 建立客户端Socket连接,指定服务器的位置为本机以及端口为8800
			socket = new Socket("localhost", 8800);
			// 打开输出流
			os = socket.getOutputStream();
			// 对象序列化
			oos = new ObjectOutputStream(os);
			// 发送客户端信息,即向输出流中写入信息
			User user = new User("Tom", "123456");
			oos.writeObject(user);
			socket.shutdownOutput();

			// 接收服务器端的响应,即从输入流中读取信息
			is = socket.getInputStream();
			br = new BufferedReader(new InputStreamReader(is));
			String reply;
			while ((reply = br.readLine()) != null) {
				System.out.println("我是客户端,服务器的响应为:" + reply);
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				br.close();
				is.close();
				oos.close();
				os.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
	}

}

2.2 ->服务器端

package demo03;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 示例03:升级示例02,实现多客户端的响应处理
 * 
 */
public class LoginServer {
	
	public static void main(String[] args) {
		
		ServerSocket serverSocket = null;
		try {
			// 建立一个服务器Socket(ServerSocket)指定端口并开始监听
			serverSocket = new ServerSocket(8800);

			// 监听一直进行中
			while (true) {
				// 使用accept()方法等待客户发起通信
				Socket socket = serverSocket.accept();
				LoginThread loginThread = new LoginThread(socket);
				loginThread.start();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

2.3 --->创建服务器端线程类

package demo03;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * 示例03:升级示例02,实现多客户端的响应处理。
 * 
 *
 */
public class LoginThread extends Thread {
	/*
	 * 示例03:升级示例02,实现多客户端的响应处理。
	 * 
	 * 分析如下:
	 * 	(1)创建服务器端线程类,run()方法中实现对一个请求的响应处理。
	 * 	(2)修改服务器端代码,让服务器端Socket一直处于监听状态。
	 * 	(3)服务器端每监听到一个请求,创建一个线程对象并启动
	 */
	
	Socket socket = null;
	//每启动一个线程,连接对应的Socket

	public LoginThread(Socket socket) {
		this.socket = socket;
	}
	
	//启动线程,即响应客户请求
	public void run() {
		InputStream is = null;
		ObjectInputStream ois = null;
		OutputStream os = null;
		try {
			//打开输入流
			is = socket.getInputStream();
			//反序列化
			ois = new ObjectInputStream(is);
			//获取客户端信息,即从输入流读取信息
			User user = (User)ois.readObject();
			if(user!=null){
				System.out.println("我是服务器,客户登录信息为:"+user.getLoginName()+","+user.getPwd());
			}
			
			//给客户端一个响应,即向输出流中写入信息
			os = socket.getOutputStream();
			String reply = "欢迎你,登录成功";
			os.write(reply.getBytes());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally{
			try {
				os.close();
				ois.close();
				is.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	
	}
	
}

 2.4 -->创建用户类

package demo03;

import java.io.Serializable;

/**
 * 示例03:升级示例02,实现多客户端的响应处理。

 * 
 */
public class User implements Serializable {

	private static final long serialVersionUID = 1L;
	/** 用户名 */
	private String loginName;
	/** 用户密码 */
	private String pwd;

	public User() {
		super();
	}

	public User(String loginName, String pwd) {
		super();
		this.loginName = loginName;
		this.pwd = pwd;
	}

	public String getLoginName() {
		return loginName;
	}

	public void setLoginName(String loginName) {
		this.loginName = loginName;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

}

3. InetAddress类

-->java.net包中的InetAddress类用于封装IP地址和DNS。要创建InetAddress类的实例,可以使用工厂方法,因为此类没有构造方法。

-->InetAddress类中的工厂方法

tcp 拆包 java tcp socket java_java_04