java网络编程基础知识
通信协议:计算机网络中实现通信必须有一些约定,即通信协议。对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。
TCP协议:提供可靠的数据传输服务;(传输层)
IP协议: 进行IP数据包的分割和组装。(应用层)
IP地址和端口号
1、IP地址:为实现网络中不同的计算机之间的通信,在网络中的每台机器都必须有一个与众不同的标识,这就是IP地址(IP Address)。
格式:数字型、32位、由4段8位的二进制数组成。一般表示为十进制形式(4个0~255的十进制整数),中间用圆点隔开。如:166.111.78.98
域名地址:也是分段表示的,便于记忆的、字符串形式。
DNS服务器:域名服务器(解析器)
将www.baidu.com类型的域名解析为对应的IP地址
*2、端口:一个16位的整数,用于表示数据交给哪个通信程序处理。因此,端口就是应用程序与外界交流的出入口,它是一种抽象的软件结构,包括一些数据结构和I/O(基本输入/输出)缓冲区。
不同的应用程序处理不同端口上的数据,同一台机器上不能有两个程序使用同一个端口,端口号可以从0到65535,通常将它分为三类:
*、公认端口(Well Known Ports):从0到1023,它们紧密绑定(Binding)一些服务。
*、注册端口(Registered Ports):从1024到49151。它们松散地绑定一些服务。
*、动态和/或私有端口(Dynamic and/or Private Ports):从49152到65535,这些端口是应用程序使用的动态端口,应用程序一般不会主动使用这些端口。
TCP\UDP协议
1、TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个 socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送或接收操作。TCP是Tranfer Control Protocol的 简称,是一种面向连接的保证可靠传输的协议。通过TCP协议传输,得到的是一个顺序的无差错的数据流。发送方和接收方的成对的两个socket之间必须建立连接,以便在TCP协议的基础上进行通信,当一个socket(通常都是server socket)等待建立连接时,另一个socket可以要求进行连接,一旦这两个socket连接起来,它们就可以进行双向数据传输,双方都可以进行发送 或接收操作。
2、UDP是User Datagram Protocol的简称,是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
比较:
UDP:
1, 每个数据报中都给出了完整的地址信息,因此无需要建立发送方和接收方的连接。
2, UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内。
3, UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方
TCP:
1, 面向连接的协议,在socket之间进行数据传输之前必然要建立连接,所以在TCP中需要连接时间。
2, TCP传输数据大小限制,一旦连接建立起来,双方的socket就可以按统一的格式传输大的数据。
3, TCP是一个可靠的协议,它确保接收方完全正确地获取发送方所发送的全部数据。
Demo1:客户端向服务器端上传图片
class Client{
public static void main (String []args) throws IOException {
//连接服务器端 获取socket
Socket socket = new Socket("127.0.0.1",8080);
// 创建服务器端对应的输入流 用于接受向服务器端发来的数据
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(br.readLine());
//向服务器端发送文件(图片)
// 1.将文件写入内存
String path = "C:/Users/12931/Pictures/1.jpg";
FileInputStream fis = new FileInputStream(path);
//将内容输出到服务器端
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
//3.将文件的内容一点一点的传输给服务器
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 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 = "D:/Java learning/java/src/main/java/day12/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);
}
}
}
注:实现客户端和服务器器端传递数据: 注意端⼝口号必须相同 先运行Server 再运行Client
使用多线程实现对聊
功能:客户端和服务器都可以随意地发送内容
使用多线程实现如下操作:
客户端操作:
主线程:接收终端输入 将终端的输入发送给服务器端
子线程:接收服务器端发过来的数据
服务器端:
主线程:接受终端输入 将终端输入发送给客户端
子线程:接收客户端发过来的数据
class Client{
public static void main(String [] args) throws IOException {
Socket socket = new Socket("127.0.0.1",6665);
//主线程处理终端输入发送给服务器端
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
PrintStream ps = new PrintStream(socket.getOutputStream());
//用一个子线程处理服务器端数据
new Thread( new ClientThread(socket)).start();
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(6665);
//获取客户端的socket
Socket socket = ss.accept();
//终端输入流对象
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
//创建子线程 梳理客户输入信息
new ServerThread(socket).start();
//客户端的输出流对象
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 {
//关闭输入输出流
//关闭对应的socket连接
try{
if(br !=null){
br.close();
}
if(socket!= null){
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
用多线程实现群聊
1.在对聊的基础上更改Server类,使其不停的等待客户端来连接
2.用数组保存所来连接的客户端
3.更改ServerThread类,将服务器接收到的全部消息发给每一个客户端
/**
*1.如何实现群聊
* 在服务器端维护一个数组{socket}
* 2.私聊
*/
class Server{
//保存每一个连接过来的socket对象
public static ArrayList 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 {
//关闭输入输出流
//关闭socket文件
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("127.0.0.1",6666);
//主线程处理终端输入发送给服务器端
BufferedReader keyin = new BufferedReader(new InputStreamReader(System.in));
PrintStream ps = new PrintStream(socket.getOutputStream());
//用一个子线程处理服务器端数据
new Thread( new day12.ClientThread(socket)).start();
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();
}
}
}
}
感悟: 有时候因为手速问题,自己敲代码不够快,导致 上面讲的时候自己又在跟着敲代码,听的就不是很认真,会错过些东西,可能还是自己敲代码敲得少吧。今天把所有的Demo都敲了两遍之后才理解的差不多,感觉在学新东西之前还是要先预习一下才能听的好。