java 基础之网络编程 --09

本文介绍了网络编程的基础知识,包括网络模型、TCP/IP参考模型、网络通讯要素等。详细讲解了UDP和TCP两种主要的传输协议的特点及应用场景,并通过示例演示了如何使用Socket进行UDP和TCP编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


20 网络编程

20.1 网络编程介绍

网络模型:  Osi 参数模型
TCP/IP 参考模型
网络通讯要素
IP地址
端口号
传输协议

通讯规则:
1.找到对方IP 
2.数据要发送到对方指定的应用程序上。为了标识这些应用程序,所以给这些网络应用程序都用
数字进行标识。为了方便称呼这数字,叫做端口。逻辑端口
3.定义通讯规则。这通讯规则称为协议  国际定义了 通用协议 TCP/IP

20.2 网络参考模型

网际层 IP 协议
传输层 TCP/UDP 协议 
应用层 http /ftp 都属于应用层协议

20.3 网络通讯要素

1.IP地址:InetAddress
  网络中设备的标识
  不易记忆,可用主机名
  本地回环地址:127.0.0.1  主机名:localhost
2.端口号
  用于标识进程的逻辑地址,不同进程的标识
  有效端口:0~65535,其中0~1024系统使用或保留端口。
3.传输协议
  通讯的规则
  常见协议:TCP,UDP

20.4 TCP和UDP协议详解

20.4.1 UDP
网络视频,桌面共享 一般用的都是不需要保证数据的完整性 UDP协议
1)将数据及源和目的封装成数据包中,不需要建立连接
2)每个数据报的大小在限制在64k内
3)因无连接,是不可靠协议
4)不需要建立连接,速度快
20.4.2 TCP
下载 数据显示 需要保证数据的完整性的都是用的TCP协议
1)建立连接,形成传输数据的通道。
2)在连接中进行大数据量传输
3)通过三次握手完成连接,是可靠协议
4)必须建立连接,效率会稍低

20.5 Socket编程

也称为套接字
Socket就是为网络服务提供的一种机制。
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。
20.5.1 UDP传输
传输步骤:
1)DatagramSocket与DatagramPacket
udp需要将信息封装成包再发送出去 类-- DatagramPacket
DatagramSocket(int port) 
          创建数据报套接字并将其绑定到本地主机上的指定端口。 
DatagramSocket(int port, InetAddress laddr) 
          创建数据报套接字,将其绑定到指定的本地地址。 
DatagramPacket(byte[] buf, int length) 
          构造 DatagramPacket,用来接收长度为 length 的数据包。 
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 
          构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。 
2)建立发送端,接收端。
3)建立数据包。
4)调用Socket的发送接收方法。
5)关闭Socket。
简单示例:
public static void main(String[] args) throws Exception {
		byte[] b ="fuck bitch the socket".getBytes();
		byte[] bs =new byte[1024];
		//发送socket
		DatagramSocket socket = new DatagramSocket(9999,InetAddress.getLocalHost());
		//接收socket2
		DatagramSocket socket2 = new DatagramSocket(10000,InetAddress.getLocalHost());
		//发送的数据包
		DatagramPacket dp = new DatagramPacket(b,b.length);
		//接收数据包
		DatagramPacket dp2 = new DatagramPacket(bs,bs.length);
		dp.setAddress( InetAddress.getLocalHost());
		dp.setPort(10000);
		
		socket.send(dp);
		socket2.receive(dp2);
		//获取接收的数据
		System.out.println(new String(dp2.getData(),0,b.length));
		socket.close();
		socket2.close();
	}

UDP 聊天程序
要求 1.通过键盘录入获取要发送的信息。
     2.将发送和接收分别封装到两个线程中。

package socket;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class SocketIm {
	String username; //用户名
	DatagramSocket ds;
	public SocketIm(String username,int port,InetAddress laddr) throws SocketException{
		ds = new  DatagramSocket(port, laddr);
		this.username = username;
	}
	/**
	 * 用户登录 并且开始聊天
	 * @param port  目标端口号
	 * @param laddr 目标IP地址
	 * @throws SocketException
	 */
	public void shangxian(int port,InetAddress laddr) throws SocketException{
		new Send(this,port,laddr).start();
		new Reseve(this).start();
	}
	public String getUsername() {
		return username;
	}
	public DatagramSocket getDs() {
		return ds;
	}
	public static void main(String[] args) {
		try {
			new SocketIm("li", 9999, InetAddress.getLocalHost()).shangxian(10000, InetAddress.getLocalHost());
			new SocketIm("小米", 10000, InetAddress.getLocalHost()).shangxian(9999, InetAddress.getLocalHost());
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
class Send extends Thread{
	SocketIm ds;
	int port;
	InetAddress laddr;
	/**
	 * 发送
	 * @param ds
	 */
	public Send(SocketIm ds,int port,InetAddress laddr){
		this.ds = ds;
		this.port = port;
		this.laddr = laddr;
	}
	@Override
	public void run() {


		BufferedReader br= new BufferedReader(new InputStreamReader(System.in));
		DatagramPacket dp = null ;
		while(true){
			byte[] b = null;
			try {
				b = (ds.getUsername()+" 对你说 : "+br.readLine()).getBytes();
				dp = new DatagramPacket(b,b.length,laddr,port);
				ds.getDs().send(dp);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
class Reseve extends Thread{
	SocketIm ds;
	/**
	 * 发送
	 * @param ds
	 */
	public Reseve(SocketIm ds){
		this.ds = ds;
	}
	@Override
	public void run() {
		byte[] b = new byte[1024*1024];
		DatagramPacket dp2 = new DatagramPacket(b,b.length);
		while(true){
			try {
				ds.getDs().receive(dp2);
				System.out.println(new String(dp2.getData(),0,dp2.getLength()));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

20.6.1 TCP详解
1.连接步骤:
1)Socket和ServerSocket
Socket(InetAddress address, int port) 
          创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(String host, int port) 
          创建一个流套接字并将其连接到指定主机上的指定端口号。
 InputStream getInputStream() 返回此套接字的输入流。 
 OutputStream getOutputStream() 返回此套接字的输出流。 


2)建立客户端和服务器端
3)建立连接后,通过Socket中的IO流进行数据的传输
4)关闭socket
5)同样,客户端与服务器端是两个独立的应用程序。
2.客户端的基本思路:
客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经
提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。
与服务端通讯结束后,关闭Socket。
3.服务端的基本思路:
服务端需要明确它要处理的数据是从哪个端口进入的。
当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。
当该客户端访问结束,关闭该客户端。


注意事项:
记得标记该socket结束 
在读取文件的时候 有两种方式可以结束读取文件
1.自己写自定义的结束约定
2.使用socket本身自带的结束约定方法 
    void shutdownInput()  此套接字的输入流置于“流的末尾”。 
    void shutdownOutput() 禁用此套接字的输出流。 
示例:简易聊天室
//============客户端实现===================
package socket;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * 客户端
 * @author 李昂志
 */
public class ClinetTest {
	String name ;
	public Socket s;
	public ClinetTest(String name){
		this.name = name;
	}
	public String getName(){
		return name;
	}
	public void startClient(){
		try { 
			s = new Socket("localhost", 10000);
			 new Thread(new SendMsg(this)).start();
			 new Thread(new ReserveMsg(this)).start();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		new ClinetTest("Big").startClient();
	}
}
/**
 * 客户端发送信息
 * @author 李昂志
 */
class SendMsg implements Runnable{
	ClinetTest sk;
	Socket s;
	public SendMsg(ClinetTest clinetTest){
		this.sk = clinetTest;
		s = sk.s;
	}


	@Override
	public void run() {
		// TODO Auto-generated method stub
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out = null;
		try {
			out = new PrintWriter(s.getOutputStream(),true);
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		while(true){
			try {
				out.println(sk.name+" 说:"+br.readLine());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
/**
 * 客户端接收信息
 * @author 李昂志
 */
class ReserveMsg implements Runnable{
	ClinetTest sk;
	Socket s;
	public ReserveMsg(ClinetTest clinetTest){
		this.sk = clinetTest ;
		s = sk.s;
	}
	@Override
	public void run() {
		BufferedReader in = null ;
		try {
			in = new BufferedReader(new InputStreamReader(s.getInputStream()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		while(true){
			try {
				System.out.println(in.readLine());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}


//============服务端实现===================
package socket;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
 * 服务端
 * @author 李昂志
 */
public class ServerTest {
	public static  List<Socket> ls = new ArrayList<Socket>();
	public ServerTest(){
	}
	public void startServer(){
		ServerSocket ss = null;
		Socket s = null;
		
			try {
				ss = new ServerSocket(10000);
				 System.out.println("服务器已经启动.....");
			
			 while(true){
				try {
					 s = ss.accept();
					 System.out.println("监听到一个客户端开启");
					 ls.add(s);
					 new Thread(new SrevMsg(s)).start();
				 } catch (Exception e) {
						// TODO Auto-generated catch block
					ls.remove(s);
					s.close();
				}
			 }
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		
	}
	public static void main(String[] args) {
		new ServerTest().startServer();
	}
}


/**
 * 接收到一个用户的信息则马上转发给所有在线的用户
 * @author 李昂志
 */
class SrevMsg implements Runnable{
	String msg ;
	Socket s ;
	public SrevMsg(Socket s){
		this.s = s;
	}
	@Override
	public void run() {
		BufferedReader br = null;
		
		try {
			br=  new BufferedReader(new InputStreamReader(s.getInputStream()));
			while(true){
				if(!s.isClosed()){
					msg = br.readLine();
					sendMsg(msg);
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			ServerTest.ls.remove(s);
			sendMsg(s.getInetAddress().getHostName()+" 用户退出聊天室!");
			try {
				if(s!= null){
					s.close();
				}
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
	}
	public  void sendMsg(String msg){
		PrintWriter out = null;
		for(Socket ss : ServerTest.ls  ){
			if(s == ss) continue;
			if(ss.isConnected()){
				try {
					out = new PrintWriter(ss.getOutputStream(),true);
					out.println(msg);
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} 
			}else{
				ServerTest.ls.remove(ss);
			}
		}
	}
}

20.6.2 Tcp传输问题
客户端连接上服务端,两端都在等待,没有任何数据传输。
通过例程分析:
因为read方法或者readLine方法是阻塞式。
解决办法:
自定义结束标记
使用shutdownInput,shutdownOutput方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值