添加一名作者
博客:https://www.jianshu.com/p/b04930d2b85e
上周,我们讨论了传输层协议TCP 和UDP,但归根结底,它们只是协议;你看不到它们,也摸不到它们。那么我们如何通过TCP 和UDP 实际发送呢?别担心,看完这篇文章你一定就明白了。
插座概览
Socket中文是套接字的意思,技术术语称为套接字。这意味着开发者可以调用socket相关的API来实现网络通信。 Java也有Socket相关的API,大致可以分为两种:基于UDP传输协议的Socket和基于TCP传输协议的Socket。
UDP套接字
从上一节可以看出,UDP只要提供对方的IP地址和端口号,就可以无连接地发送数据。一旦知道了目标IP和目标端口号,就可以在Java中通过UDP套接字执行IO发送。我们来看一下具体的代码实现。
*** Sender UDP*/public class UDPSocketSend { public static void main(String[] args) throws IOException { System.out.println(‘Sender Start.’); //1. 创建套接字服务DatagramSocket ds=new DatagramSocket(); //2.封装数据str=’今天你说这个词了吗’; byte[] bytes=str.getBytes(); //AddressInetAddress.getByName(‘192.168.31.137’) ;参数: data , 长度, 地址, 端口DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address,6666); //3. 发送数据包ds.send(dp); class UDPSocketReceive{ public static void main(String[] args) throws IOException { System.out.println(‘Receiver Start.’); 声明udp 套接字服务并声明端口号要做的事情。 DatagramSocket ds=new DatagramSocket(6666); //2. 创建数据包来接收数据byte[] bytes=new DatagramPacket dp=new DatagramPacket(bytes,bytes .length) ); //3.这是一种阻塞方法。 //4. 解析数据。 InetAddress 地址=dp.getAddress(); //发送者IP int port=dp. getPort();//发送方端口String content=new String(dp.getData(),0,dp.getLength()); System.out.println(‘address:’+address+’—port:’+ port+’ – –content:’+content); //终止服务ds.close() }}让我们分别启动发送方和接收方。
发件人:
发件人已开始.收件人:
收件人开始. 地址:/192.168.31.137—端口:65037—内容: 你今天背单词了吗?你成功收到消息了,打印了发送者的IP和端口。下面解释了这些步骤。
发件人:
首先,创建一个UDP套接字服务,并将要发送的数据放入数据包DatagramSocket中。 DatagramSocket按照UDP协议封装数据包、IP、端口号,通过UDP套接字服务发送数据包。最后关闭接收UDP服务。
创建udp套接字服务并创建DatagramSocket来解析数据包。通过DatagramSocket接收数据并关闭整个服务。像这样。
当心:
UDP是一种无连接且不可靠的传输,因此接收方必须先发起它,然后发送方才能发送数据。否则,将无法接收到数据。也就是说,您必须首先运行UDPSocketReceive,然后运行UDPSocketSend。
聊天实例
您可以通过对上面的示例进行一些小的更改来实现聊天功能。
公共类UDPSocket1 { 公共静态void main(String[] args) { 尝试{ 新线程(new Runnable() { @Override public void run() { 尝试{accept(); } catch (IOException e) { e.printStackTrace( ); } } }). start(); } catch (IOException e) { e.printStackTrace(); } //消息接收方法private static void receive() throws IOException ‘UDOSocket1 Receiver Start.’ ); //1.创建udp套接字服务并声明端口号。 DatagramSocket ds=new DatagramSocket(6666); //无限循环, while (true); 2.创建数据包接收数据byte[] bytes=new DatagramPacket dp=new DatagramPacket(bytes, bytes.length); 接收数据包。 //4. 数据字符串content=new String(dp.getData(), 0, dp.getLength()); System.out.println(‘UDPSocket1 Receive:’ + content); //1. DatagramSocket ds=new DatagramSocket(); //将键盘输入信息转换为输入流并存储在缓冲区中BufferedReader br=new BufferedReader(new InputStreamReader(System.in));readLine())!=null){ //2.封装数据byte[] bytes=line.getBytes(); //AddressInetAddress 地址=InetAddress.getByName(‘192.168.31.137 ‘); 地址, 端口DatagramPacket (bytes,bytes.length,address,7777); //3. 发送数据包。关闭套接字服务ds.close(); }}public class UDPSocket2 { public static void main(String[] args){ try { new Thread(new Runnable() { @Override public void run() { try ( ); } catch (IOException e) { e.printStackTrace() } }).start(); } catch (IOException e) { e.printStackTrace() } //消息接收方法void get () throws IOException { System.out .println (‘UDOSocket2 Receiver Start.’); //1. 创建udp套接字服务并声明端口号。 DatagramSocket ds=new DatagramSocket(7777); 始终处于接收状态while (true) { //2. 创建数据包接收数据byte[] bytes=new DatagramPacket dp=new DatagramPacket(bytes, bytes.length) ;data数据包中ds.receive(dp); //4. Data String content=new String(dp.getData(), 0, dp.getLength()); } //关闭服务.close(); } private static void send() throws IOException { //1. 创建一个套接字服务DatagramSocket ds=new DatagramSocket();然后对其进行缓冲。 BufferedReader br=new BufferedReader(new InputStreamReader(System.in)) String line=null while ((line=br.readLine())!=null);封装数据byte[ ] bytes=line.getBytes(); address=InetAddress.getByName(‘192.168.31.137’); //参数:数据、长度、地址、端口DatagramPacket dp=new DatagramPacket(bytes,bytes.length,address ,6666); //3. send(dp); //4. 关闭套接字服务}} 在接收器方法中编写无限循环。连续接收状态,发送消息是通过键盘打字并按回车键发送的,两个端点都具有发送和接收能力。请注意,accept() 运行无限循环,因此receive() 和send() 必须位于不同的线程上。否则,不会发送任何消息,也不会接收任何消息。
我们来看看打印的结果。
UDP套接字1
UDPSocket 接收器启动.UDPSocket Receive:hello udp1heelo udp2UDPSocket2
UDPSocket2 接收器启动.hello udp1UDPSocket2 Receive:hello udp2 我在UDPSocket1的控制台输入“hello udp2”,在UDPSocket2的控制台输入“hello udp1”。接收和发送的内容完全一样,是一个简单的聊天功能。现在已经实现了,不习惯的朋友也可以尝试输入代码练习一下。
TCP套接字
TCP是一种基于客户端/服务器的可靠的、面向连接的传输。在上一篇文章中我们也讨论了TCP的安全传输机制,这个机制相当复杂,对于需要封装TCP协议的个人来说显然是困难的。为了实现这一点,Java 还为开发人员提供了基于TCP 的套接字。与UDP不同,TCP套接字分为套接字和服务器套接字,分别对应客户端和服务器。我们用代码来实现一个简单的TCP通信功能。
客户:
//Client public class TCPClient { public static void main(String[] args) throws IOException { //1. 创建一个TCP 客户端。 Socket client=new Socket(); //2. 连接到服务器。 InetSocketAddress=new InetSocketAddress(‘192.168.31.137’,10000); client.connect(address); //3. 如果连接成功,获取客户端的socket输出流。 outputStream=client.getOutputStream();服务器写入数据。 outputStream.write(‘hello server’.getBytes()); //5. 关闭流。 client.close() }} 首先,创建一个Socket 和一个InetSocketAddress,并通过Socket 的connect() 进行连接。该方法可以在连接成功后获取输出流,通过该输出流可以向服务器发送数据。
服务器:
public class TCPServer { public static void main(String[] args) throws IOException { //1. 创建服务器套接字并指定端口号。 ServerSocket serverSocket=new ServerSocket(10000); //2. 获取客户端的套接字。 serverSocket.accept(); //3.通过客户端的socket获取输入流inputStream=socket.getInputStream() //4.通过输入流获取客户端传入的数据bufferedReader=new BufferedReader ( inputStream )); line=null while ((line=bufferedReader.readLine())!=null){ System.out.println(line) } //5. 关闭流。我们首先创建一个服务器Socket并指定端口号,通过accept()方法获取链接的客户端Socket,从客户端Socket获取输入流,最后从输入中读取客户端发送的数据。溪流。
我们看一下服务器端的打印结果。
hello 服务器成功接收到客户端的数据
当心:
服务器可以同时与多个客户端通信,但是它如何区分不同的客户端呢?从上面的代码中,服务器首先通过accept()获取客户端的socket,然后使用客户端的socket来进行通信。通过捕获的流。这也允许服务器区分每个客户端。
文件传输示例
流程:客户端上传文件到服务器。服务器成功保存文件后,向客户端发送响应。
客户代码
public class TCPUploadClient { public static void main(String[] args) throws IOException { //1. 创建一个TCP 客户端。 Socket client=new Socket(); //2. 连接到服务器InetSocketAddress address=new InetSocketAddress( ‘192.168) .31.137’,10001); //3. 加载客户端文件。 FileInputStream fis=new FileInputStream(‘E://girl.jpg’); //5. 将文件写入服务器byte[] byte=0; bytes)) { OutputStream.write(bytes,0,len) } //6. 通知服务器数据已写入client.shutdownOutput() //7. 读取服务器返回的数据。 ( ); BufferedReader br=new BufferedReader(inputStream); String line=br.readLine(); //8. 关闭客户端.close();将文件写入服务器,写入成功后读取服务器返回的数据。
服务器代码
public class TCPUploadServer { public static void main(String[] args) throws IOException { //1. 创建客户端套接字。 ServerSocket=new ServerSocket(10001); //2. 获取客户端套接字。 //3. 通过客户端套接字获取输入流。 InputStream is=socket.getInputStream() //4. 将流以文件格式写入磁盘。 File dir=new File(‘F://tcp’) ) ; //如果文件夹不存在则创建if(!dir.exists()){ dir.mkdirs() } File file=new File(dir, ‘girl.jpg’); byte[] bytes=new byte[1024]; int len=0; while ((len=is.read(bytes))!=-1){ fos . write(bytes,0,len) ); } //5. 通知客户端文件已保存。 os.close();Socket.close(); }}创建Socket,获取客户端的Socket和输入流,在F盘tcp目录下创建文件,将从客户端读取到的数据输出到文件中。马苏。如果成功保存到客户端,则返回“成功”。
这就是我们如何实现向亿格客户端上传文件的功能。我希望任何人都可以按照该代码并再次输入它。
TCP套接字多线程应用程序
细心的同学可能已经注意到,在上面的例子中,一台服务器只能接收一个客户端的一个请求,这显然是不现实的。那么一台服务器如何同时服务多个客户端呢?接下来写代码
//Client 1 public class TCPClient1 { public static void main(String[] args) throws IOException { System.out.println(‘TCPClient1 Start.’); //1. 创建TCP 客户端Socket 服务Socket client=new Socket(); //2.连接服务器InetSocketAddress(‘192.168.31.137’,10004); //3.如果连接成功,获取客户端的Socket输出流。=client.getOutputStream(); //4.通过输出流向服务器写入数据。 outputStream.write(‘Hello my name is Client1’.getBytes()); //5. client.shutdownOutput( ); //6. 读取服务器返回的数据。out.println(new String (bytes,0,len)); //7. 关闭流client.close() }}//Client 2 public class TCPClient2 { public static void main(String[] args); { System.out.println( ‘TCPClient2 Start.’); //1. 创建TCP 客户端套接字服务。 Socket client=new Socket(); //2. 连接到服务器。 192.168.31.137′,10004) ; client.connect(address); //3.连接成功后,通过output获取客户端的socket输出流。 Stream OutputStream.write(‘Hello, my name is Client2’.getBytes()); //5. 告诉服务器传输完成client.shutdownOutput(); //6. 从服务器返回的数据。 client.getInputStream(); byte[] bytes=new byte[1024];=is.read(bytes); System.out.println(new String(bytes,0,len)); 关闭流client.close( ) ; }}//服务器public class TCPServer { public static void main(String[] args) throws IOException {accept(); } private static void accept() throws IOException { System.out.println(‘服务器启动. ‘ ); //为服务创建终端套接字并指定端口号。 ServerSocket serverSocket=new ServerSocket(10004); //获取客户端套接字。 //通过客户端的套接字获取输入流。 InputStream is=socket .getInputStream(); //获取客户端通过输入流传递过来的数据。 byte[] bytes=new byte [1024]=is. read(bytes); System.out.println(new String(bytes,0 ,len)); //原样返回客户端发送的数据os=socket.getOutputStream();0,len).getBytes()); //关闭连接。 socket.close() } }}客户端1 控制台
TCPClient1 启动.您好,我的名字是Client1 客户端2 控制台
TCPClient2 启动.您好,我的名字是Client2。这样,一台服务器就可以为多个客户端提供服务。
细心的同学可能已经注意到上面的说法有问题了。服务器始终在主线程上处理请求,因此客户端请求必须由服务器排队。例如,Client1 在服务器上执行请求。服务器在响应Client1之前不会接受任何其他请求。这显然是不合逻辑的。真实服务器必须能够并发处理。多线程可以解决这个问题。我们来看看修改后的服务器代码。
public class TCPServer { public static void main(String[] args) throws IOException {accept(); } private static void accept() throws IOException { System.out.println(‘Server Start.’);指定终端套接字并指定端口号。 ServerSocket serverSocket=new ServerSocket(10004); while (true){ //获取客户端的套接字。 Runnable () { @Override public void run() { try { //通过客户端的socket获取输入流。 InputStream is=socket.getInputStream() //获取客户端通过输入流传入的数据。 byte[] bytes;=new byte[1024]=System.out.println(new String(bytes,0,len)); //按原样返回客户端发送的数据马苏。 socket.getOutputStream(); os.write(new String(bytes,0,len).getBytes()); //关闭连接。运行时效果和添加线程之前一样,但是这种方法效率更高。
本文介绍基于UDP和TCP的套接字。 UDP是无连接的,因此UDP套接字发送数据时只需要目标IP和端口。 TCP 是面向连接的,需要先建立连接才能发送数据。多线程技术允许同时处理客户端请求。本文比较简单,因此掌握Sockets将有助于您更好地理解UDP和TCP。
原创文章,作者:小条,如若转载,请注明出处:https://www.sudun.com/ask/85358.html