一、TCP
使用 Socket 对象 进行 通信 ,使用底层的TCP 协议。
面向连接的协议。
1:先建立连接。 通信的两端 都会有进行通信的Socket 对象。
2:使用Socket 对象 使用字节流进行消息的通信。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 使用 Socket 连接服务器,给服务器发送一条信息。服务器收到之后,反馈一个boolean 值。
*
*/
public class MyServer {
public static final int SERVER_PORT = 1777;
public static void main(String[] args) throws Exception{
//创建建立连接服务的 服务对象。
ServerSocket ss = new ServerSocket(SERVER_PORT);
System.out.println("等待连接的请求!");
//阻塞式方法,只有连接请求发过来之后,该方法才会返回,并生成一个与连接过来的对象进行通信的Socket。
Socket socket = ss.accept();
System.out.println("连接成功!---"+socket);
//获取客户端发送的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str = br.readLine();
System.out.println("服务器收到的信息为:"+str);
//给客户端回复一个boolean 值
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeBoolean(true);
dos.flush();
//需要写到finally 中 代码
socket.close();
ss.close();
}
}
class MyClient{
public static void main(String[] args) throws Exception{
//创建连接服务器的Socket 对象
Socket socket = new Socket("127.0.0.1", MyServer.SERVER_PORT);
//给服务器发送一条信息
String str = "我可以连接你么?";
OutputStream os = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.write(str);
bw.newLine();
bw.flush();
//接收服务的反馈
DataInputStream dis = new DataInputStream(socket.getInputStream());
System.out.println("服务器的反馈:"+dis.readBoolean());
dis.close();
bw.close();
socket.close();
}
}
二、tcp-模拟用户登录
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
/**
* 模拟用户登录
*
* 客户端:发送一个用户名+密码给服务器。接收服务器的一个反馈。
* 服务器:接收客户端的登录请求,解析客户端的用户名和密码知否有效(需要和数据库中的用户信息比对)。
* 如果用户存在,返回 true,否则返回 false。
*
*/
public class LoginServer {
public static final int SERVER_PORT = 1777;
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(SERVER_PORT);
Socket socket = ss.accept();
System.out.println(socket);
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] buf = new byte[100];
int count = bis.read(buf);
String str = new String(buf,0,count);
String name = str.split("&")[0].split("=")[1];
String pwd = str.split("&")[1].split("=")[1];
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeBoolean(hasUser(name,pwd));
dos.flush();
socket.close();
ss.close();
}
/**
* 判断 配置文件中,是否包含指定的用户
* @param name
* @param pwd
* @return
*/
public static boolean hasUser(String name, String pwd)throws Exception{
Properties users = new Properties();
users.load(new FileInputStream("./res/users.txt"));
boolean bool = users.containsKey(name);
if(bool){//人存在,比对密码
if(users.getProperty(name).equals(pwd)){
return true;
}
return false;
}
return false;
}
}
class LoginClient{
public static void main(String[] args) throws Exception{
Socket socket = new Socket("192.168.51.242", LoginServer.SERVER_PORT);
String str = "name=xiaogang&pwd=123456";
byte[] buf = str.getBytes();
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(buf);
bos.flush();
DataInputStream dis = new DataInputStream(socket.getInputStream());
System.out.println("服务器的反馈:"+dis.readBoolean());
//只关闭socket 即可。
socket.close();
}
}
三、tcp-转换大写
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 需求:客户端给服务器发送一个字符串,可以多次发送,服务器接收到之后,将字符串的大写形式返回。
* 客户端发送一个bye,结束转换
*
*/
public class UpperCaseServer {
//收到字符串,转换为大写返回
public static final int SERVER_PORT = 1777;
public static final String BYE_STR = "bye";
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(SERVER_PORT);
//监听连接请求
Socket socket = ss.accept();
System.out.println(socket);
//得到读取输入流的字符输入流,,得到往输出流写数据的字符输出流
// br 用于读取小写信息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//bw 用户回复大写
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
while(true){
//读取客户端发送的内容,转换大写,回复,切记要刷新
String string = br.readLine();
if(BYE_STR.equals(string)){
break;
}
bw.write(string.toUpperCase());
bw.newLine();
bw.flush();
}
socket.close();
ss.close();
}
}
class UpperCaseClient{
public static void main(String[] args) throws Exception{
Socket socket = new Socket("192.168.51.242", UpperCaseServer.SERVER_PORT);
//bw 用于发送给服务器信息的 字符输出流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//br 用于读取服务器反馈的
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//用于本地输入的
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
while(true){
System.out.println("请您输入要转换的内容:");
//接收键盘的输入
String string = input.readLine();
//将string 发送给服务器
bw.write(string);
bw.newLine();
bw.flush();
//如果输入了bye ,结束 循环
if(UpperCaseServer.BYE_STR.equals(string))
break;
//读取服务器的反馈
String response = br.readLine();
System.out.println(response);
}
socket.close();
}
}
四、tcp-上传文件
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务器上传文件:
*
* 客户端:
* 1:先把文件的名字传给服务器
* 2:读取本地文件的数据,发送给服务器。
*
* 服务器:
* 1:接收文件的名字
* 2:接收文件数据,写入到本地。
*
*/
public class UploadFileServer {
public static final int PORT = 1777;
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(PORT);
Socket socket = ss.accept();
System.out.println(socket);
DataInputStream dis = new DataInputStream(socket.getInputStream());
//获得文件的名字
String fileName = dis.readUTF();
//写到本地字节输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("c:/server/"+fileName));
//读取客户端发送的数据
BufferedInputStream bis =new BufferedInputStream(socket.getInputStream());
byte[] buf = new byte[100];
int count = bis.read(buf);
while(count != -1){
bos.write(buf, 0, count);
count = bis.read(buf);
}
bos.flush();
bos.close();
//给客户端的反馈
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(fileName + "\t上传成功!");
dos.flush();
socket.close();
ss.close();
}
}
class UploadFileClient{
public static void main(String[] args) throws Exception{
Socket socket = new Socket("192.168.51.242", UploadFileServer.PORT);
File file = new File("c:/dear.jpg");
//将文件的名字发过去
String name = file.getName();
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(name);
dos.flush();
//可以得到文件的字节大小,给服务器发过去。
//发送字节数据
//读取本地字节数据
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
//将读取到的字节数据发送出去
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] buf = new byte[100];
//读取本地字节数据
int count = bis.read(buf);
while(count != -1){
bos.write(buf, 0, count);
//读取本地字节数据
count = bis.read(buf);
}
bos.flush();
bis.close();
//给输出流添加一个流的末尾的标识,不会影响其他的流
socket.shutdownOutput();
//接收服务器的反馈 ,文件上传成功
DataInputStream dis = new DataInputStream(socket.getInputStream());
System.out.println("服务器反馈:"+dis.readUTF());
socket.close();
}
}
五、tcp-并发上传
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
/**
* 服务器上传文件:
*
* 服务器:
* 1:可以同时 响应多个客户端上传文件的请求
* 2:需要满足一个客户端 多次上传同一个文件的需求。
* 客户端的IP地址 + 当前时间的时间戳(System.currentTimeMillis()) + 文件名.txt
*/
public class UploadFileServer {
public static final int PORT = 1777;
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(PORT);
ArrayList<Socket> sockets = new ArrayList<>();
try {
while(true){
Socket socket = ss.accept();
//得到一个socket 连接之后,立马启动一个线程,该线程负责 当前得到的socket 对象和 与之对应的客户端的通信。
sockets.add(socket);
System.out.println(socket);
//来了一个请求,就需要启动一个线程,该线程负责和该请求进行通信。
new SocketThread(socket).start();
}
} finally {
ss.close();
}
}
/**
* 用来和客户端的一个请求的socket 进行通信的。
*
*/
static class SocketThread extends Thread{
private Socket socket;
public SocketThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
//获得文件的名字
String fileName = dis.readUTF();
//写到本地字节输出流
//TODO
String ip = socket.getInetAddress().getHostAddress();
String timeStr = Long.toString(System.currentTimeMillis());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("c:/server/"+ip+"_"+timeStr + "_"+ fileName));
//读取客户端发送的数据
BufferedInputStream bis =new BufferedInputStream(socket.getInputStream());
byte[] buf = new byte[10000];
int count = bis.read(buf);
while(count != -1){
bos.write(buf, 0, count);
count = bis.read(buf);
//Thread.sleep(10);
}
bos.flush();
bos.close();
//给客户端的反馈
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(fileName + "\t上传成功!");
dos.flush();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
六、断点续传
1:从服务器下载文件。
第一步:给服务器发送一个 下载文件的名字(String);
第二步:服务器从本地读取指定的文件,再通过输出流发送给客户端。
中断了。
2:需要发送2调数据:
1:想要下载的文件的名字。
2:已经下载的文件的大小。
服务器收到已经下载的字节数据的大小,从指定的位置开始读取文件下发给客户端。
---- skip(1024*1024)。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 从服务器下载文件。断点续传
*
*/
public class DownLoadFileServer {
public static final int SERVER_PORT = 1777;
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(SERVER_PORT);
Socket socket = ss.accept();
//读取文件的名字
DataInputStream dis = new DataInputStream(socket.getInputStream());
String fileName = dis.readUTF();
//读取文件的大小(已经下载的文件大小)
long size = dis.readLong();
//发送文件是否存在
File file = new File("c://server/"+fileName);
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
boolean exist = file.exists();
dos.writeBoolean(exist);
if(!exist){//文件不存在,结束
socket.close();
ss.close();
return;
}
//读取本地数据,发送给客户端
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] buf = new byte[1000];
//从文件指定的位置读取
bis.skip(size);
int count = bis.read(buf);
while(count!= -1){
bos.write(buf,0,count);
bos.flush();
count = bis.read(buf);
}
bis.close();
socket.close();
ss.close();
}
}
/**
* 服务器下载文件,客户端
*
*/
class DownLoadFileClient{
public static void main(String[] args) throws Exception{
Socket socket = new Socket("192.168.51.242" , DownLoadFileServer.SERVER_PORT);
//发送请求文件名字
File file = new File("c://dear.jpg");
String fileName = file.getName();
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
dos.writeUTF(fileName);
dos.flush();
//如果文件存在,发送文件大小给服务器,不存在发送0 给服务器
long size = 0;
if(file.exists()){
size = file.length();
}
dos.writeLong(size);
dos.flush();
//接收服务器的反馈,如果服务器文件不存在,则直接终止程序
DataInputStream dis = new DataInputStream(socket.getInputStream());
boolean fileExist = dis.readBoolean();
if(!fileExist){
System.out.println("请求文件不存在,下载 失败!");
socket.close();
return;
}
//接收服务器发送的数据写到本地。切记要写入指定的文件,尾部追加
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file, true));
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
byte[] buf = new byte[1000];
int count = bis.read(buf);
while(count != -1){
bos.write(buf, 0, count);
bos.flush();
count = bis.read(buf);
Thread.sleep(10);
}
bos.close();
socket.close();
}
}
1:如果涉及到缓冲流,Buffered 相关的, 要及时对流进行刷新 调用flush 方法。
2:关闭流的时候,只需要关闭socket ,关闭socket 就会把socket 的inputStream 和OutputStream 同时关闭。
3:如果需要关闭socket 的inputStream 和OutputStream 先关输入输出流,再关socket。
4:如果涉及到readLine 方法。需要保证对方会发送一行数据已经行的结束标识。 newLine() 或者使用PrintWriter 要打开自动刷新功能。 的println()。
5:通过关闭socket的输入输出流 通过close 关闭 ,都会导致socket不可用。
6:通过shutDownInputStream 和 shutDownOutputStream 只会单方面的关闭某一个流,不会导致另外的一个流不可用。
八、泛型
1:概念:泛型:generic type---参数化类型。
2:泛型的有效期:编译期。
泛型是从java 源文件 编程成 class 的过程中,对泛型约束的类型进行检查。
在class 文件中。所有的泛型相关的信息都将被擦除掉。
运行期将不再进行泛型类型的检查。
3:泛型类
class MyStack<T>{
T t;
void test(T t){
}
}
4:泛型接口---泛型接口实现子类。
interface MyInterface<T>{
void test(T t);
}
//子类实现父接口,如果父接口是一个泛型接口,那么子类的也许要指定泛型,和父接口类型一致。
class MyInterfaceImpl<Integer> implements MyInterface<Integer>{
@Override
public void test(Integer t) {
}
}
5:泛型通配符(方法参数泛型不固定)
//? 泛型通配符,可以认为是所有泛型类型的父类型
//? 某种特定的类型。可以是任意类型。
static void test(ArrayList<?> list){
}
这个时候,调用该方法,list 中的元素的类型可以是任意类类型。
6:复杂的泛型方法:
声明
使用
泛型类中使用泛型方法
7:静态方法与泛型
泛型类中的静态的方法,不能直接使用类的泛型类型。因为类的泛型的类型是依赖于对象的。而静态方法是依赖于类加载。
如果想在静态方法中使用泛型类的类型,那么必须将该方法声明成泛型方法。
(静态泛型方法的泛型的类型可以和所在的类的泛型的类型相同,也可以不同)
8:泛型的上下边界
方法参数的边界控制 ?
//希望元素的类型是 Number 和 Number 的子类类型
static void test1(List<? extends Number> list){
}
//希望元素的类型是 Number 和 Number 的父类的类型
static void test2(List<? super Number> list){
}
//泛型类的边界的控制 T
class MyStack<T extends Number>
//泛型方法边界 <T extends Number>
//泛型方法的边界的控制
//只能是Number 的子类和 Number 的类型
static <T extends Number> void test1(T t){
}
例:
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("123");
//list.add(1);
MyStack<String> stack = new MyStack<>();
test(new ArrayList<String>());
//test1("123");
test1(new ArrayList<Object>());
}
//? 泛型通配符,可以认为是所有泛型类型的父类型
static void test(ArrayList<?> list){
}
//希望元素的类型是 Number 和 Number 的子类类型
static void test1(List<? extends Number> list){
}
//希望元素的类型是 Number 和 Number 的父类的类型
static void test2(List<? super Number> list){
}
//泛型方法
//static <T> void test1(T t){
////t 只能当Object 用。
//}
}
class Student implements Comparable<Student>{
private int age;
@Override
public int compareTo(Student o) {
//Student student = (Student)o;
return age-o.age;
}
}
//容器中元素的类型只能是 Number 和 Number 的子类类型。
class MyStack<T extends Number>{
T t;
<E> void test(E t){
}
//泛型方法的边界的控制
//只能是Number 的子类和 Number 的类型
static <T extends Number> void test1(T t){
}
}
interface MyInterface<T>{
void test(T t);
}
//子类实现父接口,如果父接口是一个泛型接口,那么子类的也许要指定泛型,和父接口类型一致。
class MyInterfaceImpl<Integer> implements MyInterface<Integer>{
@Override
public void test(Integer t) {
}
}