上一篇写到使用UDP实现文字交互,UDP和TCP各有千秋,所以现在写一个TCP上传图片的demo。
TCP通信同UDP通信区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。
在开发TCP程序时,首先需要创建服务器端程序,JDK的java.net包中提供了一个ServerSocket类。
使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上(参数port就是端口号)。
ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。
ServerSocket对象可以实现服务端程序,但只实现服务器端程序还不能完成通信,此时还需要一个客户端程序与之交互,为此JDK提供了一个Socket类,用于实现TCP客户端程序。
参数address用于接收一个InetAddress类型的对象,该对象用于封装一个IP地址。
Socket的常用方法,如图所示。
在Socket类的常用方法中,getInputStream()和getOutStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信。
下面上代码
客户端
// 创建Socket对象,连接服务器Socket socket = new Socket("127.0.0.1",8000);
这段代码只要写TCP客户端必定出现
/*
* 实现TCP客户端,连接到服务器
* 和服务器实现数据交换
* 实现TCP客户端程序的类java.net.Socket
*
* 构造方法:
* Socket(String host,int port) 传递服务器IP和端口号
* 注意:构造方法只要运行,就会和服务器进行连接,连接失败,抛出异常
* OutputStream getOutputStream() 返回套接字的输出流
* 作用:将数据输出,输出到服务器
* InputStream getInputStream() 返回套接字的输入流
* 作用:从服务器端读取数据
* 客户端服务器数据交换,必须使用套接字对象Socket中的获取IO流,自己new流,不行
*/
package com.lty.wangluotcpPhotoUp;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
* 实现TCP图片上传客户端
* 实现步骤
* 1.Socket套接字连接服务器
* 2.通过Socket获取字节输出流,写图片
* 3.使用自己的流对象,读取图片数据源
* FileInputStream
* 4.读取图片使用字节输出流,将图片写到服务器
* 采用字节数组进行缓冲
* 5.通过Socket套接字获取字节输入流
* 读取服务器发回来的上传成功
* 6.关闭资源
*/
public class TCPClient {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",8000);
//获取字节输出流,图片写到服务器
OutputStream out = socket.getOutputStream();
// 创建字节输入流,读取本机上的数据源图片
FileInputStream fis = new FileInputStream("c:\\t.jpg");
//开始读写字节数组
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));
fis.close();
socket.close();
}
}
服务器:
这个demo是多线程的,所以有如下代码
package com.lty.wangluotcpPhotoUp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.Socket;
import java.util.Random;
public class Upload implements Runnable{
private Socket socket;
public Upload(Socket socket) {this.socket=socket;}
public void run() {
//通过客户端连接对象,获取字节输入流,获取客户端图片
try {
InputStream in = socket.getInputStream();
//将目的文件夹封装到File对象
File upload = new File("d:\\upload");
if(!upload.exists())
upload.mkdirs();
//防止文件同名被覆盖,重新定义文件名字
//规则: 域名+毫秒值+6位随机数
String filename="itcast"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";
//创建字节输出流,将图片写入到目的文件夹中
FileOutputStream fos = new FileOutputStream(upload+File.separator+filename);
//读写字节数组
byte[] bytes=new byte[1024];
int len=0;
while ((len=in.read(bytes))!=-1) {
fos.write(bytes,0,len);
}
//通过客户端连接对象获取字节输出流
//上次成功写回到客户端
socket.getOutputStream().write("上传成功".getBytes());
fos.close();
socket.close();
} catch (Exception ex) {
}
}
}
写服务器端下面两端代码是肯定会出现
ServerSocket server = new ServerSocket(8000);
//调用服务器套接字对象中的方法accept()获取套接字对象
Socket socket =server.accept();
/*
* 实现TCP服务程序
* 表示服务器程序的类java.net.ServerSocket
* 构造方法:
* SreverSoceket(int port)传递端口号
*
* 很重要的事情:必须要获得客户端的套接字对象Socket
* Socket accept()
* 服务器可以获取到客户端的套接字对象Socket
*
*/
package com.lty.wangluotcpPhotoUp;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPThreadServer {
public static void main(String[] args)throws IOException {
ServerSocket server = new ServerSocket(8000);
while(true) {
//获取一个客户端,必须开启新线程
Socket socket=server.accept();
new Thread(new Upload(socket)).start();
}
}
}
效果图
网络编程与IO密不可分,但是IO内容太过繁杂,这里就不一一说了
这里涉及到了多线程请见下回分解