Java基础(一) 网络编程

目录

一. 网络基础知识

1. 计算机网络

2. 网络编程

3. IP、域名与端口

4. 网络通讯过程

5. 网络传输方式

二. Java网络编程技术

1. 编程基础

2. 关键组件

2.1 Socket

2.2 ServerSocket

2.3 InetAddress

3. TCP网络编程步骤

3.1 客户端开发

3.2 服务端开发

3.3 完整代码

三. 多线程网络编程

1. MyThread

2. SingleServer

3. SingleClient

4. 运行结果


一. 网络基础知识

1. 计算机网络

计算机网络是两台或更多的计算机组成的网络,同一网络内的任意两台计算机可以直接通信,所有计算机必须遵循同一种网络协议。

2. 网络编程

        对于计算机网络硬件以及底层的基础知识,我们在下学期的《计算机网络》一书中会详细学习。仅对于网络编程来说,大部分的程序设计语言都设计了专门的API实现这些功能,程序员只需要学会组织调用即可。什么是网络编程,简单来说网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据。程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据来进行处理。

3. IP、域名与端口

(1)IP地址

        为了能够方便的识别网络上的每个设备,网络中的每个设备都会有一个唯一的数字标识,这个就是IP地址。由于有这种唯一的地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。

(2)域名

        由于IP地址是一组二进制,不好记忆于是就产生了域名,如www.baidu.com。一个IP地址可以对应多个域名,一个域名只能对应一个IP地址。域名的概念可以类比手机中的通讯簿,由于手机号码不方便记忆,所以添加一个姓名标识号码,在实际拨打电话时可以选择该姓名,然后拨打即可。

       但是由于在网络中传输的数据,全部是以IP地址作为地址标识,所以在实际传输数据以前需要将域名转换为IP地址,实现这种功能的服务器称之为DNS服务器,也就是通俗的说法叫做域名解析。例如当用户在浏览器输入域名时,浏览器首先请求DNS服务器,将域名转换为IP地址,然后将转换后的IP地址反馈给浏览器,然后再进行实际的数据传输。

(3)端口

        IP地址和域名使得我们可以在复杂的网络中找到请求的计算机对象,但是我们最终需要执行的是该计算机中的某个程序,同时也为了让一个计算机可以同时运行多个网络程序,我们就引出了端口。

       同一个计算机中每个程序对应唯一的端口,这样一个计算机上就可以通过端口区分发送给每个端口的数据了,换句话说,也就是一个计算机上可以并发运行多个网络程序,而不会在互相之间产生干扰。在硬件上规定,端口的号码必须位于0-65535之间,0~1023的端口号为系统所保留(为了避免冲突在选择端口时尽量选择1024之上)每个端口唯一的对应一个网络程序,一个网络程序可以使用多个端口。这样一个网络程序运行在一台计算上时,不管是客户端还是服务器,都是至少占用一个端口进行网络通讯。在接收数据时,首先发送给对应的计算机,然后计算机根据端口把数据转发给对应的程序。

(4)localhost、127.0.0.1和本机IP之间的区别

  • localhost等于127.0.0.1,不过localhost是域名,127.0.0.1是IP地址。
  • localhost和127.0.0.1不需要联网,都是本机访问。
  • 本机IP需要联网,本机IP是本机或外部访问, 本机 IP 就是本机对外放开访问的IP地址,这个网址就是与物理网卡绑定的IP地址。比如你叫小明,127.0.0.1是“自己”,而本机IP是“小明”。

4. 网络通讯过程

        网络通讯基于“请求-响应”模型。这种一问一答的形式就是网络中的“请求-响应”模型。也就是通讯的一端发送数据,另外一端反馈数据,网络通讯都基于该模型。

        在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。这种网络编程的结构被称作客户端/服务器结构,也叫做Client/Server结构,简称C/S结构,需要建立特定的服务器来服务特定的客户端。而有时候使用通用的客户端,例如浏览器,使用浏览器作为客户端的结构被称作浏览器/服务器结构,也叫做Browser/Server结构,简称为B/S结构。

5. 网络传输方式

(1)TCP协议:是一种面向连接的保证可靠传输的协议,它保障了两个应用程序之间的可靠通信,通讯时必需要建立连接,包括三次握手和四次挥手。它确保接收方完全正确地获取发送方所发送的全部数据。(类似于打电话通讯)

(2)UDP协议:是一种无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。(类似于发短信通讯)

二. Java网络编程技术

1. 编程基础

        java.net 包中 J2SE 的 API 包含有类和接口,它们提供低层次的通信细节。你可以直接使用这些类和接口,来专注于解决问题,而不用关注通信细节。java.net 包中提供了两种常见的网络协议的支持:

  • TCP:TCP 是传输控制协议的缩写,它保障了两个应用程序之间的可靠通信。通常用于互联网协议,被称 TCP / IP。

  • UDP:UDP 是用户数据报协议的缩写,一个无连接的协议。提供了应用程序之间要发送的数据的数据包。

2. 关键组件

2.1 Socket

        socket又被称为套接字,实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。通过Socket,我们才能使用计算机底层的TCP/IP协议栈,实现通信传输。客户端要获取一个 Socket 对象通过实例化建立通信 ,而服务器获得一个 Socket 对象则通过accept() 方法的返回值。

(1)构造方法

序号方法描述
1public Socket(String host, int port) throws UnknownHostException, IOException.
创建一个流套接字并将其连接到指定主机上的指定端口号。
2public Socket(InetAddress host, int port) throws IOException
创建一个流套接字并将其连接到指定 IP 地址的指定端口号。

(2)主要方法

1public InputStream getInputStream() throws IOException
返回此套接字的输入流(读流,从套接字中读取另一端的反馈信息)。
2public OutputStream getOutputStream() throws IOException
返回此套接字的输出流(写流,给另一端传输信息)。
3public void close() throws IOException
关闭此套接字。

注意:一旦服务器端与客户端建立连接以后,双方都由Socket来进行通讯,本质上没有什么区别。本质上,说到java的网络编程倒不如说是Java的I/O编程,因为整个过程中关于服务端和客户端的socket创建也就那么两三行代码,其余的都是操作字节流,字符流等。

2.2 ServerSocket

        ServerSocket是服务器端监听器,服务器通过ServerSocket来监听某个端口的请求。

(1)构造方法

1public ServerSocket(int port) throws IOException
创建绑定到特定端口的服务器套接字。
2public ServerSocket(int port, int backlog) throws IOException
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。

(2)主要方法

1public Socket accept() throws IOException
侦听并接受到此套接字的连接。该方法为阻塞方法,直到接到请求才执行下一步
2public void setSoTimeout(int timeout)
 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位。
3public void bind(SocketAddress host, int backlog)
将 ServerSocket 绑定到特定地址(IP 地址和端口号)。

2.3 InetAddress

        这个类表示互联网协议(IP)地址,将IP地址和域名相关的操作方法包含在该类的内部。其主要方法如下:

序号方法描述
1static InetAddress getByAddress(byte[] addr)
在给定原始 IP 地址的情况下,返回 InetAddress 对象。
2static InetAddress getByAddress(String host, byte[] addr)
根据提供的主机名和 IP 地址创建 InetAddress。
3static InetAddress getByName(String host)
在给定主机名的情况下确定主机的 IP 地址。
4String getHostAddress() 
返回 IP 地址字符串(以文本表现形式)。
5String getHostName() 
 获取此 IP 地址的主机名。
6static InetAddress getLocalHost()
返回本地主机。
7String toString()
将此 IP 地址转换为 String。

        在后续的使用中,经常包含需要使用InetAddress对象代表IP地址的构造方法,当然,该类的使用不是必须的,也可以使用字符串来代表IP地址进行实现。

3. TCP网络编程步骤

3.1 客户端开发

(1)建立连接

Socket socket = new Socket("127.0.0.1",6666);//向ip地址127.0.0.1的计算机的6666端口发起连接请求

(2)获取流对象

            //由Socket对象得到输出流,并构造相应的BufferedReader对象
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //由Socket对象得到输入流,并构造相应的BufferedReader对象
            BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//获取屏幕输入流
sin = new BufferedReader(new InputStreamReader(System.in));

(3)信息传输

	while(true) {
				System.out.print("请输入字符串:");
				String line = sin.readLine();//屏幕输入字符串
				//String line = scanner.nextLine();
               
				bw.write(line + "\n");//注意+"\n"
				bw.flush();//刷新
				//pw.println(line);
				//pw.flush();

				if(line.equals("goodbye")) break;
			    //接受反馈信息
			    System.out.println("结果:" + br.readLine());

			}

(4)关闭连接

try {
				if(socket!=null)socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(pw!=null)pw.close();
			if(bw!=null)
				try {
					bw.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			try {
				if(br!=null)br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

3.2 服务端开发

(1)监听端口

ServerSocket server = null;
server = new ServerSocket(6666);

(2)建立连接

Socket socket = server.accept();//阻塞方法

(3)获得流对象

br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//pw = new PrintWriter(socket.getOutputStream());

(4)传输信息

while(true) {
	//System.out.println("*");
     String line = br.readLine();
	//System.out.println("**");
	System.out.println(line);
	if(line.equals("goodbye"))break;
				
	bw.write(line.toUpperCase() + "\n");
	bw.flush();
	//pw.println(line.toUpperCase());
	//pw.flush();
}

(5)关闭连接

                    try {
				if(server!=null)server.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(bw!=null)
				try {
					bw.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			try {
				if(br!=null)br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

3.3 完整代码

        目的是将客户端字符串转为大写字符串后返回客户端显示。注意:

(1)在使用readLine()方法读取数据时:

  • 读取数据:一行被视为由换行符(’\ n’),回车符(’\ r’)中的任何一个或随后的换行符终止。

  • 返回结果:包含行的内容的字符串,不包含任何行终止字符,如果到达流末尾,则为null。

        所以在往socket中写入信息时为了让readline能够顺利结束读取不阻塞在那,我们在使用write()时手动加上换行控制。

(2)在使用PrintWriter时,其println()方法可以自动打印换行,不用手动加入。

(1)Client端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import javax.swing.text.AbstractDocument.BranchElement;

import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

public class SingleClient {
    public static void main(String []args) {
    	Socket socket = null;
    	BufferedReader br = null;
    	BufferedWriter bw = null;
    	BufferedReader sin=null;
    	PrintWriter pw = null;
    	 
		try {
			socket = new Socket("127.0.0.1",6666);
			
			System.out.println("连接成功...");
			
			Scanner scanner = new Scanner(System.in);
			sin = new BufferedReader(new InputStreamReader(System.in));
			
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			pw = new PrintWriter(socket.getOutputStream());
			
			while(true) {
				System.out.print("请输入字符串:");
				String line = sin.readLine();
				//String line = scanner.nextLine();

				//bw.write(line + "\n");
				//bw.flush();
				pw.println(line);
				pw.flush();

				if(line.equals("goodbye")) break;

			    System.out.println("结果:" + br.readLine());

			}
			
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			try {
				if(socket!=null)socket.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(pw!=null)pw.close();
			if(bw!=null)
				try {
					bw.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			try {
				if(br!=null)br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

    }
}

(2)Server端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

import javax.naming.InitialContext;

public class SingleServer {
	
    public static void main(String []args) {
    	ServerSocket server = null;
    	BufferedReader br = null;
    	BufferedWriter bw = null;
    	PrintWriter pw = null;
    	
    	try {
			server = new ServerSocket(6666);
			System.out.println("服务器正在监听...");
			Socket socket = server.accept();
			System.out.println("接到请求...");
			
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			pw = new PrintWriter(socket.getOutputStream());
			
			while(true) {

				String line = br.readLine();

				System.out.println(line);
				if(line.equals("goodbye"))break;
				
				bw.write(line.toUpperCase() + "\n");
				bw.flush();
				//pw.println(line.toUpperCase());
				//pw.flush();
			}
			
		} catch (IOException e) {
			System.out.println("端口被占用...");
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			try {
				if(server!=null)server.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			if(bw!=null)
				try {
					bw.close();
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			try {
				if(br!=null)br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
    	
    }
}

(3)运行结果

三. 多线程网络编程

我们知道一台服务器是不能每次只处理一个用户请求的。我们必须可以让服务器同时可以处理多个请求,这就涉及到了多线程。多线程改装代码如下:

1. MyThread

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class MyThread implements Runnable {
	private Socket socket;
	private int user;

	public MyThread(Socket socket, int id) {
		// TODO Auto-generated constructor stub
		this.socket = socket;
		user = id;
	}

	@Override
	public void run() {
		System.out.println("Server Thread " + user + " is running!");
		BufferedReader br = null;
		PrintWriter pw = null;

		try {
			br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			pw = new PrintWriter(socket.getOutputStream());

			while (true) {

				String line = br.readLine();

				System.out.println("Thread " + user + " :" + line);
				if (line.equals("goodbye"))
					break;

				pw.println(line.toUpperCase());
				pw.flush();
				
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(br!=null)br.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			pw.close();
		}
	}

}

2. SingleServer

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.naming.InitialContext;

public class SingleServer {

	public static void main(String[] args) {
		ServerSocket server = null;
        ExecutorService servicePool = Executors.newCachedThreadPool();
		
		try {
			server = new ServerSocket(6666);
			System.out.println("服务器正在监听...");
			int i = 0;

			while (true) {
				Socket socket = server.accept();// blocking
				System.out.println("Receive " + (++i) + " Request");
                servicePool.execute(new MyThread(socket, i));
			}

		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}

3. SingleClient

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;

import javax.swing.text.AbstractDocument.BranchElement;

import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;

public class SingleClient {
	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			Socket socket = null;
			BufferedReader br = null;
			BufferedWriter bw = null;
			BufferedReader sin = null;
			PrintWriter pw = null;
			System.out.println("User " + i + " :");
			try {
				socket = new Socket("127.0.0.1", 6666);

				System.out.println("连接成功...");

				Scanner scanner = new Scanner(System.in);
				sin = new BufferedReader(new InputStreamReader(System.in));

				br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
				pw = new PrintWriter(socket.getOutputStream());

				while (true) {
					System.out.print("请输入字符串:");
					String line = sin.readLine();
					// String line = scanner.nextLine();

					// bw.write(line + "\n");
					// bw.flush();
					pw.println(line);
					pw.flush();

					if (line.equals("goodbye"))
						break;

					System.out.println("结果:" + br.readLine());

				}

			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				try {
					if (socket != null)
						socket.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if (pw != null)
					pw.close();
				if (bw != null)
					try {
						bw.close();
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				try {
					if (br != null)
						br.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}
}

4. 运行结果

Server:

服务器正在监听...
Receive 1 Request
Server Thread 1 is running!
Thread 1 :aaaBBByQw
Thread 1 :goodbye
Receive 2 Request
Server Thread 2 is running!
Thread 2 :ttttTTTTbby
Thread 2 :goodbye
Receive 3 Request
Server Thread 3 is running!
.....

Client:

User 0 :
连接成功...
请输入字符串:aaaBBByQw
结果:AAABBBYQW
请输入字符串:goodbye
User 1 :
连接成功...
请输入字符串:ttttTTTTbby
结果:TTTTTTTTBBY
请输入字符串:goodbye
User 2 :
连接成功...
请输入字符串:.....

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿阿阿安

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值