心得体会
- 今天学的东西挺有趣的,从简单的服务端与客户端模拟,到实现群聊,一步一步感觉demo实现的功能越来越多。同一段的代码,不厌其烦的反复敲,与其他代码组合起来,就实现了不同的功能^^
--
今日所学
-
1.OSI与TCP/IP体系模型
-
2.URL
-
3.IP地址和端口
-
4.TCP和UDP
-
5.Socket
-
6.服务器端和客户端(聊天小demo)
具体操作
1.OSI与TCP/IP体系模型
image.pngimage.png
2.URL
URL的定义
URL是
统一资源定位符(Uniform Resource Locator,缩写为URL)
,又叫做网页地址,是互联网上标准的资源的地址(Address)。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
URL的组成
PVF1_B{`M{4SFAN`04DAUJG.pnghttp://www.baidu.com/search?code=android
http:——指定因特网服务的类型。最流行的类型是HTTP
www.baidu.com——域名 表示一台网络中的电脑
search——URL的这部分被称为层级文件路径
code=android —— 查询字符串是一个非必须的字段,只要负责将一系列非层级格式的任意参数传给服务器。可同时传递多个参数,参数之间用“&"符号连接,每个参数名与值用 = 隔开
& ——如果有多个参数使用&符号链接 例如: www.baidu.com?uid=777&qqq=666
3.IP地址和端口号
- 1、IP地址用于唯一标示网络中的一个通信实体。这个通信实体可以是一台主机,可以是一台打印机,或者是路由器的某一个端口。而在基于IP协议网络中传输的数据包,必须使用IP 地址来进行标示。IP地址就像写一封信,必须指定收件人的地址一样。每个被传输的数据包中都包括了一个源IP和目标IP。
- 2、IP地址唯一标示了通信实体,但是一个通信实体可以有多个通信程序同时提供网络服务。这个时候就要通过端口来区分开具体的通信程序。一个通信实体上不能有两个通信程序,使用同一个端口号。
- 3.
IP地址和端口号,就像一个出差去外地入住酒店一样,IP地址表示了酒店在具体位置,而端口号则表示了这个人在酒店的房间号。
4.TCP和UDP
1、TCP是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。它能够提供两台计算机之间的可靠的数据流,HTTP、FTP、Telnet等应 用都需要这种可靠的通信通道。
2、UDP是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传送目的地,至于能够达到目的地,达到目的地的时间以及内容的正确性都是不能保证的。
既然有了保证可靠传输的TCP协议,为什么还要非可靠传输的UDP协议呢?原因有两个:
- 1、可靠的传输是要付出代价的,对数据内容的正确性的检验必然会占用计算机处理时间和网络带宽。因此TCP的传输效率不如UDP高。
- 2、许多应用中并不需要保证严格的传输可靠性,比如视频会议系统,并不要求视频音频数据绝对正确,只要能够连贯就可以了。所以在这些场景下,使用UDP更合适些。
5.Socket
Socket定义
套接字(通讯双方C/S协商好的约定),网络上两个程序通过一个双向的通信连接实现数据的交换,这个链接的一端称为一个Socket。应用程序通过套接字向网络发出请求或者应答网络请求。
image.png
网络通信的要素
- 网络请求就是通过Socket建立连接然后互相通信
①IP地址(主机的唯一标识)
②端口号(定位程序,标示不同的进程)
③传输协议(通讯的规则
)[常见的协议:TCP、UDP]
2.服务器端和客户端
服务器端和客户端的定义与联系
-
服务器端:
服务端是为客户端服务的,服务的内容诸如向客户端提供资源,保存客户端数据。例如:
手机的存储、数据处理
-
客户端:
客户端(Client)或称为用户端,是指与服务器相对应,为客户提供本地服务的程序。除了一些只在本地运行的应用程序之外,一般安装在普通的客户机上,需要与服务器端互相配合运行 例如:
手机app 浏览器 桌面QQ
客户端、服务器端模拟
demo1—实现客户端和服务器端传递数据:( 注意端⼝号必须相同 先运⾏Server 再运⾏Client)
-
服务器端
- 1.新建一个Server类,并在类中写一个main方法
- 2.创建服务器端的ServerSocket(直接创建一个ServerSocket对象,并写一个端口 ) (注意:端口的值不能太大,否则会出现异常!!!)
- 3.获取连接的客户端的socket
- 4.向客户端发送数据
- 5.接收客户端发来的数据
//模拟服务器端
class Server{
public static void main(String[] args)throws IOException{
//1.创建服务器端的ServerSocket
ServerSocket ss=new ServerSocket(8989);
//2.获取连接的客户端的socket
Socket clientSocket=ss.accept();
//3.向客户端发送数据
//BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(cilentSocket.getOutputStream()));
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);
}
}
}
ps:
- 1.在创建ServerSocket对象时会出现 异常,这时需要抛出异常或自己处理异常,上图代码采取的是系统自动抛出异常
- 2.向客户端发送数据有两种方式,一种用
BufferedWriter
,一种用 **PrintStream**
- 3.向客户端发送数据后需要关闭数据
-
客户端:
- 1.新建一个Client类
- 2.创建用于通信的socket (指明和谁通信:ip地址 端口号)
- 3.接收服务器端的数据
- 4.读取服务器端发来的数据
- 5.客户端向服务器端发送数据
class Client{
public static void main(String[] args) throws IOException {
//1.创建用于通信的socket
//指明和谁通信:ip地址 端口号
Socket socket=new Socket("192.168.43.121",8989);
//接收服务器端的数据
BufferedReader br=new BufferedReader( new InputStreamReader(socket.getInputStream()));
//读取服务器端发来的数据
// socket.getOutputStream();
String line =null;
while((line=br.readLine())!=null){
System.out.println(line);
}
//客户端向服务器端发送数据
PrintStream ps=new PrintStream(socket.getOutputStream());
ps.println("你好啊!");
socket.shutdownOutput();
}
}
ps:
- 1.在创建Socket对象时会出现 异常,这时需要抛出异常或自己处理异常,上图代码采取的是系统自动抛出异常
- 2.向服务器端发送数据后需要关闭数据
运行结果:
13X5Q9@V9${O__R@$NZPKA5.png
image.png
demo2—实现服务器端⼿动输⼊,将输⼊的内容发送给客户端 ,可以⼀直发( 注意端⼝号必须相同 先运⾏Server 再运⾏Client)
-
服务器端
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 ps = new PrintStream(socket.getOutputStream());
String line = null;
while ((line = keyin.readLine()) != null){
//发送给客户端
ps.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
客户端:
class Myclient{
public static void main(String[] args){
//连接服务器端的socket
Socket socket = null;
try{
socket = new Socket("192.168.43.121",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();
}
}
}
}
PS:
- 1.创建的对象的时候有异常,此时应该捕获异常,并且最后还要用finally来关闭(finally是必须执行的,有清理资源等作用),finally有异常进行捕获
- 2.**demo1与demo2最大的区别是demo2实现了从终端输入
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
运行结果
20190821_221949.gif
demo3—实现客户端向服务器端上传图⽚
-
服务器端
class Server{
public static void main(String[] args) throws IOException {
//创建服务器端的ServerSoket
ServerSocket ss = new ServerSocket(8080);
//监听客户端连接
//当有客户端来连接这个服务器 就可以得到对应的socket
//当没有客户端来连接 服务器一直在这里等待
Socket socket = ss.accept();
//创建客户端对应的输出流 用于向这个客户端发送数据
PrintStream ps = new PrintStream(socket.getOutputStream());
ps.println("连接成功 可以发数据了!");
BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
String path = "F:/JAVA/DAY12/Day1/src/main/java/Day12/java/media/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 socket = new Socket("192.168.43.121",8080);
//创建服务器端对应的输入流 用于接收服务器端发来的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
//向服务器端发送文件(图片)
//1.将文件写入到内存里
String path = "C:/Users/Administrator.000/Desktop/psd/4456d40b75f2f8b4";
FileInputStream fis = new FileInputStream(path);
//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();
}
}
7$Q(QXO8O`DV1A284FI5`Y4.png
U5BV(){G)`CG_I4KV{_85MV.png
PS:
- 我选择图片时用png格式不能成功,如果用png格式不行,可以换一个格式看看
demo4—使⽤多线程实现对聊
为什么要用多线程实现?
- 聊天的过程是双向,同时进行的
class Client{
public static void main(String[] args)throws IOException {
Socket socket=new Socket("192.168.43.121",8888);
//用一个子线程处理服务器端数据
new Thread(new ClientThread(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);
}
}
}
/**
* 创建一个子线程处理客户端接收服务器端数据
*/
class ClientThread implements Runnable{
private Socket socket;
//保存操作socket
public ClientThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br=null;
try {
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("网络出错 请重新登录");
System.exit(-1);
}finally{
try {
if (br != null) {
br.close();
}
if(socket!=null) {
socket.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
class Server{
public static void main(String[] args)throws IOException{
ServerSocket ss=new ServerSocket(8888);
//获取客户端的socket
Socket socket= ss.accept();
new ServerThread(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);
}
}
}
/**
* 服务器端创建一个子线程处理服务器端接受客户端的数据
*/
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader br =null;
try {
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("网络出错 请重新登录");
System.exit(-1);
}finally{
try {
if (br != null) {
br.close();
}
if(socket!=null) {
socket.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
PS:
-
1.上图的demo中创建子线程用了继承Thread的方法
运行结果:
![`]E13M5$OXFI)N2MF2N368E.png
demo4—实现群聊
-
Client和ClientThread代码和对聊不变,主要在Server⾥添加数组和监听客户端连接
2ZPYR{~)(5IS2J_AEAVR_QG.jpg
class server{
public static ArrayList<Socket> sockets=new ArrayList<>();
public static void main(String[] args)throws IOException {
ServerSocket ss=new ServerSocket(6666);
//不停的等待用户端连接
while(true) {
Socket socket = ss.accept();
//当有客户端连接过来了 就保存
sockets.add(socket);
//开启一个线程处理每个客户端的输入
new ServerThread(socket).start();
}
}
}
/**
* 服务器端创建一个子线程处理服务器端接受客户端的数据
*/
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
BufferedReader br =null;
try {
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line=null;
while((line=br.readLine())!=null){
//群发消息
//遍历数组
for(Socket s:server.sockets){
PrintStream ps=new PrintStream(s.getOutputStream());
ps.println(line);
}
}
}catch(IOException e){
System.out.println("网络出错 请重新登录");
System.exit(-1);
}finally{
try {
if (br != null) {
br.close();
}
if(socket!=null) {
socket.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
class Client{
public static void main(String[] args)throws IOException {
Socket socket=new Socket("192.168.43.121",6666);
//用一个子线程处理服务器端数据
new Thread(new ClientThread(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);
}
}
}
/**
* 创建一个子线程处理客户端接收服务器端数据
*/
class ClientThread implements Runnable{
private Socket socket;
//保存操作socket
public ClientThread(Socket socket){
this.socket=socket;
}
@Override
public void run() {
BufferedReader br=null;
try {
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("网络出错 请重新登录");
System.exit(-1);
}finally{
try {
if (br != null) {
br.close();
}
if(socket!=null) {
socket.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
20190821_231418.gif
PS:
- 所连接的所有客户端,端口号必须一致!!!
网友评论