网络通讯要素
端口
1. 物理端口:如,网线的端口。
2. 逻辑端口:用于标识进程的逻辑地址,不同进程的标识;
有效端口:0~65535,其中0~1024系统使用或保留端口。tomcat默认端口:8080 mysql默认端口:3306
协议
为了进行网络中的数据交换(通信)而建立的规则、标准或约定。国际组织定义了通用协议 TCP/IP,IPS协议是用于局域网的。常见协议:TCP,UDP。
IP
具有全球唯一性,相对于internet,IP为逻辑地址。
IP地址是网络中设备的标识,本地回环地址:127.0.0.1 主机名:localhost广播地址:192.168.1.255。
网络模型
不同层的协议是不同的
应用层常见的有:HTTP、FTP
传输层常见的有:TCP、UDP
网际层常见的有:IP
物理层常见的有:网线、光纤、无线……
java中IP对象:InetAddress
此类表示互联网协议(IP)地址。该类没有构造方法,不用new。
示例:获取IP地址与主机名
import java.net.*;
class IPDemo{
public static void main(String[] args){
//通过名称(ip字符串or主机名)来获取一个ip对象。
InetAddress ip = null;
try {
ip = InetAddress.getByName("www.baidu.com");//getAllByName(String host)返回IP地址所组成的数组
// ip = InetAddress.getLocalHost();//本地IP地址
} catch (UnknownHostException e) {
throw new RuntimeException("没有找到该主机");
}
System.out.println("addr:"+ip.getHostAddress());
System.out.println("name:"+ip.getHostName());
}
}
Socket(套接字)
Socket就是为网络服务提供的一种机制,通信的两端都有Socket,网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输。Socket是通信的端点。只要是网络传输,就必须有socket。
UDP与TCP
UDP(User Datagram Protocl):用户数据包协议
UDP特点:
1. 面向无连接
2.数据会被封包,包体积有限制(64K内)
3.不可靠协议
4.速度快
UDP传输
UDP中数据一定要封装到数据包中,数据包中包括目的地址、端口、数据等信息。
需要创建DatagramSocket对象与DatagramPacket对象。
DatagramSocket:此类表示用来发送和接收数据报包的套接字。DatagramSocket具备发送和接受功能,在进行udp传输时,需要明确一个是发送端,一个是接收端。
DatagramPacket:此类表示数据报包,数据报包用来实现无连接包投递服务。
send():从此套接字发送数据报包。
receive():从此套接字接收数据报包,这个方法是一个阻塞式方法。
UDP发送端
步骤:
1.建立udp的socket服务。
2.明确要发送的具体数据。
3.将数据封装成数据包。
4.通过socket服务的发送功能,将数据包发出去。
5.关闭资源。
注意:创建DatagramSocket对象时如果没有明确端口,系统会自动分配一个未被使用的端口。
示例:通过udp传输方式,将一段文字数据发送出去。
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPSend {
public static void main(String[] args) throws Exception {
// 1.通过DatagramSocket,建立UDP的socket服务
DatagramSocket ds = new DatagramSocket(7777);
// 2.明确要发送的数据
byte[] data = "UDP发送端的数据。。。。。。。。。".getBytes();
// 3.将数据封装成数据包
DatagramPacket dp = new DatagramPacket(data, data.length,
InetAddress.getByName("192.168.1.255"), 9999);
// 4.用send方法将数据包发送出去
ds.send(dp);
// 5.关闭资源
ds.close();
}
}
UDP接收端
步骤:
1,建立udp的socket服务。
2,定义数据包,用于存储接收到数据。数据包对象中有更多功能可以提取字节数据中的不同数据信息。
3,通过socket服务的接收方法将收到的数据存储到已定义好的数据包中。
4,通过数据包对象的特有功能,将不同的数据取出来。如IP、端口、数据等。
5,关闭资源。
注意:通常要明确一个端口,作用在于只有发送到这个端口的数据才是这个接收端可以处理的数据。
示例:接收udp协议传输的数据并处理。
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPReceive {
public static void main(String[] args) throws Exception {
// 1.建立UDP的socket服务
DatagramSocket ds = new DatagramSocket(9999);
// 2.定义数据包,存储接收的数据
byte[] data = new byte[1024];
DatagramPacket dp = new DatagramPacket(data, data.length);
// 3。通过服务的receive方法将收到数据存入数据包中
ds.receive(dp);
// 4.通过数据包的方法获取其中的数据
String ip = dp.getAddress().getHostAddress();//IP地址
int port = dp.getPort();//接收端口号
String text = new String(dp.getData(),0,dp.getLength());//将字节数组中的有效部分转成字符串
System.out.println("IP:"+ip+"\n 端口号:"+port+"\n 发送端数据:"+text);
// 5.关闭资源
ds.close();
}
}
示例:编写一个聊天程序
import java.io.*;
import java.net.*;
class Send implements Runnable {
private DatagramSocket ds;
public Send(DatagramSocket ds) {
this.ds = ds;
}
public void run() {
try {
BufferedReader bufr = new BufferedReader(new InputStreamReader(
System.in));
String line = null;
while ((line = bufr.readLine()) != null) {
byte[] buf = line.getBytes();
DatagramPacket dp = new DatagramPacket(buf, buf.length,
InetAddress.getByName("192.168.1.255"), 10000);
ds.send(dp);
if ("886".equals(line))
break;
}
} catch (Exception e) {
throw new RuntimeException("发送端失败");
}
}
}
class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
this.ds = ds;
}
public void run() {
try {
while (true) {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
ds.receive(dp);
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(), 0, dp.getLength());
if ("886".equals(data)) {
System.out.println(ip + "....离开聊天室");
break;
}
System.out.println(ip + ":" + data);
}
} catch (Exception e) {
throw new RuntimeException("接收端失败");
}
}
}
class ChatDemo {
public static void main(String[] args) throws Exception {
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10000);
new Thread(new Send(sendSocket)).start();
new Thread(new Rece(receSocket)).start();
}
}
注意:发送端可以用默认端口,接收端要指定端口。
TCP(Transmission Control Protocol):传输控制协议
TCP特点:
1. 面向连接,需要建立连接,形成传输数据通道
2. 可以进行大数据量传输
3. 通过三次握手机制完成连接,是可靠协议
4. 效率较低
三次握手是什么?
如:1.我给你发信息“你在么”2.你回复“我在”3.我看到后又给你发“我知道了”。通过这三次我们就完成了TCP这个通道的建立。
TCP传输
如果两个端点的连接建立成功了,就说明通路建立了。通路一建立就有了一个socket流,也就是网络流(建立在网络基础上的流)。该流中既有输入流,也有输出流。
TCP的两个端点:
1.客户端 2.服务端。
客户端:Socket,此类实现客户端套接字。套接字是两台机器间通信的端点。
服务端: ServerSocket,此类实现服务器套接字。服务器套接字等待请求通过网络传入。
accept():侦听并接受到此套接字的连接。该方法是阻塞式方法,没有连接就会等。
shutdownOutput():禁用此套接字的输出流。
shutdownInput():此套接字的输入流置于“流的末尾”。
TCP客户端
步骤:
1.建立tcp客户端的socket服务,并指定要连接的主机和端口。
2,获取socket流中的输出流(getOutputStream)。将数据写到该流中。通过网络发送给服务端。
3,获取socket流中的输入流(getInputStream),将服务端反馈的数据获取到,并打印。
4.关闭资源。
示例:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
public class TcpClient
{
public static void main(String[] args)throws Exception
{
Socket s = new Socket("192.168.1.254",10004);
OutputStream out = s.getOutputStream();
out.write("服务端,你好".getBytes());
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
s.close();
}
}
TCP服务端
步骤:
1.建立tcp服务端的socket服务,并监听一个端口。
2.获取连接过来的客户端对象。
3.通过获取到的socket对象中socket流与具体的客户端进行通讯。
4.通讯结束,关闭资源。
注意:要先关客户端,再关服务端(一般服务端也可以不关)。
示例:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。
<span style="font-size:14px;">public class TcpServer2
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10004);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
InputStream in = s.getInputStream();//读取客户端的数据,使用客户端对象的socket读取流
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
OutputStream out = s.getOutputStream();
Thread.sleep(10000);
out.write("服务端收到,客户端你好".getBytes());
s.close();
ss.close();
}
}
</span>
代码示例
示例1:建立一个文本转换服务器。客户端给服务端发送文本,服务单会将文本转成大写在返回给客户端。而且客户度可以不断的进行文本转换。当客户端输入over时,转换结束。
步骤:
1,建立服务。
2,获取键盘录入。
3,将数据发给服务端。
4,后去服务端返回的大写数据。
5,结束,关闭资源。
因为都是文本数据,所以可以使用字符流进行操作,同时提高效率,加入缓冲技术。
客户端
<strong>import java.io.*;
import java.net.*;
class TransClient
{
public static void main(String[] args) throws Exception
{
Socket s = new Socket("192.168.1.254",10005);
//定义读取键盘数据的流对象。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//定义目的,将数据写入到socket输出流。发给服务端。
//BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(),true); //上面的简写
//定义一个socket读取流,读取服务端返回的大写信息。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
out.println(line);
// bufOut.write(line);
// bufOut.newLine();
// bufOut.flush();
String str =bufIn.readLine();
System.out.println("server:"+str);
}
bufr.close();
s.close();
}
}
</strong>
服务端
<strong>class TransServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10005);
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"....connected");
//读取socket读取流中的数据。
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(s.getInputStream()));
//目的。socket输出流。将大写数据写入到socket输出流,并发送给客户端。
//BufferedWriter bufOut = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
String line = null;
while((line=bufIn.readLine())!=null)
{
out.println(line.toUpperCase());
// bufOut.write(line.toUpperCase());
// bufOut.newLine();
// bufOut.flush();
}
s.close();
ss.close();
}
}
</strong>
这个示例中会出现的问题:
现象:客户端和服务端都在莫名的等待。
原因:因为客户端和服务端都有阻塞式方法。这些方法没有读到结束标记。那么就会导致两端都在等待。
示例2:多线程上传图片
客户端
import java.io.*;
import java.net.*;
class PicClient
{
public static void main(String[] args)throws Exception
{
if(args.length!=1)
{
System.out.println("请选择jpg格式的图片");
return ;
}
File file = new File(args[0]);
if(!(file.exists() && file.isFile()))
{
System.out.println("该文件有问题,不存在或不是文件");
return ;
}
if(!file.getName().endsWith(".jpg"))
{
System.out.println("图片格式错误,请重新选择");
return ;
}
if(file.length()>1024*1024*4)
{
System.out.println("文件过大");
return ;
}
Socket s = new Socket("127.0.0.1",10000);
FileInputStream fis = new FileInputStream(file);
OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
{
out.write(buf,0,len);
}
//告诉服务端数据已写完
s.shutdownOutput();
InputStream in = s.getInputStream();
byte[] bufIn = new byte[1024];
int num = in.read(bufIn);
System.out.println(new String(bufIn,0,num));
fis.close();
s.close();
}
}
服务端
class PicThread implements Runnable
{
private Socket s;
PicThread(Socket s)
{
this.s = s;
}
public void run()
{
int count = 1;
String ip = s.getInetAddress().getHostAddress();
try
{
System.out.println(ip+"....connected");
InputStream in = s.getInputStream();
File dir = new File("D:\\MyDrivers");
File file = new File(dir,ip+"("+(count)+")"+".jpg");
while(file.exists())
file = new File(dir,ip+"("+(count++)+")"+".jpg");
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while((len=in.read(buf))!=-1)
{
fos.write(buf,0,len);
}
OutputStream out = s.getOutputStream();
out.write("上传成功".getBytes());
fos.close();
s.close();
}
catch (Exception e)
{
throw new RuntimeException(ip+"上传失败");
}
}
}
class PicServer
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(10000);
while(true)
{
Socket s = ss.accept();
new Thread(new PicThread(s)).start();
}
//ss.close();
}
}
网络编程扩展
自定义服务器
客户端:浏览器 (telnet:windows中的远程登录命令)
服务端:自定义
import java.net.*;
import java.io.*;
class ServerDemo
{
public static void main(String[] args) throws Exception
{
ServerSocket ss = new ServerSocket(11000);
Socket s = ss.accept();
System.out.println(s.getInetAddress().getHostAddress());
//获取请求信息
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
PrintWriter out = new PrintWriter(s.getOutputStream(),true);
out.println("<font color='green' size='7'>客户端你好</font>");
s.close();
ss.close();
}
}
URL类
类URL代表一个统一资源定位符,它是指向互联网“资源”的指针。
String getFile():获取此 URL的文件名。
String getHost():获取此 URL的主机名(如果适用)。
String getPath():获取此 URL的路径部分。
int getPort() :获取此 URL 的端口号。
String getProtocol():获取此 URL的协议名称。
String getQuery():获取此 URL的查询部
openConnection():返回一个URLConnection对象,它表示到URL所引用的远程对象的连接。
openStream():打开到此URL的连接并返回一个用于从该连接读入的InputStream。它是openConnection().getInputStream();的缩写。
网络架构
C/S:Client/Server
客户端,服务端。
特点:
1,需要在客户端和服务端都需要按照编写的软件。
2,维护较麻烦。
好处:可以减轻服务端的压力,如网络游戏。
B/S:Browser/Server
浏览器,服务端。
特点:
1,客户端不用单独编写软件。因为客户端用的就是浏览器。
2,对于软件升级,只要考虑服务端即可。
弊端:所有的程序都运行在服务端,客户端的浏览器毕竟解析能力较弱,对游戏等。
总结:
UDP是面向无连接的,发的包是有限制的。TCP必须是面向连接的。
获取任意一台主机的IP地址时,有可能会因为无法解析,返回的主机名也是IP地址。我们在下载时要用TCP,因为这中间是不可以丢失数据的。
每个传输协议都有自己不同建立端点的方式。想要将主机名翻译成IP地址,需要域名解析。