草庐IT

TCP和UDP网络编程

专治八阿哥的孟老师 2023-04-21 原文

TCP和UDP协议是TCP/IP协议的核心。 TCP 传输协议:TCP 协议是一TCP (Transmission Control
Protocol)和UDP(User Datagram
Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送;而UDP则不为IP提供可靠性、流控或差错恢复功能。一般来说,TCP对应的是可靠性要求高的应用,而UDP对应的则是可靠性要求低、传输经济的应用

1. 基于TCP的网络编程

IP是一个通信实体在网络上的地址,通信实体可以是打印机、计算机等。IP协议的作用是让Internet成为一个允许连接不同类型计算机和不同操作系统的网络。IP协议负责将消息从一个主机传到另一个主机,消息在传递过程中被分割成小包。但是IP协议不能解决数据包在传输过程中遇到的问题,所以计算机需要TCP协议来保证传输的可靠性。
TCP叫端对端协议,当一台计算机要与另一台远程计算机进行通信时,TCP在两台计算机之间建立一个连接,用于发送和接收数据。TCP协议负责将数据包按一定顺序放好并发送,接收端在按照正确的顺序排列数据包。TCP提供了一个重发机制,当一个通信实体A发送消息给通信实体B后,A需要收到B的确认信息,如果没有收到B的确认信息,A会重新发送信息。
凡是在Internet上的计算机都必须安装IP协议和TCP协议,这两个协议统称TCP/IP协议。 TCP的三次握手
a.客户端向服务端发送一个请求 b.服务端收到请求后,回客户端一个响应 c.客户端向收到服务端的响应后,回服务端一个确认信息

1.1 ServerSocket创建服务端

ServerSocket用于监听来自客户端的Socket连接。如果没有连接,它将处于等待状态。 ServerSocket中常用方法:
Socket accept():如果接收到一个客户端Socket连接请求,该方法返回一个客户端对应的Socket对象。
ServerSocket(int port):用户指定端口port来创建一个ServerSocket,端口在1~65535之间。
close():用于关闭服务端。

public class Server {
	public static void main(String[] args) {
		// 1.实例化一个ServerSocket的对象
		ServerSocket serverSocket = null;
		OutputStream output = null;// 用于向别人发消息
		InputStream input = null;// 用于读取别人发过来的消息
		try {
			serverSocket = new ServerSocket(8888);
			System.out.println("等待服务端的连接...");
			// 2.监听,获取连接到的客户端 注意:在未连接成功之前,将一直处于阻塞状态
			Socket socket = serverSocket.accept();
			System.out.println("连接成功");
			// 3.获取网络到内存的一个输入流
			input = socket.getInputStream();
			// 4.读取数据,这个数据是Client发过来的
			byte[] arr = new byte[1024];
			int len = input.read(arr);
			String message = new String(arr, 0, len);
			// 5.组织信息
			String ipString = socket.getInetAddress().getHostAddress();
			int port = socket.getPort();
			System.out.println(ipString + ":" + port + "说:" + message);
			// 6.服务端给客户端回复消息
			output = socket.getOutputStream();
			output.write("你也好,好久不见了".getBytes());
			output.flush();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				output.close();
				input.close();
				serverSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

服务端不应该只接受一个客户端请求后就停止,所以在程序中可以通过循环不断的调用accept()方法:

while(true){
Socket socket=serverSocket.accept();
}

1.2 Socket创建客户端

客户端可以使用Socket构造器连接指定的服务端。

public class Client {
	public static void main(String[] args) {
		Socket socket = null;// 1.建立一个与服务端之间的连接
		OutputStream output = null;// 用于向别人发消息
		InputStream input = null;// 用于读取别人发过来的消息
		try {
			socket = new Socket("127.0.0.1", 8888);
			// 2.将需要发送的数据写入到网络中,注意:包含了两个流:InputStream和OutputStream
			output = socket.getOutputStream();
			output.write("hello你好吗?".getBytes());// 3.写入
			output.flush();
			// 4.收取服务端发送来的消息 new BufferedInputStream(socket.getInputStream());
			input = socket.getInputStream();
			byte[] arr = new byte[1024];
			int len = input.read(arr);
			String message = new String(arr, 0, len);
			System.out.println("来自服务端的回复:" + message);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				input.close();
				output.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

1.3 加入多线程

使用多线程实现客户端和服务端持续会话:

public class ServerThread extends Thread {
	private Socket client;

	public ServerThread() {
	}

	public ServerThread(Socket client) {
		this.client = client;
	}

	@Override
	public void run() {
		OutputStream output = null;
		InputStream input = null;
		try {
			output = client.getOutputStream();
			input = client.getInputStream();
			// 从控制台进行获取数据
			Scanner scanner = new Scanner(System.in);
			// 进行循环的发送和接收消息
			while (true) {
				byte[] arr = new byte[1024];// 接收客户端发送来的消息
				int len = input.read(arr);
				String message = new String(arr, 0, len);
				System.out.println("来自客户端的消息:" + message);
				System.out.println("服务端对客户端说:");
				String reply = scanner.nextLine();// 回复消息
				output.write(reply.getBytes());
				output.flush();
				if (message.equals("886") || message.equals("bye") || message.equals("再见")) {// 约定
					break;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				input.close();
				output.close();
				client.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
public class Server {
	public static void main(String[] args) {
		ServerSocket serverSocket = null;
		try {
			serverSocket = new ServerSocket(8888);
			while (true) {
				Socket socket = serverSocket.accept();
				ServerThread thread = new ServerThread(socket);
				thread.start();		
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				serverSocket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
public class Client {
	public static void main(String[] args) {
		Socket socket = null;// 1.建立一个与服务端之间的连接
		OutputStream output = null;// 用于向别人发消息
		InputStream input = null;// 用于读取别人发过来的消息
		Scanner scanner = new Scanner(System.in);
		try {
			socket = new Socket("127.0.0.1", 8888);
			output = socket.getOutputStream();
			input = socket.getInputStream();
			while (true) {
				System.out.println("客户端对服务端说:");
				String message = scanner.nextLine();
				output.write(message.getBytes());// 进行发送
				output.flush();
				byte[] arr = new byte[1024];// 接收消息
				int len = input.read(arr);
				String reply = new String(arr, 0, len);
				System.out.println("收到服务端的反馈:" + reply);
				if (reply.equals("886") || reply.equals("bye") || reply.equals("再见")) {// 约定
					break;
				}
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				input.close();
				output.close();
				socket.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

2. 基于UDP的编程

UDP是User Datagram
Protocol的简称,用户数据报协议,它不管对方是否在线,直接向对方发送数据,至于对方能否收到,UDP无法控制,所以它是一种简单不可靠的协议。适用于一次传输量较少,对可靠性要求不高的情景。

特点:
a.不安全
b.无连接
c.效率高
d.UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内

public class Receiver {
	public static void main(String[] args) {
		// 1.实例化DatagramSocket的对象,需要进行绑定端口号:由发送方发送来的端口号进行决定
		DatagramSocket socket = null;
		try {
			socket = new DatagramSocket(6666);
			// 2.将接收到的数据封装到数据报包中,用来接收长度为 length 的数据包。
			byte[] arr = new byte[4096];
			DatagramPacket packet = new DatagramPacket(arr, arr.length);
			System.out.println("等待接收数据~~~~~~~");
			// 3.接收数据
			// 注意:将数据从网络中读取出来
			socket.receive(packet);
			// 4.获取发送方的详细信息
			byte[] messages = packet.getData();
			String result = new String(messages, 0, packet.getLength());
			// 获取发送方的ip地址
			// 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
			InetAddress address = packet.getAddress();
			String ip = address.getHostAddress();
			// 获取消息是从发送发的哪个端口号发出来的
			int port = packet.getPort();
			System.out.println(ip + ":" + port + "说:" + result.trim());
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			socket.close();
		}
	}
}
public class Sender {
	public static void main(String[] args) {
		// 端口号表示的是指定的接收方的端口号,而发送方的端口是由系统自动分配的
		sendMessage("127.0.0.1", 6666, "你好啊");
	}

	public static void sendMessage(String ip, int port, String message) {
		// 1.实例化DatagramSocket的对象, 注意:和流的使用类似,使用套接字完成之后需要关闭
		DatagramSocket socket = null;
		try {
			socket = new DatagramSocket();
			// 2.构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
			DatagramPacket packet = new DatagramPacket(message.getBytes(), message.getBytes().length, InetAddress.getByName(ip),
					port);
			// 3.发送,将数据写入网络的过程,void send(DatagramPacket p) 从此套接字发送数据报包。
			socket.send(packet);
		} catch (SocketException e) {
			// 父类为IOException
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			socket.close();
		}
	}
}

传输控制协议(TCP):TCP(传输控制协议)定义了两台计算机之间进行可靠的传输而交换的数据和确认信息的格式,以及计算机为了确保数据的正确到达而采取的措施。协议规定了TCP软件怎样识别给定计算机上的多个目的进程如何对分组重复这类差错进行恢复。协议还规定了两台计算机如何初始化一个TCP数据流传输以及如何结束这一传输。TCP最大的特点就是提供的是面向连接、可靠的字节流服务。
用户数据报协议(UDP):UDP(用户数据报协议)是一个简单的面向数据报的传输层协议。提供的是非面向连接的、不可靠的数据流传输。UDP不提供可靠性,也不提供报文到达确认、排序以及流量控制等功能。它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。因此报文可能会丢失、重复以及乱序等。但由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

有关TCP和UDP网络编程的更多相关文章

  1. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  2. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  3. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  4. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  5. ruby - 如何以编程方式删除实例上的 "singleton information"以使其编码(marshal)? - 2

    我创建了一个由于“在运行时执行的单例元类定义”而无法编码的对象(这段代码的描述是否正确?)。这是通过以下代码执行的:#defineclassXthatmyusesingletonclassmetaprogrammingfeatures#throughcallofmethod:break_marshalling!classXdefbreak_marshalling!meta_class=class我该怎么做才能使对象编码正确?是否可以从对象instance_of_x的classX中“移除”单例组件?我真的需要一个建议,因为我们的一些对象需要通过Marshal.dump序列化机制进行缓存。

  6. Ruby 元编程问题 - 2

    我正在查看Ruby日志记录库Logging.logger方法并从sourceatgithub提出问题与这段代码有关:logger=::Logging::Logger.new(name)logger.add_appendersappenderlogger.additive=falseclass我知道类 最佳答案 这实际上删除了方法(当它实际被执行时)。这是确保close不会被调用两次的保障措施。看起来好像有嵌套的“class 关于Ruby元编程问题,我们在StackOverflow上找到一

  7. ruby - Paperclip:以编程方式分配图像并设置其名称 - 2

    使用Paperclip,我想从这样的URL抓取图像:require'open-uri'user.photo=open(url)问题是我最后得到一个像“open-uri20110915-4852-1o7k5uw”这样的文件名。有什么方法可以更改user.photo上的文件名?作为一个额外的变化,Paperclip将我的文件存储在S3上,所以如果我可以在初始分配中设置我想要的文件名就更好了,这样图像就会上传到正确的S3key。像这样:user.photo=open(url),:filename=>URI.parse(url).path 最佳答案

  8. ruby - 如何以编程方式检查证书是否已被吊销? - 2

    我正在开发一个xcode自动构建系统。在执行一些预构建验证时,我想检查指定的证书文件是否已被撤销。我了解securityverify-cert验证其他证书属性但不验证吊销。我如何检查撤销?我正在用Ruby编写构建系统,但我对任何语言的想法都持开放态度。我阅读了这个答案(Openssl-Howtocheckifacertificateisrevokedornot),但指向底部的链接(DoesOpenSSLautomaticallyhandleCRLs(CertificateRevocationLists)now?)进入的Material对我的目的来说有点过于复杂(用户上传已撤销的证书是一

  9. ruby - 检查网络文件是否存在,而不下载它? - 2

    是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby​​不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案

  10. ruby - 404 未找到,但可以从网络浏览器正常访问 - 2

    我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT

随机推荐