黑马程序员--javaSE--网络编程基础总结

------- android培训java培训、期待与您交流! ----------


在写这篇博客之前,我先写点自己的感慨:java网络编程以前是学过的,但在今天复习总结这方面的
知识点时,还真有点力不从心,好多东西只是记得大概,所以在真正写代码时就遇见了好多问题,不说别的,
就说在写通过网络编程实现多线程上传文件时,就是因为读取文件路径的方式错误,衍生了好多的错误,
比如说connect reset等,最后花了一个多小时才调试出来。更觉得以后学知识一定要扎实,不能想当然的去编写程序,
出错了,吃苦头的还是自己!
感慨完了,就开始总结吧。

一、关于java网络编程一些必须要知道的概念

网络编程:说的简单点就是两台计算机相互通讯数据,基本模型就是客户机到服务器模型,
简单的说就是两个进程之间相互通讯,然后其中一个必须提供一个固定的位置,而另一个则只需要知道这个固定的位置。
并去建立两者之间的联系,然后完成数据的通讯就可以了,这里提供固定位置的通常称为服务器,而建立联系的通常叫做客户端,
基于这个简单的模型,就可以进入网络编程啦。

网络通信的三要素:
1.IP地址:
通信的计算机之间是通过IP地址来查找相应的目标计算机的。要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,
通过这个标识号来指定要接受数据的计算机和识别发送的计算机,在TCP/IP协议中,这个标识号就是IP地址。
2.端口号:
一台计算机向另一台计算机发送数据,发送到某个应用程序上,而计算机上这个应用程序就是用端口号来标志的。
计算机一共分配了0--65535个数据用来表示端口。每一个应用程序都有一个自己的端口,也就是一个整数,
通过这个端口来决定传输数据的时候具体发送到那一个应用程序上。
3.传输协议:TCP和UDP
TCP协议特点:
<1>.在传输数据前需要建立连接,通过三次握手完成,tcp通信是面向连接的。
<2>.数据在传输过程中不需要大包操作,可以传输大量的数据。
<3>.由于是在建立连接的基础上进行数据的传输,该协议比较可靠。
<4>.想传数据,必须建立连接,在速度和效率上稍低。
UDP协议的特点:
<1>.UDP是面向无连接的,双方进行通信不需要建立连接。
<2>.数据在传输过程中要进行打包操作,每个包的最大为64K字节。
<3>.面向无连接的传输,该协议不可靠。
<4>.不需要建立连接,传输速度快,效率高。

二、java中的网络编程
jdk为网络编程所设计的类,都封装在java.lang.net包中,以下从两方面列举一些比较重要的类和方法

UDP协议的网络编程:
DatagramSocket --用于创建通信端口的类
send(DatagramPacket p)  --从此套接字发送数据报包。(用于发送端)
receive(DatagramPacket p)  --从此套接字接收数据报包。(用于接收端)
close()  -- 关闭此数据报套接字。
构造方法:DatagramSocket(int port)   创建数据报套接字并将其绑定到本地主机上的指定端口。

DatagramPacket --用于封装要发送的数据包
构造方法:DatagramPacket(byte[] buf, int length, InetAddress address, int port) 
作用: 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号(用于创建发送端的数据包)
DatagramPacket(byte[] buf, int length) (用于创建接收端)
注意点:所以要将发送的数据用字节数组来存储
getData() 返回数据缓冲区。 (用于接收客户端发来的数据)

TCP协议的网络编程:
Socket --用于客户端 
构造方法:Socket(InetAddress address, int port)  --创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
getInputStream()  --返回此套接字的输入流。读取流中的数据
getOutputStream() --返回此套接字的输出流。向流中写数据
close()  -- 关闭此数据报套接字。

ServerSocket --用于服务器端
构造方法:ServerSocket(int port) --创建绑定到特定端口的服务器套接字。
accept() --侦听并接受到此套接字的连接

三、代码实现

tcp案例:要求是在客户端输入信息,在服务器端输出,并在客户端打印服务端给的反馈信息
package cn.itcast.udp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
 * 客户端:
 * 通过键盘录入数据。"886"表示停止。
 */
public class SendClient {
public static void main(String[] args) throws IOException {
	// 创建UDP发送端的服务
	DatagramSocket ds = new DatagramSocket();
	// 把键盘录入数据用高效缓冲流封装
	BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

	String line = null;
	while ((line = br.readLine()) != null) {
		if ("886".equals(line)) {
			break;
		}
		byte[] bys = line.getBytes();
		// 数据包
		DatagramPacket dp = new DatagramPacket(bys, bys.length,
				InetAddress.getByName("liuyi"), 11111);
		// 发送数据
		ds.send(dp);
	}

	// 关闭资源
	ds.close();
	}

}

package cn.itcast.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/*
 * 服务端:
 * 接受端一直开启服务。
 */
public class ReceiveServer {
	public static void main(String[] args) throws IOException {
		// 创建UDP接收端Socket对象
		DatagramSocket ds = new DatagramSocket(11111);

		// 为了循环多次接受
		while (true) {
			// 创建字节数组作为数据包的缓冲区
			byte[] bys = new byte[1024];
			DatagramPacket dp = new DatagramPacket(bys, bys.length);
			// 读取数据包数据
			ds.receive(dp);

			// 解析数据包
			String ip = dp.getAddress().getHostAddress();
			int port = dp.getPort();

			String text = new String(dp.getData(), 0, dp.getLength());
			System.out.println(ip + "***" + port + "***" + text);
		}

		// 关闭资源
		// ds.close();
	}

}

注意点:在运行代码时,可以先运行服务端也可以先运行客户端

tcp案例:在客户端上传文件,服务器端接收并存储文件,因为昨天总结的是多线程方面的知识,
所以在这里,我模拟了多线程上传文件的情形。
//服务器端的线程
package cn.itcast.tcp;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class UploadThraad implements Runnable {
	//线程要共享的变量
	private Socket s;
	public UploadThraad(Socket s) {
		this.s = s;
	}
	//将服务器端要执行的代码放到run方法内
	@Override
	public void run() {
		// 获取IP
		String ip = s.getInetAddress().getHostAddress();
		System.out.println(ip + "连接上了");
		try {

			File file = new File("d:\\java");
			if (!file.exists()) {
				file.mkdir();
			}

			long time = System.currentTimeMillis();

			File fileDir = new File(file, ip +"-"+time + ".txt");

			// 从客户端获取输入写入到文件中,将文件流信息用包装流更高效
			BufferedReader br = new BufferedReader(new InputStreamReader(
					s.getInputStream()));

			PrintWriter pw = new PrintWriter(new FileWriter(fileDir), true);

			String line = null;
			while ((line = br.readLine()) != null) {
				pw.println(line);
			}

			// 给客户端一个反馈
			PrintWriter pwClient = new PrintWriter(s.getOutputStream(), true);
			pwClient.println("上传成功了");

			pw.close();
			s.close();
			// ss.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

//客户端的代码
package cn.itcast.tcp;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/*
 * 上传文件
 */
public class UploadClient {
	public static void main(String[] args) throws IOException {
		Socket s = new Socket("192.168.1.100", 12021);

		// 把一个文本文件上传到服务器
		BufferedReader br = new BufferedReader(new FileReader("hello.txt"));
		PrintWriter pw = new PrintWriter(s.getOutputStream(), true);

		String line = null;
		while ((line = br.readLine()) != null) {
			pw.println(line);
		}
		//上传完毕后,就将流关闭,因为方法readLine是阻塞式的,否则会报错
		s.shutdownOutput();
		

		// 如果服务器接收到所有的数据后,给我一个反馈
		BufferedReader serverBr = new BufferedReader(new InputStreamReader(
				s.getInputStream()));
		String text = serverBr.readLine();
		System.out.println("server:" + text);

		br.close();
		s.close();

	}

}

//测试类
package cn.itcast.tcp;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadThreadTest {

	public static void main(String[] args) throws IOException {
		ServerSocket ss = new ServerSocket(12021);

		while (true) {
			Socket s = ss.accept();
			new Thread(new UploadThraad(s)).start();
		}
	}

}

以上我只是实现了文本文件的上传,但是仍然可以将此代码扩展,实现任意文件的上传,
我的思路是:首先必须以二进制的方式读写文件,可以使用BufferedInputStream类封装流,会
使代码更有效率,其次在客户端必须要将文件的后缀名写入到流中,并在服务端获取。


总结:(要总结的是java网络编程的步骤,本来想自己总结的,但在网上看到一大神总结特别好,就摘抄下来了—..-)
客户端网络编程步骤
客户端(Client)是指网络编程中首先发起连接的程序,客户端一般实现程序界面和基本逻辑实现,在进行实际的客户端编程时,无论客户端复杂还是简单,以及客户端实现的方式,客户端的编程主要由三个步骤实现:
1、 建立网络连接
客户端网络编程的第一步都是建立网络连接。在建立网络连接时需要指定连接到的服务器的IP地址和端口号,建立完成以后,会形成一条虚拟的连接,后续的操作就可以通过该连接实现数据交换了。
2、 交换数据
连接建立以后,就可以通过这个连接交换数据了。交换数据严格按照请求响应模型进行,由客户端发送一个请求数据到服务器,服务器反馈一个响应数据给客户端,如果客户端不发送请求则服务器端就不响应。
根据逻辑需要,可以多次交换数据,但是还是必须遵循请求响应模型。
3、 关闭网络连接
在数据交换完成以后,关闭网络连接,释放程序占用的端口、内存等系统资源,结束网络编程。
最基本的步骤一般都是这三个步骤,在实际实现时,步骤2会出现重复,在进行代码组织时,由于网络编程是比较耗时的操作,所以一般开启专门的现场进行网络通讯。
 服务器端网络编程步骤
服务器端(Server)是指在网络编程中被动等待连接的程序,服务器端一般实现程序的核心逻辑以及数据存储等核心功能。服务器端的编程步骤和客户端不同,是由四个步骤实现,依次是:
1、 监听端口
服务器端属于被动等待连接,所以服务器端启动以后,不需要发起连接,而只需要监听本地计算机的某个固定端口即可。
这个端口就是服务器端开放给客户端的端口,服务器端程序运行的本地计算机的IP地址就是服务器端程序的IP地址。
2、 获得连接
当客户端连接到服务器端时,服务器端就可以获得一个连接,这个连接包含客户端的信息,例如客户端IP地址等等,服务器端和客户端也通过该连接进行数据交换。
一般在服务器端编程中,当获得连接时,需要开启专门的线程处理该连接,每个连接都由独立的线程实现。
3、 交换数据
服务器端通过获得的连接进行数据交换。服务器端的数据交换步骤是首先接收客户端发送过来的数据,然后进行逻辑处理,再把处理以后的结果数据发送给客户端。简单来说,就是先接收再发送,这个和客户端的数据交换数序不同。
其实,服务器端获得的连接和客户端连接是一样的,只是数据交换的步骤不同。
当然,服务器端的数据交换也是可以多次进行的。
在数据交换完成以后,关闭和客户端的连接。
4、 关闭连接
当服务器程序关闭时,需要关闭服务器端,通过关闭服务器端使得服务器监听的端口以及占用的内存可以释放出来,实现了连接的关闭。

最后在博客的结尾吐糟一下:为什么我还要在元旦写博客呢?!!!哎,悲催的程序员啊!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值