Java-Socket通信简单实现

#TCP连接
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。
#TCP通过三次握手建立连接
1.客户端发送SYN(SEQ=x)报文给服务端,进入SYN_SEND状态
2.服务端收到SYN(SEQ=y) ACK(ACK=x+1)报文,回应一个SYN报文,进入SYN_RECV状态
3.客户端收到服务端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态
(至此三次握手完成,TCP客户端和服务器端成功建立连接,可以开始传输数据)
示例代码如下:

tcp server端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
 *  * 实现TCP服务器程序
 *  * 表示服务器程序的类 java.net.ServerSocket
 *  * 构造方法:
 *         ServerSocket(int port) 传递端口号
 *  * 很重要的事情:必须要获得客户端的套接字对象Socket
 *     Socket accept()
 */
public class TcpServer {
	public static void main(String[] args)throws IOException {
        ServerSocket server=new ServerSocket(8888);
        while(true) {
        	//调用服务器套接字对象中的方法accept()获取客户端套接字对象
            Socket socket=server.accept();
            //通过客户端套接字对象,socket获取字节输入流,读取的是客户端发送来的数据
            InputStream in=socket.getInputStream();
            byte[] data=new byte[1024];
            int len=in.read(data);
            
            System.out.println("tcp server: " + new String(data,0,len));
            
            //服务器向客户端回数据,字节输出流,通过客户端套接字对象获取字节输出流
            OutputStream out=socket.getOutputStream();
            out.write("hello,tcp client.i am tcp server.".getBytes());
            
//            socket.close();
//            server.close();
        }
        
    }
}

tcp client端:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/*
 * * 实现TCP客户端,接收到服务器
 *  * 和服务器实现数据交换
 *  * 实现TCP客户端程序的类 java.net.Socket
 *  * 构造方法:
 *      Socket(String host,int port) 传服务器IP和端口号
 *      	注意:构造方法只要运行,就会和服务器进行连接,连接失败,抛出异常   
 *         OutputStream getOutputStream() 返回套接字的输出流
 *          *             作用:将数据输出,输出到服务器
 *         InputStream getInputStream() 返回套接字的输入流
 *          *             作用:从服务端读取数据
 * *  客户端服务器数据交换,必须使用套接字对象Socket中的获取的IO流,自己new流,不行
 */
public class TcpClient {
	 public static void main(String[] args) throws IOException, InterruptedException{
	        while(true) {
	        	Thread t = new Thread(new Runnable() {
					public void run() {
						try {
							//创建Socket对象,连接服务器
					        Socket socket=new Socket("127.0.0.1",8888);
					        //通过客户端的套接字对象Socket方法,获取字节输出流,将数据写向服务器
					        OutputStream out=socket.getOutputStream();
					        out.write("hello,tcp server.i am client.".getBytes());
					        
					        //读取服务器发回的数据,使用socket套接字对象中的字节输入流
					        InputStream in=socket.getInputStream();
					        byte[] data=new byte[1024];
					        int len=in.read(data);
					        System.out.println("tcp client:" + new String(data,0,len));
					        
//					        socket.close();
						} catch (Exception e) {
							// TODO: handle exception
						}
					}
	        	});
	        	Thread.sleep(1000);
	        	t.start();
	        }
	    }
}

#UDP连接
UDP是面向无连接的通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接
示例代码如下:

UDP server端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpServer {
	public static void main(String[] args) throws Exception {
		//创建socket
		DatagramSocket socket = new DatagramSocket(7000);
		//创建packet
		DatagramPacket packet = new DatagramPacket(new byte[1024],1024);
		//接收数据
//		while(true){
			socket.receive(packet);
			//获取数据
			byte[] arr = packet.getData();
			
			System.out.println("===="+new String(arr,"utf-8"));
			//获取有效的字节个数
			int len = packet.getLength();
			//获取Ip地址
			String ip = packet.getAddress().getHostAddress();
			//获取端口号
			int port = packet.getPort();
			System.out.println(ip+":"+port+":"+new String(arr,0,len));
//		}
	}
}

UDP client端:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;


public class UdpClient {
	public static void main(String[] args) throws Exception {
		//创建键盘录入对象
//		Scanner scan = new Scanner(System.in);
		DatagramSocket socket = new DatagramSocket();
//		while(true){
			String line ="A fox may grow grey, but never good.";
			if("exit".equals(line)){
//				break;
			}else{
				DatagramPacket packet = new DatagramPacket(line.getBytes(),
						line.getBytes().length,InetAddress.getByName("192.168.1.82"),7000);
				//发送数据
				socket.send(packet);
			}
			System.out.println("发送成功");
//		}
		
		//关闭socket
//		socket.close();
	}
}

udp介绍更详细内容: UDP协议详细讲解<以下部分内容引用自"Linux公社"公众号,可直接关注该公众号进行查阅,如有侵权,请联系删除>

UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。

由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118142903991.png)
1.1 DatagramPacket

前面介绍了UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。UDP通信的过程就像是货运公司在两个码头间发送货物一样。在码头发送和接收货物时都需要使用集装箱来装载货物,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。

想要创建一个DatagramPacket对象,首先需要了解一下它的构造方法。在创建发送端和接收端的DatagramPacket对象时,使用的构造方法有所不同,接收端的构造方法只需要接收一个字节数组来存放接收到的数据,而发送端的构造方法不但要接收存放了发送数据的字节数组,还需要指定发送端IP地址和端口号。

接下来根据API文档的内容,对DatagramPacket的构造方法进行逐一详细地讲解。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019111814283977.png)


使用该构造方法在创建DatagramPacket对象时,指定了封装数据的字节数组和数据的大小,没有指定IP地址和端口号。很明显,这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143003157.png)


使用该构造方法在创建DatagramPacket对象时,不仅指定了封装数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。该对象通常用于发送端,因为在发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址一样。

上面我们讲解了DatagramPacket的构造方法,接下来对DatagramPacket类中的常用方法进行详细地讲解,如下表所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143032965.png)


1.2 DatagramSocket

DatagramPacket数据包的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还需要有码头。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包,发送数据的过程如下图所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143102546.jpg)


在创建发送端和接收端的DatagramSocket对象时,使用的构造方法也有所不同,下面对DatagramSocket类中常用的构造方法进行讲解。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143125773.png)


该构造方法用于创建发送端的DatagramSocket对象,在创建DatagramSocket对象时,并没有指定端口号,此时,系统会分配一个没有被其它网络程序所使用的端口号。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143146951.png)


该构造方法既可用于创建接收端的DatagramSocket对象,又可以创建发送端的DatagramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个端口号,这样就可以监听指定的端口。

上面我们讲解了DatagramSocket的构造方法,接下来对DatagramSocket类中的常用方法进行详细地讲解。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191118143208468.png)


1.3 UDP实现1.3.1 案例代码二:

package com.linuxmi_02;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

/*
 * 使用UDP协议发送数据
创建发送端Socket对象
创建数据并打包
发送数据
释放资源
 *
 * DatagramSocket:此类表示用来发送和接收数据,基于UDP协议的
 *
 * DatagramSocket() :创建Socket对象并随机分配端口号
 * DatagramSocket(int port) :创建Socket对象并指定端口号
 */
public class SendDemo {
public static void main(String[] args) throws IOException  {
//创建发送端Socket对象
DatagramSocket ds = new DatagramSocket();
//创建数据并打包
/*
 * DatagramPacket :此类表示数据报包
 * 数据 byte[]
 * 设备的地址 ip
 * 进程的地址  端口号
  DatagramPacket(byte[] buf, int length, InetAddress address, int port)
 */
String s = "hello udp,im comming!";
byte[] bys = s.getBytes();
int length = bys.length;
InetAddress address = InetAddress.getByName("linuxmi");//发送给当前设备
int port = 8888;
//打包
DatagramPacket dp = new DatagramPacket(bys,length,address,port);
//发送数据
ds.send(dp);
//释放资源
ds.close();
}
}


package com.linuxmi_02;

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

/*
 * 使用UDP协议接收数据
创建接收端Socket对象
接收数据
解析数据
输出数据
释放资源

 */
public class ReceiveDemo {
public static void main(String[] args) throws IOException {
//创建接收端Socket对象
DatagramSocket ds = new DatagramSocket(8888);
//接收数据
//DatagramPacket(byte[] buf, int length)
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys,bys.length);
System.out.println(1);
ds.receive(dp);//阻塞
System.out.println(2);
//解析数据
//InetAddress getAddress() : 获取发送端的IP对象
InetAddress address = dp.getAddress();
//byte[] getData()  :获取接收到的数据,也可以直接使用创建包对象时的数组
byte[] data = dp.getData();
//int getLength()  :获取具体收到数据的长度
int length = dp.getLength();
//输出数据
System.out.println("sender ---> " + address.getHostAddress());
//System.out.println(new String(data,0,length));
System.out.println(new String(bys,0,length));
//释放资源
ds.close();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值