Tcp传输应用
应用一、实现TCP传输的客户端和服务端的简单互访
需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
客户端:
1,建立socket服务。指定要连接主机和端口。
2,获取socket流中的输出流。将数据写到该流中。通过网络发送给服务端。
3,获取socket流中的输入流,将服务端反馈的数据获取到,并打印。
4,关闭客户端资源。
关键在于通过getOutputStream()和getInputStream()获取读写流
- package cn.xushuai.Test;
- import java.io.*;
- import java.net.*;
- class TcpClient2 {
- public static void main(String[] args)throws Exception {
- Socket s = new Socket("127.0.0.1",10007);
- OutputStream out = s.getOutputStream();
- out.write("服务端,你好".getBytes());
- InputStream in = s.getInputStream();
- byte[] buf = new byte[1024];
- int len = in.read(buf);
- System.out.println(new String(buf,0,len));
- s.close();
- }
- }
服务端:
1,建立服务端的socket服务。ServerSocket();
并监听一个端口。
2,获取连接过来的客户端对象。
通过ServerSokcet的 accept方法。没有连接就会等,所以这个方法阻塞式的。
3,客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据。
并打印在控制台。
4,关闭服务端。(可选)
- class TcpServer2{
- public static void main(String[] args) throws Exception{
- ServerSocket ss = new ServerSocket(10007);
- Socket s = ss.accept();
- String ip = s.getInetAddress().getHostAddress();
- System.out.println(ip+"....connected");
- InputStream in = s.getInputStream();
- byte[] buf = new byte[1024];
- int len = in.read(buf);
- System.out.println(new String(buf,0,len));
- OutputStream out = s.getOutputStream();
- //Thread.sleep(10000);
- out.write("哥们收到,你也好".getBytes());
- s.close();
- ss.close();
- }
- }
应用二、编写一个文本转换服务器
分析:
既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。
而且客户度可以不断的进行文本转换。当客户端输入over时,转换结束。
该例子出现的问题:
现象:客户端和服务端都在莫名的等待。
原因:客户端和服务端都有阻塞式方法readLine(),这些方法没有读到结束标记,那么就一直等,而导致两端,都在等待。
为了书写简化,可以使用打印流,自动刷新与换行。
客户端:
源:键盘录入。 目的:网络设备,网络输出流。
而且操作的是文本数据,可以选择字符流。
步骤
1,建立服务。
2,获取键盘录入。
3,将数据发给服务端。
4,获取服务端返回的大写数据。
5,结束,关资源。
- import java.io.*;
- import java.net.*;
- class TransClient{
- public static void main(String[] args) throws Exception{
- Socket s = new Socket("127.0.0.1",10010);
- //定义读取键盘数据的流对象。
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(System.in));
- //定义目的,将数据写入到socket输出流。发给服务端。
- //BufferedWriter bufOut =
- //new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- //定义一个socket读取流,读取服务端返回的大写信息。
- BufferedReader bufIn =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- String line = null;
- while((line=bufr.readLine())!=null){
- if("over".equals(line))
- break;
- out.println(line);
- //bufOut.write(line); //将键盘录入写给服务端
- //bufOut.newLine(); //写入换行符,让服务端readLine识别,否则服务端一直阻塞
- //bufOut.flush();
- String str =bufIn.readLine(); //读取服务端反馈的信息
- System.out.println("server:"+str);
- }
- bufr.close();
- s.close(); //会在socket流中加入结束标记,服务端会识别,所以在客户端结束//后,服务端也就结束了
- }
- }</span>
服务端:
源:socket读取流。目的:socket输出流。
都是文本,装饰。
- class TransServer{
- public static void main(String[] args) throws Exception{
- ServerSocket ss = new ServerSocket(10010);
- Socket s = ss.accept();
- String ip = s.getInetAddress().getHostAddress();
- System.out.println(ip+"....connected");
- //读取socket读取流中的数据。
- BufferedReader bufIn =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- //目的:socket输出流,将大写数据写入到socket输出流,并发送给客户端。
- //BufferedWriter bufOut =
- //new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- String line = null;
- while((line=bufIn.readLine())!=null)//只有读到换行符才进行下次读取,否则一直阻塞{
- System.out.println(line);
- out.println(line.toUpperCase());
- //bufOut.write(line.toUpperCase());
- //bufOut.newLine(); //写入换行符,让客户端的readLine识别
- //bufOut.flush(); //必须刷新缓冲区
- }
- s.close();
- ss.close();
- }
- }
网络编程需注意的问题:
1、 读写流是否能读取到结束标记
读写流中的阻塞式方法read和write要刷新缓冲区和加入结束标记,readLine要进行换行操作,以便加入结束标记。
可以使用printReaader 和printWriter 来替代,有自动刷新和换行,这样简化了书写。
2、服务端是否能够接收到客户端结束标记
客户端上传完文件之后,需要向服务端提供一个结束标记。否则虽然客户端的循环结束,还会向下执行,又开始读取服务端反馈的信息,
但是,服务端还没有反馈时,客户端就向下执行了,以至于两端都阻塞。
3、自定义标签可能出现在文本中,所以导致文本文件还读取完就结束,所以使用通用的解决方案:shutDownOutput( )
服务端的服务线程:使用多线程实现并发访问,只需将共享的代码放在run方法中即可。
应用三、TCP上传文件
客户端:上传文本,并等待服务端的反馈信息
- import java.io.*;
- import java.net.*;
- //客户端上传文本,并等待服务端的反馈信息
- class TextClient{
- public static void main(String[] args) throws Exception{
- Socket s = new Socket("192.168.1.254",10006);
- //生成一个缓冲读取流,同时关联一个文件
- BufferedReader bufr =
- new BufferedReader(new FileReader("IPDemo.java"));
- //使用打印流,实现自动刷新和换行
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- //循环向服务端发送数据
- String line = null;
- while((line=bufr.readLine())!=null){
- out.println(line);
- }
- //自定义结束标记
- //缺陷:文件中可能出现自定义标记符
- //out.println("over");
- s.shutdownOutput();//关闭客户端的输出流,相当于,给流中加入一个结束标记-1.
- //服务端readLine()到-1时就结束读取,接着执行下面的代码
- //得到一个缓冲读取流,用于获取服务端的反馈信息
- BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
- String str = bufIn.readLine();
- System.out.println(str);
- bufr.close();
- s.close();
- }
- }
服务端:收到上传的文件,保存到本地server.txt中,并反馈给客户端上传成功的信息
- class TextServer{
- public static void main(String[] args) throws Exception{
- ServerSocket ss = new ServerSocket(10006);
- Socket s = ss.accept();
- String ip = s.getInetAddress().getHostAddress();
- System.out.println(ip+"....connected");
- //得到一个缓冲读取流对象,用于读取客户端传过来的数据
- BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
- //得到一个打印流,用于写入读取的文件
- PrintWriter out = new PrintWriter(new FileWriter("server.txt"),true);
- String line = null;
- //读取数据,直到读到结束标记
- while((line=bufIn.readLine())!=null){
- //if("over".equals(line))
- //break;
- out.println(line);
- }
- //获取一个写入流,向客户端反馈上传结果信息
- PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
- pw.println("上传成功");
- out.close();
- s.close();
- ss.close();
- }
- }
应用四、客户端并发上传图片
客户端
1,服务端点。
2,读取客户端已有的图片数据。
3,通过socket 输出流将数据发给服务端。
4,读取服务端反馈信息。
5,关闭。
- import java.io.*;
- import java.net.*;
- class PicClient{
- public static void main(String[] args)throws Exception {
- //只能传入一个文件名
- if(args.length!=1){
- System.out.println("请选择一个jpg格式的图片");
- return ;
- }
- //验证文件是否存在并是一个文件
- File file = new File(args[0]);
- if(!(file.exists() && file.isFile())){
- System.out.println("该文件有问题,要么补存在,要么不是文件");
- return ;
- }
- //限制图片为jpg格式
- if(!file.getName().endsWith(".jpg")){
- System.out.println("图片格式错误,请重新选择");
- return ;
- }
- //限定文件的大小
- if(file.length()>1024*1024*5){
- System.out.println("文件过大,没安好心");
- return ;
- }
- Socket s = new Socket("192.168.1.254",10007);
- FileInputStream fis = new FileInputStream(file);
- OutputStream out = s.getOutputStream();
- byte[] buf = new byte[1024];
- int len = 0;
- while((len=fis.read(buf))!=-1){
- out.write(buf,0,len);
- }
- //告诉服务端数据已写完
- s.shutdownOutput();
- InputStream in = s.getInputStream();
- byte[] bufIn = new byte[1024];
- int num = in.read(bufIn);
- System.out.println(new String(bufIn,0,num));
- fis.close();
- s.close();
- }
- }
服务端提供文件上传的线程代码:
- class PicThread implements Runnable{
- private Socket s;
- PicThread(Socket s){
- this.s = s;
- }
- public void run(){
- int count = 1;//定义为局部变量,让每个客户都拥有一个变量,而不是共享(成员变量)
- String ip = s.getInetAddress().getHostAddress();
- try{
- System.out.println(ip+"....connected");
- InputStream in = s.getInputStream();
- File dir = new File("d:\\pic");
- File file = new File(dir,ip+"("+(count)+")"+".jpg");
- //循环判断文件是否已经存在
- while(file.exists())
- file = new File(dir,ip+"("+(count++)+")"+".jpg");
- FileOutputStream fos = new FileOutputStream(file);
- byte[] buf = new byte[1024];
- int len = 0;
- while((len=in.read(buf))!=-1){
- fos.write(buf,0,len);
- }
- OutputStream out = s.getOutputStream();
- out.write("上传成功".getBytes());
- fos.close();
- s.close();
- }
- catch (Exception e){
- throw new RuntimeException(ip+"上传失败");
- }
- }
- }
服务端:为每个要上传文件的客户new一个线程提供服务
- class PicServer{
- public static void main(String[] args) throws Exception{
- ServerSocket ss = new ServerSocket(10007);
- while(true){
- Socket s = ss.accept();
- new Thread(new PicThread(s)).start();
- }
- //ss.close();
- }
- }
应用五、登陆服务
需求:客户端向服务端发送用户请求登陆,服务端通过验证,返回”欢迎光临“,未通过”用户不存在“
客户端通过键盘录入用户名。
服务端对这个用户名进行校验。
如果该用户存在,在服务端显示xxx,已登陆,并在客户端显示 xxx,欢迎光临。
如果该用户存在,在服务端显示xxx,尝试登陆,并在客户端显示 xxx,该用户不存在。
最多就登录三次。
客户端登陆代码:
- import java.io.*;
- import java.net.*;
- class LoginClient{
- public static void main(String[] args) throws Exception{
- Socket s = new Socket("192.168.1.254",10008);
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(System.in));
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- BufferedReader bufIn =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- for(int x=0; x<3; x++){
- String line = bufr.readLine();
- if(line==null)
- break;
- out.println(line);
- String info = bufIn.readLine();
- System.out.println("info:"+info);
- if(info.contains("欢迎"))
- break;
- }
- bufr.close();
- s.close();
- }
- }
服务端提供登陆服务的线程
- class UserThreadimplements Runnable{
- private Socket s;
- UserThread(Socket s){
- this.s = s;
- }
- public void run(){
- String ip =s.getInetAddress().getHostAddress();
- System.out.println(ip+"....connected");
- try{
- for(int x=0; x<3;x++){
- BufferedReaderbufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
- String name= bufIn.readLine();
- //避免按Ctrl+C 结束时,readLine读取为null,不满足,而进行3次循环的判断,所以这里对null进行判断
- if(name==null)
- break;
- BufferedReaderbufr = new BufferedReader(new FileReader("user.txt"));
- PrintWriterout = new PrintWriter(s.getOutputStream(),true);
- String line= null;
- booleanflag = false; //定义标记,判断是否获取到用户
- while((line=bufr.readLine())!=null) {//循环读取判断,用户是否存在
- if(line.equals(name)){
- flag= true;
- break;
- }
- }
- if(flag){
- System.out.println(name+",已登录");
- out.println(name+",欢迎光临");
- break;
- }
- else{
- System.out.println(name+",尝试登录");
- out.println(name+",用户名不存在");
- }
- }
- s.close();
- }
- catch (Exception e){
- throw newRuntimeException(ip+"校验失败");
- }
- }
- }
服务端:不断循环以产生新的线程为客户端提供服务
- class LoginServer{
- public static void main(String[] args) throws Exception{
- ServerSocket ss = new ServerSocket(10008);
- //通过循环,不断提供服务
- while(true){
- Socket s = ss.accept();
- new Thread(new UserThread(s)).start();
- }
- }
- }