1.收获
说实话,我今天有点跟不上了,是因为我遇到一些问题后就会去想它,想把它弄明白,可是时间不允许,就在我想的时候,课已经上了很大一部分了,就感觉有点急,不像前面那样,有的东西是已经学过的,有的东西一想就会有思路,可能今天学的是从前没有接触过的东西,理解上就会有点慢,花费的时间就会多一点,还好的是慢慢可以理解这些知识。有时候在想自己为什么自己在理解上赶不上别人,也许自己有点差吧,这也许就是差距,但是这个差距并不是鸿沟,还以可以跨过去的。加油!
2.技术
(1)网络编程
(2)实现聊天
(3)实现向服务器上传图片和在服务器上下载图片
3.技术的实际应用和实践
(1)网络编程
什么是网络编程:
实现两台不同的计算机之间传递数据。
在生活中,我们用QQ聊天,浏览器上网等这些都是需要数据的传递的。
数据是怎样传递:
客服端:手机APP 浏览器 在桌面QQ,当客服端需要数据时服务器就向客服端传递,当然客户端也可向服务器端传递数据。
服务器端:存储/处理数据
数据传输:TCP/IP协议
TCP:面向连接的 安全的 打电话
UDP:面向无连接的 不安全 但快 飞鸽传书
网络中的数据传输:Socket实现->套接字
-----Socket类:客服端
------ServerSocket:服务器端
读取数据:服务器 客服端读取数据
------ BufferedReader->InputStringReader->socket.getInputStream
--- 读取终端
-------BufferedReader->InputStringReader->System.in
输出数据:客服端 服务器端
-----BufferedWriter->OutputStreamWriter->socket.getOutputStream()
-----PrintStream->socket.getOutputStream()
在生活中,在浏览器中我们经常看到一些网址
网址的组成:
http://www.baidu.com/sesrch?code=android&type=1
- http/https:传输协议 Tcp/ IP
- www.baidu.com:域名 表示一台网络中的电脑
- ipv4 ipv6IP地址:唯一标识一台网络中的计算机
------ 32位 四个部分每个部分是八个字节
------ 10.129.25.253<->www.baidu.com - DNS服务器:域名解析 将www.baidu.com类型的域名解析为对应的ip地址
- sesrch:对应的后台的程序文件 php java jsp
- ? 表示运行这个程序传递的参数
- code=android code 是服务器端规定的字段
- & 如果有多个参数使用&符号链接
IP地址:唯一标识某一台电脑
端口号:唯一标识电脑上的某一个进程
通过IP地址来访问这台电脑
通过端口号(某一个进程)来访问应用程序
(2)实现聊天
在这个实现的过程中并不是那么一帆风顺,首先我们实现的单向的发送消息,后来是实现单向的从终端输入传输,只能实现由客服端到服务器端或者由服务器端到客户端,再后来就是实现双向的发送消息。最后实现了群聊。
单向的发固定的内容
首先创建两个类来分别模拟客服端和服务器端
//客服端
lass Client{
public static void main(String[] args)throws IOException {}
}
//服务器端
class Server{
public static void main(String[] args)throws IOException {}
}
在客服端
1.创建用于通信的socket
// 指明和谁通信:IP地址 端口号
Socket socket=new Socket("10.129.25.253",7171);
2.接收服务器端的数据
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
3.读取服务器端发送的数据
String line=null;
while((line=br.readLine())!=null){
System.out.println(line);
}
4.向服务器端发送数据
PrintStream ps=new PrintStream(socket.getOutputStream());
ps.println("发送成功");
socket.shutdownOutput();
在服务器端
1.创建服务器端的serversocket
ServerSocket ss=new ServerSocket(7171);
2.获取连接的客服端的socket
Socket clientSocket=ss.accept();
3.向客服端发送数据
PrintStream ps=new PrintStream(clientSocket.getOutputStream());
ps.println("登陆成功!!!");
//输出完毕
clientSocket.shutdownOutput();
4.接收客服端发来的数据
BufferedReader br=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String line=null;
while((line=br.readLine())!=null){
System.out.println(line);
}
这样只能时发固定的消息,在上面的基础上做出一点修改,从终端输入信息
模拟客服端
class MyClient{
public static void main(String[] args) {
//连接服务器端的socket
Socket socket = null;
try {
socket=new Socket("10.129.25.253",8888);
//接收服务器端信息
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
while ((line=br.readLine())!=null){
System.out.println(line);
}
//从键盘输入数据 发送给服务器
} catch (IOException e) {
System.out.println("网络出错 请重新链接");
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
模拟服务器
class MyServer{
public static void main(String[] args) {
//创建ServerSocket
try(ServerSocket ss=new ServerSocket(8888)) {
//监听客服端的链接
Socket socket=ss.accept();
//从终端接收数据
BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
//获取向客服端输出数据的输出流
PrintStream sp=new PrintStream(socket.getOutputStream());
String line=null;
while((line=keyin.readLine())!=null){
//发送给客服端
sp.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
还有个注意点就是在运行时一定要先运行服务器,在运行客服端,不然就连不上服务器
效果:
在服务器中输入:

在客服端显示:

实现双向聊天

为啥要用多线程来实现:为了能够让聊天更加流畅,不至于出现阻塞的现象,线程是一直运行着的,主线程和子线程两者不是同时运行的,他们没有先后顺序
1.创建一个子线程处理客服端接收服务器端的数据
class ClientThread implements Runnable{
private Socket socket;
public ClientThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader bufferedReader=null;
try {
//获取服务器端地输入流对象
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//读取数据
String line=null;
while ((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络出错 请重新链接");
System.exit(-1);
}finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if(socket!=null){
socket.close();
}
}catch (IOException e){
}
}
}
}
2.模拟客服端,实现子线程
class Client{
public static void main(String[] args) throws IOException {
Socket socket=new Socket("10.129.25.253",2584);
//用一个子线程处理服务器处理服务器端数据
new Thread(new ClientThread(socket)).start();
//主线程处理终端输入 发送给服务器端
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
PrintStream printStream=new PrintStream(socket.getOutputStream());
String line=null;
while ((line=bufferedReader.readLine())!=null){
printStream.println(line);
}
}
}
3.创建一个子线程处理服务器端接收客服端地数据
class ServerThead extends Thread{
private Socket socket;
public ServerThead(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader bufferedReader=null;
try{
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
while ((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
}catch (IOException e){
System.out.println("网络异常 请重新链接");
System.exit(-1);
}finally {
try{
if(bufferedReader!=null){
bufferedReader.close();
}
if(socket!=null){
socket.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
super.run();
}
}
4.模拟服务器端,实现子线程
class Server{
public static void main(String[] args)throws IOException {
ServerSocket serverSocket=new ServerSocket(2584);
Socket socket=serverSocket.accept();
//创建子线程 处理客服端地输入信息
new ServerThead(socket).start();
//终端输入
BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
//客服端地输入流对象
PrintStream ps=new PrintStream(socket.getOutputStream());
//读取终端的输入 输出给客服端
String line=null;
while ((line=keyin.readLine())!=null){
ps.println(line);
}
}
}
实行效果:
在服务器端(图中绿色表示自己发出的)

在客服端(图中绿色表示自己发出的)

实现群聊
如何实现群聊:
在服务器端维护一个数组(socket)这个数组是用来存放每个客户端的,以便于在发群发消息的时候,能够群发消息,通过数组的遍历来实现群发消息
群聊的机制:

1.在服务器端实现一个数组
public static ArrayList<Socket> sockets = new ArrayList<>();
2.创建一个子线程处理服务器端接收客服端地数据
static class ServerThead extends Thread {
private Socket socket;
public ServerThead(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
//群发消息
//遍历数组
for (Socket socket : sockets){
//socket对应的客服端发送消息
PrintStream printStream=new PrintStream(socket.getOutputStream());
printStream.println(line);
}
}
} catch (IOException e) {
System.out.println("网络异常 请重新链接");
System.exit(-1);
} finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
super.run();
}
}
}
3.在服务器中等待客服端来连接,并把他们放入数组保存,开启一个线程处理每个客服端的输入
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(1111);
//不停地等待客服端来链接
while (true) {
Socket socket = serverSocket.accept();
sockets.add(socket);
//开启一个线程处理每个客服端的输入
new ServerThead(socket).start();
}
}
4.创建一个子程序 接收服务器发送的数据
class ClientThread extends Thread{
private Socket socket;
public ClientThread(Socket socket){
this.socket=socket;
}
BufferedReader bufferedReader=null;
@Override
public void run() {
try {
bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
while ((line=bufferedReader.readLine())!=null){
System.out.println(line);
}
} catch (IOException e) {
System.out.println("网络错误 请重新链接");
System.exit(-1);
}finally {
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if(socket!=null){
socket.close();
}
}catch (IOException e){
}
}
super.run();
}
}
5.创建客服端,并开始子程序
class Client{
public static void main(String[] args) throws IOException{
Socket socket=new Socket("10.129.25.253",1111);
//开启子程序
new ClientThread(socket).start();
//客服端的终端输出给服务器
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
PrintStream printStream=new PrintStream(socket.getOutputStream());
String line=null;
while((line=bufferedReader.readLine())!=null){
printStream.println(line);
}
}
}
class Client1{
public static void main(String[] args) throws IOException{
Socket socket=new Socket("10.129.25.253",1111);
//开启子程序
new ClientThread(socket).start();
//客服端的终端输出给服务器
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
PrintStream printStream=new PrintStream(socket.getOutputStream());
String line=null;
while((line=bufferedReader.readLine())!=null){
printStream.println(line);
}
}
}
class Client2{
public static void main(String[] args) throws IOException{
Socket socket=new Socket("10.129.25.253",1111);
//开启子程序
new ClientThread(socket).start();
//客服端的终端输出给服务器
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
PrintStream printStream=new PrintStream(socket.getOutputStream());
String line=null;
while((line=bufferedReader.readLine())!=null){
printStream.println(line);
}
}
}
实现效果:
客服端1:(图中绿色表示自己发出的)

客服端2(图中绿色表示自己发出的)

客服端3(图中绿色表示自己发出的)

(3)实现向服务器上传图片和在服务器上下载图片
实现客服端向服务器上传照片,实现客户端在服务器中下载照片。在这两个过程中图片的传输是用字节流来传输的,不能用字符串或者是字符数组来传递。在这两个过程流程都是一样的,只是在不同的过程中
向服务器上传图片
class Client{
public static void main(String[] args) throws IOException {
//链接服务器端
Socket socket=new Socket("10.129.25.253",6666);
//创建服务器端的输入流 用于接收服务器端发来的数据
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
//向服务器端发送一张图片
//1.将文件写到内存
FileInputStream fis=new FileInputStream("C:\\Users\\ASUS\\Desktop\\1.jpg");
//2.创建字节流 outputStream
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
byte[] buf = new byte[1024];
int len = -1;
while ((len = fis.read(buf)) != -1){
bos.write(buf,0,len);
}
socket.shutdownOutput();
}
}
class Server{
public static void main(String[] args)throws IOException {
//从创建服务器端ServerSocket
ServerSocket ss=new ServerSocket(6666);
//监听客服端连接
//当有客服端来连接这个服务器时 就可以得到对应的socket
Socket socket= ss.accept();
//创建客服端对应的输出流 用于向这个客服端发送数据
PrintStream ps=new PrintStream(socket.getOutputStream());
ps.println("连接成功 可以发数据了");
// 接收客服端传递过来的图片
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
//文件对应的输出流
String path = "D:\\android\\Javaday1\\java\\src\\main\\java\\day12media\\1.jpg";
FileOutputStream fos = new FileOutputStream(path);
byte[] buf = new byte[1024];
int len = -1;
while((len = bis.read(buf)) != -1){
fos.write(buf,0,len);
}
}
}
在服务器上下载图片
//模拟客户端
class Client {
public static void main(String[] args) throws IOException {
Socket socket=new Socket("10.129.25.253",3333);
//接收
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(bufferedReader.readLine());
//接收服务器传来的信息
BufferedInputStream b=new BufferedInputStream(socket.getInputStream());
String path="D:\\android\\Javaday1\\java\\src\\main\\java\\day12media1\\1.jpg";
FileOutputStream f =new FileOutputStream(path);
byte[] j=new byte[1024];
int len=0;
while((len = b.read(j)) != -1){
f.write(j,0,len);
}
}
}
//模拟服务器
class Server{
public static void main(String[] args)throws IOException {
ServerSocket serverSocket=new ServerSocket(3333);
Socket socket=serverSocket.accept();
PrintStream printStream=new PrintStream(socket.getOutputStream());
printStream.println("下载成功!");
FileInputStream fileInputStream=new FileInputStream("C:\\Users\\ASUS\\Desktop\\1.jpg");
//向客服端传输数据
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(socket.getOutputStream());
byte[] i=new byte[1024];
int len=-1;
while((len=fileInputStream.read(i))!=-1){
bufferedOutputStream.write(i,0,len);
}
socket.shutdownOutput();
}
}