Java socket 入门 示例

面向连接传输协议(TCP)

两台电脑在传输数据前,会先建立一个专属的连接,就如打电话一般,双方通话时,会占有一条专属的通讯连接,当有一方挂机后,此连接就会消失。面向连接的协议特点:

  1. 确认回应:设甲乙两台电脑,当甲传数据到乙时,会等乙送回确认后才会再去传下一笔数据。当一段时间没有收到乙的确认回复,甲会试着再传一次,如果传送多次都没有回应,就表示连接可能断了。
  2. 分组序号:面向连接协议在传送数据的时候会把数据分割成固定大小的分组(packet),然后在每个分组上加上序号。序号用来检查哪些分组收到,哪些封包重复等。
  3. 流量控制

TCP是属于可靠性传输。

 

非面向连接传输协议(UDP)

与面向连接协议相比较,使用非连接协议传输数据的电脑间并不会建立一条专属的连接,这样的连接不可靠,分组可能丢失,可能收到重复的分组,也可能出现后面的分组比前面的分组先收到的情形。UDP协议一般用于短距离的传输,或者大量传输且容许些许错误的场合,如视频会议等,或者multicasting或则broadcasting。

 

连接端口

一台电脑可能同时运行着多个Server,那么此时还需要一个端口来明确客户端到底想连此台电脑的哪个Server。就如电话的分机号码,先拨通总机,然后总机根据提供的分机号码转到该分机。

 

Socket

Socket是程序与网络间的一种接口, 通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络,请求大部分的网络应用都是点对点的(point-to-point),所谓点就是服务端和客户端所执行的程序。Socket是用来接收或传送分组的一个端点。

 

Java中基本的 服务器——客户端 模型

服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。

 

客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。

 

示例代码:

 

 服务器端:

 

public class TCPEchoServer {

	public static final int PORT = 7777;
	public static final String ADDR = "127.0.0.1";
	
	protected void handleReq(Socket clnt) throws IOException
	{
		System.out.println("Client connected from: " + clnt.getInetAddress());
		
		System.out.println(clnt.isConnected()+ "~~~~" + clnt.isClosed());
		
		BufferedInputStream dataIn = new BufferedInputStream(clnt.getInputStream());
		
		byte[] b = new byte[64];
		
		int len = dataIn.read(b);
		
		String sendBack = new String(b, 0, len);
		sendBack = sendBack.toUpperCase();
		
		OutputStream dataOut = clnt.getOutputStream();
		
		dataOut.write(sendBack.getBytes());
		
		dataIn.close();
		
		dataOut.close();
		System.out.println(clnt.isConnected()+ "~~~~" + clnt.isClosed());
	}
	
	public void start()
	{
		try 
		{ 
			ServerSocket srv = new ServerSocket( PORT, 0, InetAddress.getByName(ADDR) );
			System.out.println("Echo server start at port: " + PORT);
			
			while( true )
			{
				// 此方法阻塞,直至有socket的连接请求过来
				Socket clnt = srv.accept();
				
				handleReq(clnt);
			}
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) 
	{
		new TCPEchoServer().start();
	}
}
 

 

客户端:
public class TCPEchoClient {

	JFrame mainFrame;
	
	JPanel container;
	
	JLabel reqLabel;
	
	JLabel resLabel;
	
	JTextField reqTfld;
	
	JTextField resTfld;
	
	JButton sendBtn;
	
	private class SendEchoAction implements ActionListener
	{
		@Override
		public void actionPerformed(ActionEvent e) 
		{
			String reqStr = reqTfld.getText();
			
			sendEcho(reqStr);
		}
	}
	
	public void initUI() 
	{
		mainFrame = new JFrame("Echo client");
		
		setCmpt();
		
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		mainFrame.pack();
		
		mainFrame.setLocationRelativeTo(null);
		
		mainFrame.setVisible(true);
	}
	
	private void setCmpt() 
	{
		reqLabel = new JLabel("Input String:");
		resLabel = new JLabel("Server return String:");
		
		reqTfld = new JTextField();
		resTfld = new JTextField();
		
		sendBtn = new JButton("Send");
		
		sendBtn.addActionListener(new SendEchoAction());
		
		container = new JPanel();
		container.setLayout(new GridLayout(2, 3));
		
		container.add(reqLabel);
		container.add(reqTfld);
		container.add(sendBtn);
		
		container.add(resLabel);
		container.add(resTfld);
		
		mainFrame.setLayout(new FlowLayout(FlowLayout.CENTER));
		mainFrame.add(container);
		
	}
	
	public void sendEcho(String echo)
	{
		Socket snd = null;
		try 
		{
			snd = new Socket(TCPEchoServer.ADDR, TCPEchoServer.PORT);
			
			OutputStream out = snd.getOutputStream();
			
			out.write(echo.getBytes());
			
			InputStream in = snd.getInputStream();
			
			byte[] b = new byte[1024];
			
			int len = in.read(b);
			
			String resEcho = new String(b, 0, len); 
			resTfld.setText(resEcho);
		} 
		catch (Exception e1) 
		{
			e1.printStackTrace();
			resTfld.setText(e1.getMessage());
		}
		finally
		{
			if( snd != null )
			{
				try 
				{
					snd.close();
				} 
				catch (IOException e1)
				{
				}
			}
		}
	}
	
	
	public static void main(String[] args) {
		
		TCPEchoClient client = new TCPEchoClient();
		client.initUI();
	}
}
 运行客户端如下:

 
 
 多线程服务器端模型
 为服务器端增加多线程处理连接,因为Java基于TCP的实现具有阻塞特性,若多个Client连接进来却不能并发处理的话,这样效率很低。此处只需server做改动。
示例代码:
服务器端:
public class TCPEchoMultiThreadServer extends TCPEchoServer{

	@Override
	public void handleReq(Socket clnt) throws IOException 
	{
                // 重写此方法,睡它一下,模拟处理延时
		try 
		{
			Thread.sleep(1500L);
		} 
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		}
		super.handleReq(clnt);
	}
	
	@Override
	public void start() 
	{
		try 
		{
			ServerSocket srv = new ServerSocket(PORT, 0, InetAddress.getByName(ADDR));
			System.out.println("Echo multi-thread server start at port: " + PORT);
			
			while( true )
			{
				final Socket clnt = srv.accept();
				
				Thread thread = new Thread( new Runnable() 
					{
						@Override
						public void run() 
						{
							try 
							{
								handleReq(clnt);
							} 
							catch (IOException e) 
							{
								e.printStackTrace();
							}
						}
					}
				);
				
				thread.start();
                                // 打印出线程名字,可以看出每次请求都会新建线程来处理
				System.out.println(thread.getName());
			}
		} 
		catch (IOException e) 
		{
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) 
	{
		new TCPEchoMultiThreadServer().start();
	}
}
  
广播服务器模型
玩Dota, LoL,当中同组的消息列就是属于广播模型
示例代码:
 Server 端:
public class TCPEchoBroadcastServcer{

	public static final int PORT = 7777;
	
	public static final String ADDR = "127.0.0.1";
	
	public static final String LOGIN_CODE = "#login";
	
	public static final String LOGOUT_CODE = "#logout"; 

	public static final String REGISTER_NEED = "#yourID";
	
	public static final String FORMAL_MSG = "#fMsg";
	
	private Map<String, Socket> clntSocketList;
	
	public void start()
	{
		try 
		{
			ServerSocket ss = new ServerSocket( PORT, 0, InetAddress.getByName(ADDR) );
			System.out.println("Echo multi-thread server start at port: " + PORT);
			clntSocketList = new HashMap<String, Socket>();
			
			while( true )
			{
				final Socket clnt = ss.accept();
				
				Thread thread = new Thread( 
					new Runnable() 
					{
						@Override
						public void run() 
						{
							try 
							{
								handleReq(clnt);
							} 
							catch (IOException e) 
							{
								e.printStackTrace();
							}
						}
					}
				);
				
				thread.start();
				
			}
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
	
	protected void handleReq(Socket clnt) throws IOException 
	{
		System.out.println("Client connected from: " + clnt.getInetAddress());
		
		InputStream dataIn = clnt.getInputStream();
		
		byte[] b = new byte[64]; 
		int len = dataIn.read(b);
		String req = new String(b, 0, len);
		
                // 这里实现比较挫,用了几个静态常量去区分消息种类,
                // FORMAL_MSG代表的是普通消息,LOGIN_CODEclient连进server端的消息
                // LOGOUT_CODE是离开server端消息
		if( req.indexOf(FORMAL_MSG) == -1 )
		{
			if( req.indexOf( "["+LOGIN_CODE+"]" ) == 0 )
			{
				String clntId = req.split(":")[1];
				
				clntSocketList.put(clntId, clnt);
				
				req = "New client login";
			}
			else if(  req.indexOf( "["+LOGOUT_CODE+"]" ) == 0 )
			{
				String clntId = req.split(":")[1];
				
				if( clntSocketList.containsKey(clntId) )
				{
					clntSocketList.get(clntId).close();
					clntSocketList.remove(clntId);
				}
				
				req = "A client left";
			}
		}
		else
		{
                        //普通消息需要进行截取字符串,比较挫,但一时想不出好办法。。。
			req = req.substring(req.indexOf(FORMAL_MSG)+FORMAL_MSG.length()+1, req.length());
		}
		
		_sendBroadcastMsg(req);
	}
	
	private synchronized void _sendBroadcastMsg( String msg ) throws IOException
	{
		for( String key: clntSocketList.keySet() )
		{
			Socket clnt = clntSocketList.get(key);
			if( !clnt.isClosed() && clnt.isConnected() )
			{
				this._sendMsg(clnt, msg);
				//out.close();
			}
		}
	}
	
	private void _sendMsg( Socket clnt, String msg ) throws IOException 
	{
		OutputStream out = clnt.getOutputStream();
		
		out.write(msg.getBytes());
	}
	
	public static void main(String[] args) 
	{
		new TCPEchoBroadcastServcer().start();
	}
}
 Client端:
public class TCPEchoBroadcastClient {
	
	private String id;
	
	JFrame mainFrame;
	
	JPanel container;
	
	JPanel row1;
	
	JPanel row2;
	
	JLabel reqLabel;
	
	JLabel resLabel;
	
	JTextField reqTfld;
	
	JTextArea resTArea;
	
	JButton sendBtn;
	
	Socket connSocket;
	
	public TCPEchoBroadcastClient() 
	{
		this.id = UUID.randomUUID().toString();
	}
	
	private class SendEchoAction implements ActionListener
	{
		@Override
		public void actionPerformed(ActionEvent e) 
		{
			String reqStr = reqTfld.getText();
			
			sendEcho(reqStr);
		}
	}
	
	private class DisconnectAction extends WindowAdapter
	{
		@Override
		public void windowClosing(WindowEvent e) 
		{
			try 
			{
				_leftServer();
			} 
			catch (IOException e1) {}
			super.windowClosing(e);
		}
	}
	
	public void initUI() 
	{
		mainFrame = new JFrame("Echo Broadcast client");
		
		setCmpt();
		
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		mainFrame.addWindowListener(new DisconnectAction());
		
		//mainFrame.pack();
		mainFrame.setSize(500, 400);
		
		mainFrame.setResizable(false);
		
		mainFrame.setLocationRelativeTo(null);
		
		mainFrame.setVisible(true);
		
		_connectServer();
	}
	
	private void setCmpt() 
	{
		row1 = new JPanel();
		
		reqLabel = new JLabel("Input String:");
		resLabel = new JLabel("Server return String:");
		
		reqTfld = new JTextField();
		resTArea = new JTextArea();
		resTArea.setEditable(false);
		
		sendBtn = new JButton("Send");
		
		sendBtn.addActionListener(new SendEchoAction());
		
		container = new JPanel();
		
		GridBagLayout layout = new GridBagLayout();
		container.setLayout(layout);
	
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.fill = GridBagConstraints.BOTH;
		gbc.insets = new Insets(2, 2, 2, 2);
		
		gbc.weightx = 1;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		layout.setConstraints(reqLabel, gbc);
		container.add(reqLabel);
		
		gbc.weightx = 1;
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		gbc.ipadx = 220;
		layout.setConstraints(reqTfld, gbc);
		container.add(reqTfld);
		
		gbc.weightx = 1;
		gbc.gridwidth = 0;
		gbc.gridheight = 1;
		gbc.ipadx = 0;
		layout.setConstraints(sendBtn, gbc);
		container.add(sendBtn);
		
		JPanel resLbPanel = new JPanel();
		resLbPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
		
		resLbPanel.add(resLabel);
		
		gbc.gridwidth = 1;
		gbc.gridheight = 1;
		layout.setConstraints(resLbPanel, gbc);
		container.add(resLbPanel);
		
		JScrollPane resTaPanel = new JScrollPane(resTArea);
		resTaPanel.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		resTaPanel.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		
		gbc.weighty = 1;
		gbc.gridwidth = 0;
		gbc.gridheight = 1;
		layout.setConstraints(resTaPanel, gbc);
		container.add(resTaPanel);
		
		mainFrame.add(container);
	}
	
	private void _connectServer()
	{
		try 
		{
			connSocket = new Socket(TCPEchoBroadcastServcer.ADDR, TCPEchoBroadcastServcer.PORT);
			
			this._registerToServer();
			
			Thread thread = new Thread(new Runnable() 
				{
					@Override
					public void run() 
					{
						try 
						{
							while( true )
							{
								_listenToServer();
							}
						} 
						catch (IOException e) 
						{
							e.printStackTrace();
						}
					}
				}
			);
			
			thread.start();
		} 
		catch (Exception e) 
		{
			e.printStackTrace();
			if( connSocket != null )
			{
				try 
				{
					connSocket.close();
				} 
				catch (IOException ie) {}
			}
		} 
	}
	
	private void _registerToServer() throws IOException
	{
		OutputStream out = connSocket.getOutputStream();
		
		String reqStr = "[" + TCPEchoBroadcastServcer.LOGIN_CODE + "]:" + this.id;
		out.write(reqStr.getBytes());
		
		out.flush();
	}
	
	private void _leftServer() throws IOException
	{
		Socket snd = new Socket(TCPEchoBroadcastServcer.ADDR, TCPEchoBroadcastServcer.PORT);
		
		OutputStream out = snd.getOutputStream();
		
		String reqStr = "[" + TCPEchoBroadcastServcer.LOGOUT_CODE + "]:" + this.id;
		out.write(reqStr.getBytes());
		
		out.flush();
		
		snd.close();
	}
	
	private void _listenToServer() throws IOException
	{
		if( connSocket.isClosed() )
		{
			return;
		}
		
		String resStr = null;
		
		InputStream in = connSocket.getInputStream();
		
		byte[] b = new byte[64];
		int len = in.read(b);
		if( len != -1 )
		{
			resStr = new String(b, 0, len);
			
			resTArea.append(resStr+"\n");
		}
	}
	
	public void sendEcho(String echo)
	{
		Socket snd = null;
		try 
		{
			snd = new Socket(TCPEchoBroadcastServcer.ADDR, TCPEchoBroadcastServcer.PORT);
			
			OutputStream out = snd.getOutputStream();
			
			echo = TCPEchoBroadcastServcer.FORMAL_MSG + ":" + echo;
			out.write(echo.getBytes());
			
			out.flush();
			
		} 
		catch (Exception e1) 
		{
			e1.printStackTrace();
		}
		finally
		{
			if( snd != null )
			{
				try 
				{
					snd.close();
				} 
				catch (IOException e1){}
			}
		}
	}
	
	public static void main(String[] args) 
	{
		TCPEchoBroadcastClient client = new TCPEchoBroadcastClient();
		client.initUI();
	}
}
 

 
UDP通讯方式
最后是基于UDP传输协议的一个demo,具体应用场景还不太清楚。。。
Server 端:
public class UDPEchoServer {

	public static final int PORT = 7777;
	public static final String ADDR = "127.0.0.1";
	
	public static void main(String[] args) {
		
		DatagramSocket srv = null;
		try 
		{
			byte[] buffer = new byte[1024];
			
			srv = new DatagramSocket(PORT);
			
			DatagramPacket packet = new DatagramPacket(buffer, 1024);
			System.out.println("Server is ready, Waiting requset come...");
			
			while( true )
			{
				srv.receive(packet);
				
				InetAddress clntAddr = packet.getAddress();
				
				int clntPort = packet.getPort();
				
				System.out.println("A request package from: " + clntAddr + " # " + clntPort);
				
				String str = new String(packet.getData());
				
				String echo = str.toUpperCase();
				
				DatagramPacket resPacket = new DatagramPacket(echo.getBytes(), echo.length(), clntAddr, clntPort);
				
				srv.send(resPacket);
			}
		} 
		catch (Exception e) 
		{
			if( srv != null )
			{
				srv.close();
			}
		}
	}
}
 
客户端:
public class UDPEchoClient {

JFrame mainFrame;
	
	JPanel container;
	
	JLabel reqLabel;
	
	JLabel resLabel;
	
	JTextField reqTfld;
	
	JTextField resTfld;
	
	JButton sendBtn;
	
	private class SendEchoAction implements ActionListener
	{
		@Override
		public void actionPerformed(ActionEvent e) 
		{
			String reqStr = reqTfld.getText();
			
			sendEcho(reqStr);
		}
	}
	
	public void initUI() 
	{
		mainFrame = new JFrame("Echo client");
		
		setCmpt();
		
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		mainFrame.pack();
		
		mainFrame.setLocationRelativeTo(null);
		
		mainFrame.setVisible(true);
	}
	
	private void setCmpt() 
	{
		reqLabel = new JLabel("Input String:");
		resLabel = new JLabel("Server return String:");
		
		reqTfld = new JTextField();
		resTfld = new JTextField();
		
		sendBtn = new JButton("Send");
		
		sendBtn.addActionListener(new SendEchoAction());
		
		container = new JPanel();
		container.setLayout(new GridLayout(2, 3));
		
		container.add(reqLabel);
		container.add(reqTfld);
		container.add(sendBtn);
		
		container.add(resLabel);
		container.add(resTfld);
		
		mainFrame.setLayout(new FlowLayout(FlowLayout.CENTER));
		mainFrame.add(container);
		
	}
	
	public void sendEcho(String echo)
	{
		DatagramSocket clnt = null;
		
		try 
		{
			clnt = new DatagramSocket();
			
			byte[] sndEcho =  echo.getBytes();
			
			InetAddress addr = InetAddress.getByName(UDPEchoServer.ADDR);
			
			DatagramPacket packet = new DatagramPacket(sndEcho, sndEcho.length, addr, UDPEchoServer.PORT);
			
			clnt.send(packet);
			
			clnt.receive(packet);
			
			byte[] resData =  packet.getData();
			
			resTfld.setText(new String(resData));
		} 
		catch (Exception e) 
		{
			clnt.close();
		}
	}
	
	public static void main(String[] args) 
	{
		UDPEchoClient client = new UDPEchoClient();
		
		client.initUI();
	}
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,你的问题是关于Java socket和PLC通信的。在Java中,可以使用Socket API来实现网络编程,包括与PLC通信。下面是一个简单的Java代码示例,用于向PLC发送数据: ```java import java.io.*; import java.net.*; public class PLCClient { public static void main(String[] args) throws IOException { String hostName = "localhost"; // PLC的IP地址 int portNumber = 12345; // PLC监听的端口号 try (Socket socket = new Socket(hostName, portNumber); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))) { String userInput; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); // 向PLC发送数据 System.out.println("PLC返回的数据:" + in.readLine()); // 读取PLC返回的数据 } } catch (UnknownHostException e) { System.err.println("无法连接到主机:" + hostName); System.exit(1); } catch (IOException e) { System.err.println("无法在端口 " + portNumber + " 上进行连接。"); System.exit(1); } } } ``` 在这个例子中,我们使用了Socket类来建立与PLC的连接,并使用PrintWriter和BufferedReader类来读写数据。当我们向PLC发送数据时,我们可以使用out.println()方法,当我们接收到PLC返回的数据时,我们可以使用in.readLine()方法。 请注意,这只是一个简单的例子,实际上,与PLC通信可能需要使用特定的协议和数据格式。同时需要考虑到网络延迟和数据丢失等问题。因此,如果需要进行生产环境的PLC通信,请务必仔细研究PLC的通信协议和数据格式,以确保数据的正确传输和处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值