【Java】网络编程基础

 

一、网络编程的概述:

计算机网络:

是指将多台位置不同的具有独立功能的计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,

实现资源共享和信息传递的计算机系统。

网络编程:

就是用来实现网络互连的不同计算机软件间进行数据交换。

 

网络模型:

OSI(开放系统互连)参考模型

TCP/IP参考模型

程人员的工作是传输层和网际层,用户在应用层操作的数据,经过逐层封包,最后到物理层发送到另一个模型中,再进行逐层解包,

图示为:

OSI参考模型虽然设计精细,但过于麻烦,效率不高,因此才产生了简化版的TCP/IP参考模型。

 

二、网络通信的要素:

1.IP地址:

它是网络中的设备标识,

不易记忆,可用主机名表示,两者存在映射关系

本机回环地址:127.0.0.1,主机名为:localhost。

ps.

InetAddressip = InetAddress.getLocalHost();:本地地址获取

static InetAddress getByName(String host):获取指定主机的IP和主机名。(最好用ip地址去获取,主机名需要解析)

String getHostAddress():返回IP地址字符串文本形式,以IP地址为主

String getHostName():返回IP地址主机名。

InetAddress类中有一个静态方法:static InetAddress[] getAllByName(String host),此方法是在给定主机名的情况下

根据系统上配置的名称服务返回其IP地址所组成的数据。这是由于有些主机名对应的IP地址不唯一,如新浪、百度,

都是服务器集群。

 

2.端口号:

用于标识进程(应用程序)的逻辑地址,不同进程的标识。

有效端口:0~65535,其中0~1024系统使用或保留端口。

ps.

所谓防火墙,其功能就是将发送到某程序端口的数据屏蔽掉以及将从该程序端口发出的数据也屏蔽掉。

 

3.传输协议:

通讯的规则,常见协议:UDP、TCP。

UDP:

将数据及源和目的封装成数据包中,不需要建立连接。

每个数据报的大小在限制在64k内。

因无连接,是不可靠协议。

不需要建立连接,速度快。

应用案例:QQ、FeiQ聊天、在线视频用的都是UDP传输协议。

 

 

TCP:

建立连接,形成传输数据的通道。

在连接中进行大数据量传输。

通过三次握手完成连接,是可靠协议。

(第一次本方发送请求,第二次对方确认连接,第三次本方再次确认连接成功)

必须建立连接,效率会稍低。

 

三、传输协议:

 

Socket

Socket就是为网络服务提供的一种机制。

通信的两端都有Socket。

网络通信其实就是Socket间的通信。

数据在两个Socket间通过IO传输。

 

UDP传输:

DatagramSocket(用来发送和接收数据报包的套接字)与DatagramPacket(数据报包)。

建立发送端,接收端。

建立数据包。

调用Socket的发送接收方法。

关闭Socket。

发送端与接收端是两个独立的运行程序。

UDP发送端

 

package exam;

import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;

public class UDPSendDemo
{
        public static void main(String[] args) throws Exception {

                System.out.println("发送端启动......");
                /*
                * 创建UDP传输的发送端。
                * 思路:
                * 1. 建立udp的socket服务。
                * 2. 将要发送的数据封装到数据包中。
                * 3. 通过udp的socket服务将数据包发送出去。
                * 4. 关闭socket服务。
                */

                //1. udpsocket服务。使用DatagramSocket对象。
                //如果发送端端口未指定,就会随机分配未被使用的端口。
                DatagramSocket ds = new DatagramSocket(8888);

                //2. 将要发送的数据封装到数据包中。
                String str = "UDP发送数据";

                //使用DatagramPacket将数据封装到该对象包中。
                byte[] buf = str.getBytes();

                DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.102"),10000);
        
                //3. 通过udp的socket服务将数据包发送出去,使用send方法。
                ds.send(dp);

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

 

 

UDP接收端:

package exam;

import java.net.DatagramSocket;
import java.net.DatagramPacket;
import java.net.InetAddress;

public class UDPReceDemo
{
        public static void main(String[] args) throws Exception {

                System.out.println("接收端启动......");
                /*
                * 建立UDP接收端的思路。
                * 思路:
                * 1. 建立udp的socket服务,因为是要接收数据,必须要明确一个端口号。
                * 2. 创建数据包,用于存储接收到的数据,方便用数据包对象的方法解析这些数据。
                * 3. 使用socket服务的receive方法将接收的数据存储到数据包中。
                * 4. 通过数据包的方法解析数据包中的数据。
                * 5. 关闭资源。
                */

                //1. 建立udpsocket服务。
                DatagramSocket ds = new DatagramSocket(10000);

                //2. 创建数据包。
                byte[] buf = new byte[1024];
                DatagramPacket dp = new DatagramPacket(buf,buf.length);

                //3. 使用接收方法将数据存储到数据包中。
                ds.receive(dp);//阻塞式的。

                //4. 通过数据包对象的方法,解析其中的数据,比如:地址,端口,数据内容。
                String ip = dp.getAddress().getHostAddress();
                //获取的端口号是发送端的端口号。
                int port = dp.getPort();
                String text = new String(dp.getData(),0,dp.getLength());

                System.out.println(ip + ":" + port + ":" + text);

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


案例演示:

聊天程序(单窗口模式-群聊):

发送端:

package chatDemo;

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

public class SendDemo implements Runnable{
	private DatagramSocket ds;

	public SendDemo(DatagramSocket ds) {
		super();
		this.ds = ds;
	}

	@Override
	public void run() {
		// TODO 自动生成的方法存根
			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.0.102"),10000);
					ds.send(dp);
					if("voer".equals(line)){
						break;
					}
				}
				ds.close();
			} catch (IOException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}

	}
	
	
}

 

接收端:

 

 

package chatDemo;

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

public class ReceDemo implements Runnable{
	private DatagramSocket ds;

	public ReceDemo(DatagramSocket ds) {
		super();
		this.ds = ds;
	}

	@Override
	public void run() {
		// TODO 自动生成的方法存根
		try{
			while(true){
				
				byte[] buf = new byte[1024];
				DatagramPacket dp = new DatagramPacket(buf,0,buf.length);
				
				ds.receive(dp);
				String ip = dp.getAddress().getHostName();
				int port = dp.getPort();
				String text = new String(dp.getData(),0,dp.getLength());
				System.out.println(ip+":"+port+"::"+text);
				
				if(text.equals("over")){
                    System.out.println(ip + "...退出聊天室");
            }
			}
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
	}
	
}

 

测试结果:

 

 

package chatDemo;

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

public class ChatDemo {

	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		DatagramSocket send = new DatagramSocket();
		DatagramSocket reve = new DatagramSocket(10000);
		
		SendDemo sd = new SendDemo(send);
		ReceDemo re = new ReceDemo(reve);
		
		new Thread(sd).start();
		new Thread(re).start();
		
	}

}

 

 

 

 

 

 

 

 

TCP传输:

客户端(Client)首先与服务端(Server)建立连接,形成通道(其实就是IO流),然后,数据就可以在通道之间进行传输,并且单个Server端可以同时与多个Client端建立连接。

基本思路:

1、创建客户端对象

Socket():创建空参数的客户端对象,一般用于服务端接收数据

 

Socket(String host,int port),指定要接收的IP地址和端口号

2.创建服务端对象:ServerSocket(int port):指定接收的客户端的端口

3.Socket accept():监听并接受到此套接字的连接

4.InputStream getInputStream():返回此套接字的输入流,Socket对象调用

5.OutputStream getOutputStream():返回套接字的输出流,Socket对象调用

 

 

重点:

1.客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。

2.与服务端通讯结束后,关闭Socket。

3.服务端需要明确它要处理的数据是从哪个端口进入的。

4.当该客户端访问结束,关闭该客户端。(一般的大型服务器都是不关闭的)

 

案例演示1:

TCP客户端

 

<span style="font-size:14px;">import java.net.Socket;
import java.io.OutputStream;
import java.io.IOException;
import java.net.UnknownHostException;

public class ClientDemo
{
        public static void main(String[] args) throws UnknownHostException,IOException {

                //客户端发数据到服务端
                /*
                * TCP传输,客户端建立的过程。
                * 1. 创建TCP客户端Socket服务,使用的是Socket对象。
                *     建议该对象一创建就明确目的地,要连接的主机。
                * 2. 如果连接建立成功,说明数据传输通道已建立。
                *     该通道就是socket流,是底层建立好的。既然是流,说明这里既有输入,又有输出。
                *     想要输入或者输出流对象,可以找Socket来获取。
                *     可以通过getOutputStream(),和getInputStream()来获取两个字节流。
                * 3. 使用输出流,将数据写出。
                * 4. 关闭资源。
                */

                //创建客户端socket服务。
                Socket socket = new Socket("192.168.0.102",10001);
        
                //获取socket流中的输出流
                OutputStream out = socket.getOutputStream();

                //使用输出流将指定的数据写出去。
                out.write("tcp输出你老母".getBytes());

                //断开链接,关闭资源,socket获取的输出流也被关闭,没有必要再写代码关闭。
                socket.close();
        }
}

 

 

 

TCP服务端

 

 

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

public class ServerDemo{
        
        public static void main(String[] args) throws IOException {

                //服务端接收客户端发送过来的数据,并打印到控制台上。
               /*
                * 建立tcp服务端的思路:
                * 1. 创建服务端socket服务,通过ServerSocket对象。
                * 2. 服务端必须对外提供一个端口,否则客户端无法连接。
                * 3. 获取连接过来的客户端对象。
                * 4. 通过客户端对象获取socket流读取客户端发来的数据,
                *     并打印在控制台上。
                * 5. 关闭资源,关客户端,关服务端。
                */

                //1. 创建服务端对象
                ServerSocket ss = new ServerSocket(10002);

                //2. 获取连接过来的客户端对象。
                Socket s = ss.accept();//阻塞式
                String ip = s.getInetAddress().getHostAddress();

                //3. 通过socket对象获取输入流,要读取客户端发来的数据。
                InputStream in = s.getInputStream();

                byte[] buf = new byte[1024];

                int len = in.read(buf);
                String text = new String(buf,0,len);
                System.out.println(ip + ":" + text);

                s.close();
                ss.close();
        }
}

 

 

案例演示2:

练习:文本转换TCP客户端和服务端

客户端:

 

import java.net.Socket;
import java.io.OutputStream;
import java.io.IOException;
import java.net.UnknownHostException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class TransClient
{
        public static void main(String[] args) throws UnknownHostException,IOException {
                
                /*
                * 思路:
                * 客户端:
                * 1. 需要先有socket端点。
                * 2. 客户端的数据源:键盘。
                * 3. 客户端的目的:socket。
                * 4. 接收服务端的数据,源:socket。
                * 5. 将数据显示再打印出来。目的:控制台。
                * 6. 在这些流中操作的数据,都是文本数据。
                 *
                * 转换客户端:
                * 1. 创建Socket客户端对象。
                * 2. 获取键盘录入。
                * 3. 将录入的信息发送给socket输出流。
                */

                //1. 创建socket客户端对象。
                Socket s = new Socket("192.168.0,102",10001);

                //2. 获取键盘录入
                BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

                //3. socket输出流
                //new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                PrintWriter out = new PrintWriter(s.getOutputStream(),true);

                //4. 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);

                        //读取服务端发回的一行大写数据。
                        String upperStr = bufIn.readLine();
                
                        System.out.println(upperStr);
                }

                s.close();
        }
}


服务端

 

 

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

public class TransServer
{
        public static void main(String[] args) throws IOException {
                /*
                *
                * 转换服务器。
                * 分析:
                * 1. serversocket服务。
                * 2. 获取socket对象。
                * 3. 源:socket,读取客户端发过来需要转换的数据。
                * 4. 目的:显示在控制台上。
                * 5. 将数据转换成大写发给客户端。
                */

                //1. 创建ServerSocket。
                ServerSocket ss = new ServerSocket(10001);

                //2. 获取socket对象。
                Socket s = ss.accept();

                //获取ip。
                String ip = s.getInetAddress().getHostAddress();
                System.out.println(ip + "......connected");

                //3. 获取socket读取流,并装饰。
                BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
        
                //4. 获取socket的输出流,并装饰。
                PrintWriter out = new PrintWriter(s.getOutputStream(),true);
        
                String line = null;

                while((line = bufIn.readLine()) != null){
                                System.out.println(line);
                                out.println(line.toUpperCase());
                }

                s.close();
                ss.close();
        }
}


注意事项:

 

1.客户端的socket关闭后,服务端获取的客户端socket读取流也关闭了,因此读取不到数据,也随之关闭。

2.客户端和服务端的PrintWriter对象out获取到数据后,一定要刷新,否则对方(服务端或客户端)就获取不到数据,程序便无法正常执行。

3.上述刷新操作通过PrintWriter类的println()方法实现,也可以通过PrintWriter类的flush()方法实现。但是,由于获取数据的方法是BufferedReader对象bufIn的readLine()方法(阻塞式方法),此方法只有遇到“\r\n”标记时,才认为数据读取完毕,赋值给String对象line。所以,使用PrintWriter类的flush()方法刷新数据时一定要记得追加“\r\n”!

 

美好的一天!

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值