java学习笔记————Socket网络编程小总结

对于网络编程其实就相当于,在生活中人与人之间的交流,提问的一方作为用户端,回答问题的一方作为服务器端

接下来我就和大家我学习socket网络编程所学到的知识

Tcp/Ip协议概述

Tcp/Ip协议的四层结构

在这里插入图片描述

每一层的用处

  • 运用层:应用层为用户提供所需要的各种服务,负责传送各种最终形态的数据,是直接与用户打交道的层,典型协议包含HTTP、FTP等

  • 传输层:传输层为应用层实体提供端到端的通信功能,该层定义了两个主要的协议:传输控制协议(TCP)和用户数据报协议(UDP)。其中,TCP协议提供的是一种可靠的、面向连接的数据传输服务;而UDP协议提供的是不可靠的、无连接的数据传输服务。

  • 网络层: 网络层主要解决主机到主机的通信问题。该层有四个主要协议:网络协议(IP)、地址解析协议(ARP)、互联网组管理协议(IGMP)和互联网控制报文协议(ICMP)。其中,IP协议是网络层最重要的协议。

  • 链路层:链路层负责建立电路连接,是整个网络的物理基础,典型的协议包括以太网、ADSL等。

进行网络编程需要解决的两个问题:

  • 如何在网络中找到一台或多台主机:

    可以依靠网络层的IP解决,即提供主机的IP地址找到主机。

  • 当通信双方成功连接后,如何进行可靠的数据传输:

    针对传输层进行编程,传输层主要的两个协议是TCP和UDP。

TCP和UDP的区别

  • TCP(Tranfer Control Protocol)

面向连接的,可靠的传输协议

  • UDP(User Datagram Protocol)

无连接的,不可靠的传输协议

UDP编程代码:
服务器端

package 网络编程.Day_04_22_Code;

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

public class UDPServer {
	
	public static void main(String[] args) throws IOException {
	
		// 创建数据包(数据报) socket对象,绑定端口 4722 用来通信(接收)
		DatagramSocket receive_socket = new DatagramSocket(4722);
		// 准备一个返回数据的缓冲区
		byte buf[] = new byte[1000];
		
		// 创建数据报(数据包),准备好接收 socket对象获取到的数据信息
		DatagramPacket receive_packet = new DatagramPacket(buf, buf.length);

		String str="我收到了消息";
		while ( true ) {
			
			// 使用 socket对象 接收 packet内容 放到服务器
			receive_socket.receive(receive_packet);
			
			// 获取主机IP
			String name = receive_packet.getAddress().toString();
			System.out.println(" 来自:" + name + "  端口:" + receive_packet.getPort());
			
			// 获取数据内容
			System.out.println(receive_packet.getLength());
			String data = new String(receive_packet.getData(),0,receive_packet.getLength());
			System.out.println(" 接收到的数据:" + data);
			buf=str.getBytes();
			data = new String(buf,0,buf.length);

			// 创建数据报(数据包),封装需要发送的数据内容(数据长度、服务器地址+端口...)
			DatagramPacket send_packet = new DatagramPacket(buf, buf.length,InetAddress.getByName("127.0.0.1"),4711);

			// 使用 socket对象 发送 packet内容 到服务器
			receive_socket.send(send_packet);

		}
	}
}

客户端

package 网络编程.Day_04_22_Code;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

public class UDPClient {

	public static void main(String[] args) throws IOException {
	
		// 创建数据包(数据报) socket对象,绑定端口 4711 用来通信(发送)
		DatagramSocket send_socket = new DatagramSocket(4711);

		// 准备好需要发送的数据
//		String msg = "qwerDF1234567";
		String msg = "我是客户端向服务端发送一条消息此处凑够二十个字";
		byte[] databyte = new byte[1000];
		databyte = msg.getBytes();
		String data = new String(databyte,0,databyte.length);
		System.out.println(data);
//		System.out.println(databyte.length);
		
		// 创建数据报(数据包),封装需要发送的数据内容(数据长度、服务器地址+端口...)
		DatagramPacket send_packet = new DatagramPacket(databyte, databyte.length,InetAddress.getByName("127.0.0.1"),4722);
		
		// 使用 socket对象 发送 packet内容 到服务器
		send_socket.send(send_packet);

		// 准备一个返回数据的缓冲区
		byte buf[] = new byte[1000];

		// 创建数据报(数据包),准备好接收 socket对象获取到的数据信息
		DatagramPacket receive_packet = new DatagramPacket(buf, buf.length);

		// 使用 socket对象 接收 packet内容 放到服务器
		send_socket.receive(receive_packet);

		// 获取主机IP
		String name = receive_packet.getAddress().toString();
		System.out.println(" 来自:" + name + "  端口:" + receive_packet.getPort());

		// 获取数据内容
		System.out.println(receive_packet.getLength());
		data = new String(receive_packet.getData(),0,receive_packet.getLength());
		System.out.println(" 接收到的数据:" + data);
		
	}
}

在控制台的结果
在这里插入图片描述
在这里插入图片描述

TCP编程代码:

package 网络编程.Day_04_22_Code;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;

import javax.net.ssl.HttpsURLConnection;

public class HttpURLConnectionTest {

	public static void main(String[] args) throws IOException {
		
		URL serverURL = new URL("https://www.baidu.com/");
		
		HttpsURLConnection con = (HttpsURLConnection)serverURL.openConnection();
		
		con.setDoOutput(true);
		
		OutputStream os = con.getOutputStream();
		
		int code = con.getResponseCode();
		
		if ( code == HttpsURLConnection.HTTP_OK) {
			
			InputStream is = con.getInputStream();
			
			FileOutputStream fos = new FileOutputStream("testbaidu.html");
			
			byte[] buf = new byte[1024];
			
			int c = 0;
			
			while ( ( c = is.read(buf,0,buf.length)) != -1 ) {
				
				fos.write(buf, 0, c);
			}
			
			fos.close();
			is.close();
		}
	}
}


最后会在根目录下生成一个文件
在这里插入图片描述

Socket 编程

Socket通常称作“套接字”,通常通过“套接字”向网络发出请求或者应答网络请求

套接字使用TCP提供了两台计算机之间的通信机制。 客户端程序创建一个套接字,并尝试连接服务器的套接字。
当连接建立时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。
java.net.Socket 类代表一个套接字,并且 java.net.ServerSocket 类为服务器程序提供了一种来监听客户端,并与他们建立连接的机制。
以下步骤在两台计算机之间使用套接字建立TCP连接时会出现:

  • 服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。

  • 服务器调用 ServerSocket 类的 accept() 方法,该方法将一直等待,直到客户端连接到服务器上给定的端口。

  • 服务器正在等待时,一个客户端实例化一个 Socket 对象,指定服务器名称和端口号来请求连接。

  • Socket 类的构造函数试图将客户端连接到指定的服务器和端口号。如果通信被建立,则在客户端创建一个 Socket 对象能够与服务器进行通信。

  • 在服务器端,accept() 方法返回服务器上一个新的 socket 引用,该 socket 连接到客户端的 socket。

连接建立后,通过使用 I/O 流在进行通信,每一个socket都有一个输出流和一个输入流,客户端的输出流连接到服务器端的输入流,而客户端的输入流连接到服务器端的输出流。
TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送.以下是一些类提供的一套完整的有用的方法来实现 socket。

ServerSocket 类的方法

服务器应用程序通过使用 java.net.ServerSocket 类以获取一个端口,并且侦听客户端请求。
ServerSocket 类有四个构造方法:

此处有图片

创建非绑定服务器套接字。 如果 ServerSocket 构造方法没有抛出异常,就意味着你的应用程序已经成功绑定到指定的端口,并且侦听客户端请求。
这里有一些 ServerSocket 类的常用方法:

此处有图片

Socket 类的方法

java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。客户端要获取一个 Socket 对象通过实例化 ,而 服务器获得一个 Socket 对象则通过 accept() 方法的返回值。

Socket 类有五个构造方法:

此处有图片

当 Socket 构造方法返回,并没有简单的实例化了一个 Socket 对象,它实际上会尝试连接到指定的服务器和端口。
下面列出了一些感兴趣的方法,注意客户端和服务器端都有一个 Socket 对象,所以无论客户端还是服务端都能够调用这些方法。

在这里插入图片描述

InetAddress 类的方法

这个类表示互联网协议(IP)地址。下面列出了 Socket 编程时比较有用的方法:

在这里插入图片描述

单客户Socket编程

服务器端:

package 网络编程.资料.single;

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

public class TCPServer {
	public static void main(String[] args) {
		try {
			ServerSocket server = null;
			try {
//				创建ServerSocket对象,指定端口是4700
				server = new ServerSocket(4700);
				System.out.println("服务器启动成功");
			} catch (Exception e) {
				System.out.println("服务器启动出错");
			}
			Socket socket = null;
			try {
//				调用ServerSocket的accept方法,可以接受客户端的请求,并返回当前的Socket对象
				socket = server.accept();
			} catch (Exception e) {
				e.printStackTrace();
			}
			String line;
//			获得基于Socket的输入流
			BufferedReader is = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));

//			获得基于Socket的输出流
			PrintWriter os = new PrintWriter(socket.getOutputStream());

//			获得控制台输入
			BufferedReader sin = new BufferedReader(new InputStreamReader(
					System.in));

//			输出客户端输入
			System.out.println("Client:" + is.readLine());

			line = sin.readLine();

//			向客户端写数据
			while (!line.equals("exit")) {
				os.println(line);
				os.flush();
				System.out.println("Client:" + is.readLine());
				line = sin.readLine();
			}

//			资源关闭操作
			os.close();
			is.close();
			socket.close();
			server.close();
		} catch (Exception e) {
			System.out.println("Error:" + e);
		}

	}
}

客户端:

package 网络编程.资料.single;

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

public class TCPClient {
	public static void main(String[] args) {
		try {
//			创建Socket,指定ip,port
			Socket socket = new Socket("127.0.0.1", 4700);

//			获得键盘输入
			BufferedReader sin = new BufferedReader(new InputStreamReader(
					System.in));

//			获得基于Socket的输入流和输出流
			PrintWriter os = new PrintWriter(socket.getOutputStream());
			BufferedReader is = new BufferedReader(new InputStreamReader(socket.getInputStream()));

			String readline;
			readline = sin.readLine();

//			向服务器写数据
			while (!readline.equals("exit")) {
				os.println(readline);
				os.flush();
				System.out.println("Server:" + is.readLine());
				readline = sin.readLine();
			}
			os.close();
			is.close();
			socket.close();
		} catch (Exception e) {
			System.out.println(e);
		}
	}

}

注意先启动服务器再启动客户端
在这里插入图片描述
在这里插入图片描述

这种单客户的交流也是有限制的,必须客户端一句,服务器一句,当然也可以改善,把while循环改改就可以

模拟多人群聊(这里服务器只是起到了一个中间转发的过程)

服务器端的代码:

MultiThreadServer的代码

//MultiThreadServer的代码
package 网络编程.Day_04_21_Code;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class MultiThreadServer implements Runnable {

	private Socket socket;

	//定义输出输入流
	DataInputStream is;
	DataOutputStream os;
	
	
	public MultiThreadServer( Socket s ) {

		this.socket = s;
	}
	
	
	@Override
	public void run() {

		String line;
		
		try {
//			获取socket对象的输入输出流
			is = new DataInputStream( socket.getInputStream() );
			os = new DataOutputStream( socket.getOutputStream() );
			
			while ( true ) {
				//将客户端发送到服务器的内容存到line中
				line = is.readUTF();

//				在服务器端打印出客socket的hashcode的值和line
				System.out.println(" 客户端 [ " + socket.hashCode() + " ]: " + line);

//				调用定义好的方法将内容转发给全部的客户端
				TCPServer_02.sendtoAll(" 客户端 [ " + socket.hashCode() + " ]: " + line);
				if ( line.equals("exit") ) {
					
					break;
				}
			}
			
		} catch (Exception e) {

			e.printStackTrace();
		}
	}
}

TCPServer_02的代码

//TCPServer_02的代码
package 网络编程.Day_04_21_Code;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class TCPServer_02 {

	//定义一个静态的泛型为MultiThreadServer的线程安全的arraylist
	static List<MultiThreadServer> clients = Collections.synchronizedList( new ArrayList<MultiThreadServer>() );

	//定义向全部的客户转发消息的方法
	static void sendtoAll( String msg ) {

		//便利所哟逇服务
		for ( int i = 0; i < clients.size(); i++ ) {
			
			MultiThreadServer server = clients.get(i);
			
			try {
				//将转发的消息转发
				server.os.writeUTF(msg);
				
			} catch (Exception e) {

				e.printStackTrace();
			}
		}
	}
	
	
	public static void main(String[] args) throws Exception {
		//创建ServerSocket对象
		ServerSocket server_socket = null;
		
		try {
			//指定端口
			server_socket = new ServerSocket(5700);
			System.out.println(" 服务器启动成功!!!");
			
		} catch (Exception e) {
			
			System.out.println(" 服务器启动失败!!!");
		}
		
		Socket socket = null;
		
		while ( true ) {
			//创建socket 实例
			socket = server_socket.accept();
			//创建一个 MultiThreadServer类,将本次的socket作为构造参数传入
			MultiThreadServer st = new MultiThreadServer(socket);
//			创建一个线程
			Thread t = new Thread(st);
//			将该服务存放到线程安全的arraylist中
			clients.add(st);
//			启动线程
			t.start();
		}
	}
}


客户端的代码:

package 网络编程.Day_04_21_Code;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

public class TCPClient_02 {

	public static void main(String[] args) throws UnknownHostException, IOException {
		//创建 Socket 关键字,确定 IP 和 端口号
		Socket socket = new Socket("127.0.0.1", 5700);
		//获取键盘输入
		BufferedReader sin = new BufferedReader( new InputStreamReader( System.in ) );

		//定义socket的输出和输出流
		DataOutputStream os = new DataOutputStream( socket.getOutputStream());
		
		final DataInputStream is = new DataInputStream( socket.getInputStream());
//		定义字符串
		String readline;
		//用runnable接口创建线程
		Runnable runnable = new Runnable() {
			
			@Override
			public void run() {

				while ( true ) {
					
					try {
						//将服务器转发的信息赋值给 readline
						String readline = is.readUTF();
						//输出转发的信息
						System.out.println(readline);
						
						if ( readline.equals("exit")) {
							break;
						}
						
					} catch (Exception e) {
						
						e.printStackTrace();
					}
				}
			}
		};
		//启动线程
		new Thread(runnable).start();
		
		while ( true ) {
			//将键盘的输入赋值给readline
			readline = sin.readLine();
			// 向服务端发送数据
			os.writeUTF(readline);
		}
		
	}
}


我启动了一个服务器端,三个客户端
在这里插入图片描述
客户端1:
在这里插入图片描述
客户端2
在这里插入图片描述
客户端3
在这里插入图片描述

原创文章 73 获赞 64 访问量 2714

猜你喜欢

转载自blog.csdn.net/qq_42147171/article/details/105658905
今日推荐