(黑马程序员)学习笔记,网络编程TCP

TCP协议要比UDP协议复杂一些,UDP在传输数据时不会检查是否连接,而TCP在传输前需要先建立连接(通过服务端和客户端三次握手)。

因此在在测试的时候也一定要先开启TCP服务端,然后在开启客户端,TCP简单示例如下

package net;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

/*
 * tcp传输
 * 
 * 1.tcp分客户端和服务端
 * 2.客户端对应的对象是socket
 * 	服务端对应的对象时ServerSocket
 */
public class TCPDemo {

	public static void main(String[] args) {
		new TCPClient();
		new TCPServer();
	}
}
/*
 * 客户端
 * 通过查阅socket对象,发现在该对象建立时,就可以去连接指定主机
 * 因为有服务端存在,并连接成功,形成通路后,在钙通道进行数据传输
 * 
 * 1. 创建socket服务并指定要连接的主机和端口
 * 2. 向服务端发送数据
 * 3. 关闭资源
 */
class TCPClient{
	public static void main(String[] args) {
		try {
			//创建客户端的socket服务,指定目标主机和端口
			Socket s = new Socket("10.2.20.82",9596);
			//为了发送数据,应该获取socket流中的输出流
			OutputStream out = s.getOutputStream();
			out.write("TCP 客户端发送数据".getBytes());
			
			//客户端接收信息
			InputStream in = s.getInputStream();
			byte[] buf = new byte[1024];
			int len = in.read(buf);
			String data = new String(buf,0,len);
			System.out.println(data);
			
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
}
/*
 * 服务端
 * 
 * 1. 使用ServerSocket,建立服务端socket服务,并监听一个端口
 * 2. 通过ServerSocket的accept()方法,获取连接的客户端对象,这个方法是阻塞式的
 * 3. 客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据
 * 4. 关闭资源(关闭客户端)
 */
class TCPServer{
	public static void main(String[] args) {
		try {
			//建立服务端socket,并监听端口
			ServerSocket ss = new ServerSocket(9596);
			//通过accept()方法获取链接过来的客户端对象
			Socket s = ss.accept();
			String ip = s.getInetAddress().getHostAddress();
			
			//获取客户端发送过来的数据,那么要使用客户端对象的读取流来读取数据
			InputStream in = s.getInputStream();
			byte[] buf = new byte[1024];
			int len = in.read(buf);
			String data = new String(buf,0,len);
			System.out.println(ip+"..."+data);
			
			//服务端返回消息
			OutputStream out = s.getOutputStream();
			out.write("收到".getBytes());
			
			s.close();
			ss.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		System.out.println();
	}
}


学习了TCP传输方式,就可以写一个小程序来上传或下载文件了

练习:从客户端上传一个非文本文件到服务端的硬盘上保存

package net;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/*
 * 练习:上传文件,文件非文本,最终保存在磁盘上
 * 
 * 1.与服务端建立连接
 * 2.通过字节流读取文件,然后写到socket的输出流中向服务端发送
 * 		注意:文件传输完后应该有个结束标记,否则因为客户端和服务端都在使用read()方法,造成都在等待而不能结束程序,
 * 		这里使用socket的shutdownOutput()方法,这个方法就是向socket输出流结尾写一个"-1"
 * 3.服务端接收socket输入流中的数据,然后使用字节流写到指定的磁盘位置,写完后向客户端向socket输出流写入返回语句,表示"上传成功",
 * 4.客户端接收服务端的消息并打印
 * 5.服务器端关闭资源,客户端关闭资源
 */
public class UploadDemo {

	public static void main(String[] args) {
		new Client();
		new Server();
	}
}
class Client{
	public static void main(String[] args) {
		try {
			Socket socket = new Socket("10.2.20.82",9696);
			//从磁盘读取文件
			File file = new File("E:\\BaiduYunDownload\\FILE\\1.mp3");
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
			int len = 0;
			byte[] buf = new byte[1024];
			
			//将从文件读取出的数据发送给服务端
			BufferedOutputStream sbos = new BufferedOutputStream(socket.getOutputStream());
			while((len=bis.read(buf))!=-1){
				sbos.write(buf,0,len);
				sbos.flush();
			}
			//用于关闭客户端的socket输出流
			socket.shutdownOutput();
			//接收服务端返回信息,并打印
			BufferedInputStream sbis = new BufferedInputStream(socket.getInputStream());
			while((len=sbis.read(buf))!=-1){
				System.out.println(new String(buf,0,len));
			}
			//关闭资源
			bis.close();
			socket.close();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}
class Server{
	public static void main(String[] args) {
		try {
			//服务端监听端口
			ServerSocket serverSocket = new ServerSocket(9696);
			Socket socket = serverSocket.accept();
			//设置文件输出位置
			File file = new File("E:\\BaiduYunDownload\\FILE\\2.mp3");
			long time = System.currentTimeMillis();
			//读取数据
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
			BufferedInputStream sbis = new BufferedInputStream(socket.getInputStream());
			int len = 0;
			byte[] buf = new byte[1024];
			//将读取的数据输出到指定文件中
			System.out.println("接收文件开始");
			while((len=sbis.read(buf))!=-1){
				bos.write(buf, 0, len);
				bos.flush();
			}
			System.out.println("接收文件结束");
			System.out.println("用时"+(System.currentTimeMillis()-time)+"ms");
			//向客户端反馈信息
			BufferedOutputStream sbos = new BufferedOutputStream(socket.getOutputStream());
			sbos.write("上传成功".getBytes());
			sbos.flush();
			//关闭资源
			bos.close();
			socket.close();
			serverSocket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

练习2:并发的上传图片,注意校验文件的有效性,图片格式为.jpg

package net;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/*
 * 练习:并发上传图片,格式为.jpg
 */
public class UploadPicDemo {

	public static void main(String[] args) {
		new PicClient();
		new PicServer();
	}
}
/*
 * 客户端
 * 1.连接服务端
 * 2.读取客户端已有的图片数据
 * 3.通过socket输出流将数据发送给服务端
 * 4.读取服务端反馈信息
 * 5.关闭资源
 */
class PicClient{
	public static void main(String[] args) {
		//判断输入的是否为路径
		if(args.length!=1){
			System.out.println("路径不正确");
			return;
		}
		//判断输入的文件是不是一个有效文件
		File file = new File(args[0]); 
		if(!(file.exists()&&!file.isHidden()&&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;
		}
		try {
			//客户端创建socket
			Socket s = new Socket("10.2.20.82",9697);
			OutputStream out = s.getOutputStream();
			//读取文件,然后加到socket的输出流中
			FileInputStream fis = new FileInputStream(file); 
			int len = 0;
			byte[] buf = new byte[1024];
			while((len=fis.read(buf))!=-1){
				out.write(buf, 0, len);
			}
			//关闭socket的输出流
			s.shutdownOutput();
			
			//从socket中读取服务端反馈信息
			InputStream in = s.getInputStream();
			len=in.read(buf);
			System.out.println(new String(buf,0,len));
			//关闭资源
			fis.close();
			s.close();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
/*
 * 服务端
 * 
 * 问题:这个服务端有一个局限性,当多个客户端想要上传文件时,只要有一个客户端在上传文件,那么其他客户端只能等待。
 * 解决方式:为了解决这样的等待问题,应采取并发的上传方式,也就是将每个客户端都封装到一个线程中,这样就可以处理多个客户端同时上传的请求
 * 
 * 1.明确客户端要执行的代码,将该代码封装到run方法中
 */
class PicServer{
	public static void main(String[] args) {
		
			try {
				//服务端监听端口
				ServerSocket ss = new ServerSocket(9697);
				while(true){
					//使用多线程并发接收客户端连接
					Socket s = ss.accept();
					new Thread(new PicThread(s)).start();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		
		
	}
}
//服务端线程中的内容
class PicThread implements Runnable{
	private Socket s;
	public PicThread(Socket s){
		this.s=s;
	}
	public void run() {
		try {
			//获取连接的客户端IP
			String ip = s.getInetAddress().getHostAddress();
			//用于重复上传时在文件名称添加的字段
			int count = 1;
			File file = new File("E:\\BaiduYunDownload\\FILE\\"+ip+".jpg");
			//判断文件是否重复
			while(file.exists()){
				file = new File("E:\\BaiduYunDownload\\FILE\\"+ip+"("+(count++)+").jpg");
			}
			
			FileOutputStream fos = new FileOutputStream(file);
			InputStream in = s.getInputStream();
			int len = 0;
			byte[] buf = new byte[1024];
			//将socket传过来的文件,输出到服务端指定位置
			while((len=in.read(buf))!=-1){
				fos.write(buf, 0, len);
			}
			
			//向客户端反馈信息
			OutputStream out = s.getOutputStream();
			out.write((ip+"上传成功").getBytes());
			
			fos.close();
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}

下面简单了解一下TCP三次握手都发了什么内容:

package net;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 演示客户端和服务端
 * 
 * 1.
 * 客户端:浏览器
 * 服务端:自定义
 * 
 */
public class IEServerDemo {

	public static void main(String[] args) {
		try {
			ServerSocket ss = new ServerSocket(9797);
			int count=1;
			//使用循环接收浏览器的消息
			while(true){
				Socket s = ss.accept();
				String ip = s.getInetAddress().getHostAddress();
				System.out.println(ip+"..."+count++);
				//接收浏览器(客户端)反馈的信息
				InputStream in= s.getInputStream();
				int len = 0;
				byte[] buf = new byte[1024];
				len=in.read(buf);
				System.out.println(new String(buf,0,len));
					
				//向浏览器发送数据
				PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
				pw.println("nihao");
				s.close();
			}
//			ss.close();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
}
/*
 * 
TCP三次握手
第一次  客户端向服务器发送
	GET / HTTP/1.1
	Accept: text/html, application/xhtml+xml, 
	DNT: 1
	Accept-Language: zh-CN
	User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko LBBROWSER
	Accept-Encoding: gzip, deflate
	Host: 10.2.20.82:9797
	Connection: Keep-Alive
	
第二次  服务器向客户端反馈
	HTTP/1.1 200 OK
	Server: Apache-Coyote/1.1
	Accept-Ranges: bytes
	ETag: W/"50-1402473194507"
	Last-Modified: Wed, 11 Jun 2014 07:53:14 GMT
	Content-Type: text/html
	Content-Length: 50
	Date: Wed, 11 Jun 2014 07:57:27 GMT
	
第三次  客户端向服务器反馈
	GET /favicon.ico HTTP/1.1
	Connection: Keep-Alive
	Accept: 
	Accept-Language: zh-CN
	User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; LBBROWSER)
	Host: 10.2.20.82:9797
 */
/**
 * 演示客户端和服务端
 * 
 * 2.
 * 客户端:自定义
 * 服务端:Tomcat
 */
class IEClient{
	public static void main(String[] args) {
		try {
			Socket s = new Socket("127.0.0.1",8080);
			//配置向服务器发送的信息
			PrintWriter out = new PrintWriter(s.getOutputStream(),true);
			//请求方式
			out.println("GET /test/test1.html HTTP/1.1");
			//接收类型
			out.println("Accept: */*");
			//识别语言
			out.println("Accept-Language: zh-CN");
			//主机地址
			out.println("Host: 10.2.20.82:9797");
			//保持联通
			out.println("Connection: Keep-Alive");
			//这里需要空一行,用于把请求头和内容分开
			out.println();
			
			//接收服务器反馈信息
			InputStream in = s.getInputStream();
			int len=0;
			byte[] buf = new byte[1024];
			len=in.read(buf);
			//把内容打印出来
			System.out.println(new String(buf,0,len));
			
			s.close();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 
	}
}

最后有个小补充,就是URL这个类

package net;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class URLDemo {

	public static void main(String[] args) {
		try {
			URL url = new URL("http://10.2.20.82:8080/test/1.html?username=AAA&password=BBB");
			//URL中封装好了的一些方法,用于获取信息
			System.out.println("protocol:"+url.getProtocol());//获取协议
			System.out.println("host:"+url.getHost());//获取主机地址
			System.out.println("port:"+url.getPort());//获取端口号
			System.out.println("path:"+url.getPath());//获取资源路径
			System.out.println("file:"+url.getFile());//获取资源路径+参数
			System.out.println("query:"+url.getQuery());//获取参数
			
			//通过URL连接
			URLConnection conn = new URL("http://www.baidu.com.cn").openConnection();
			//获取连接流,URLConnection封装了socket的获取流的方法,所以使用方法是一样的
			InputStream in = conn.getInputStream();
			byte[] buf = new byte[1024];
			int len = 0;
			//获取服务器的返回数据
			while((len=in.read(buf))!=-1){
				System.out.println(new String(buf,0,len));
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值