TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建socket对象。
区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。
而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。
在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。
通信时,首先创建代表服务器端的ServerSocket对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的Socket对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。
ServerSocket
ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。
Socket
在Socket类的常用方法中,getInputStream()和getOutStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信。
在Socket类的常用方法中,getInputStream()和getOutStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信。
服务器端和客户端的数据传输,如下图所示。
代码实例:
服务器端:
/* 实现TCP 服务器程序
* 表示服务器程序的类 java.net.ServerSocket
* 构造方法:
* ServerSocket(int port)传递端口号
* 很重要的事情:必须要获得客户端的套接字对象Socket
* Socket accept()
* 1,创建服务器ServerSocket对象(指定服务器端口号)
* 2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象
* 3,给客户端反馈信息
* 4,关闭流资源*/
public class TCPServer { public static void main(String[] args) throws IOException { //创建服务器对象,明确端口号 ServerSocket server=new ServerSocket(9000); //与客户端创建链接,获取与我相连接的客户端对象 Socket socket=server.accept(); //获取字节输入流,接收客户端发送的数据 InputStream in=socket.getInputStream(); byte[] bytes=new byte[1024]; int len=in.read(bytes); System.out.println("客户端发送的内容为:"+new String(bytes,0,len)); Scanner sc=new Scanner(System.in); //回复客户端 //获取字节输出流,目的地是客户端 OutputStream out=socket.getOutputStream(); //发送数据 out.write(sc.next().getBytes()); //释放资源 server.close(); } }
客户端:
/* 实现TCP 客户端,连接到服务器
* 和服务器实现数据交换
* 实现TCP客户端程序的类 java.net.Soket
* 构造方法
* Socket(String host,int port) 传递服务器IP和端口号
* 注意:构造方法只要运行,就会和服务器进行连接,连接失败,抛出异常
* OutputStream getOutputStream() 返回套接字的输出流
* 作用:将数据输出,输出到服务器
* InputStream getInputStream() 返回套接字的输入流
* 作用:从服务器端读取数据
* 客户端服务器数据交换,必须使用套接字对象Socket中的获取的IO流,自己new流,不行
* 1,创建客户端Socket对象,(指定要连接的服务器地址与端口号)
* 2,获取服务器端的反馈回来的信息
* 3,关闭流资源*/
//客户端 public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { Scanner sc=new Scanner(System.in); //创建客户端对像 Socket socket=new Socket("127.0.0.1",9000); //获取字节输出流,目的地是服务器 OutputStream out=socket.getOutputStream(); //发送数据 out.write(sc.next().getBytes()); //接收服务器端的回复 //获取字节输入流,接受服务器端的数据 InputStream in=socket.getInputStream(); byte[] bytes=new byte[1024]; int len=in.read(bytes); System.out.println("服务器端发送的内容为:"+new String(bytes,0,len)); //释放资源 socket.close(); } }
文件上传代码实例:
开始服务器端编写,准备上传照片
/* TCP上传服务器
- ServerSocket套接字对象,监听端口8888
- 方法accept()获取客户端的连接对象
- 客户端连接对象获取字节输入流,读取客户端发送图片
- 创建File对象,绑定上传文件夹
判断文件夹存在,不存在,再创建文件夹
- 创建字节输出流,数据目的File对象所在文件夹
- 字节读取图片,字节流将图片写入到目的文件夹中
- 将上传成功返回客户端
- 关闭资源*/
public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { //创建客户端对象,明确服务器端的ip和端口号 Socket socket=new Socket("127.0.0.1",8888); //明确数据源 FileInputStream fis=new FileInputStream("E:\io1127\a.jpg"); //明确目的地 OutputStream out=socket.getOutputStream(); int len=0; byte[] bytes=new byte[1024]; //开始复制 while((len=fis.read(bytes))!=-1){ out.write(bytes,0,len); } //告知服务器端结束,别读了 socket.shutdownOutput(); //接收服务器端的回复 //获取字节输入流 InputStream in=socket.getInputStream(); len=in.read(bytes); System.out.println("服务器端回复:"+new String(bytes,0,len)); //释放资源 socket.close(); } }
完成客户端编写,实现照片上传
/* 文件上传 客户端
实现步骤:
- Socket套接字连接服务器
- 通过Socket获取字节输出流,写图片
- 使用自己的流对象,读取图片数据源:FileInputStream
- 读取图片,使用字节输出流,将图片写到服务器
- 通过Socket套接字获取字节输入流:读取服务器发回来的上传成功
- .关闭资源
- public void shutdownOutput() 禁用此Socket的输出流,间接的相当于告知了服务器数据写入完毕*/
public class TCPServer { public static void main(String[] args) throws IOException { //创建服务器对象,明确端口号 ServerSocket server=new ServerSocket(8888); //和客户端创建连接,并获取连接的客户端对象 Socket socket=server.accept(); //获取明确数据源 InputStream in=socket.getInputStream(); //明确目的地 File file=new File("E:\io1127\server"); //如果文件夹不存在 if(!file.exists()){ //创建该文件夹 file.mkdirs(); } //明确文件名 String filename="oracle"+System.currentTimeMillis()+new Random().nextInt(9999)+".jpg"; //明确目的地 FileOutputStream fos=new FileOutputStream(file+File.separator+filename); //开始复制 byte[] bytes=new byte[1024]; int len=0; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len); } //回服客户端 //获取字节输出流 OutputStream out=socket.getOutputStream(); //发送内容 out.write("收到".getBytes()); //释放资源 server.close(); } }
文件上传流程图:
此程序为单线程程序,TCP通信方法能保证数据不丢失,所以文件传输大多使用TCP通信方法以提高文件传输的完整性