1.java传统的TCP socket编程是阻塞方式,一个简单的Socket程序例子如下:
(1).服务端程序:
import java.io.*; import java.net.*; public class SocketServer{ public static final int PORT = 10500; public static void main(String[] args)throws IOException{ //本机测试程序,无需指定ip地址 ServerSocket server = new ServerSocket(PORT); System.out.println(“Started:” + server); try{ //阻塞直到客户端连接 Socket socket = s.accept(); try{ System.out.println(“Connection accepted:” + socket); BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream())); //自动刷新的PrintWriter PrintWriter out = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())), true); while(true){ String str = in.readLine(); if(str.equals(“END”)) break; System.out.println(“Echoing:” + str); out.println(str); } }finally{ System.out.println(“closing…”); socket.close(); } }finally{ server.close(); } } }
(2).客户端程序:
import java.io.*; import java.net.*; public class SocketClient{ public static void main(String[] args)throw IOException{ //获取本机回环的127.0.0.1地址,还可以使用InetAddress.getByName(“localhost”) //或InetAddress.getByName(“127.0.0.1”); InetAddress addr = InetAddress.getByName(null); System.out.println(“addr = ”+ addr); Socket socket = new Socket(addr, SocketServer.PORT); try{ System.out.println(“socket = ” + socket); BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream())); //自动刷新的PrintWriter PrintWriter out = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())), true); for(int i = 0; i < 10; i++){ out.println(“howdy ” + i); String str = in.readLine(); System.out.println(str); } out.println(“END”); }finally{ System.out.println(“closing…”); socket.close(); } } }
Socket连接必须有四个重要信息:服务端IP地址、服务端端口、客户端IP地址和客户端端口,这四个信息都具备了,服务端程序和客户端程序才能相互交互。例如客户端程序的端口为12000,则:
服务端打印的Socket信息为:
Socket[addr=127.0.0.1,port=10200,localport=10500]
其中addr和port为客户端的ip和端口,localport为服务端端口。
客户端打印的Socket信息为:
Socket[addr=localhost/127.0.0.1,port=8080, localport=12000]
其中addr和port为客户端连接的服务端ip和端口,localport为客户端端口。
2.多服务端多客户端的Socket程序:
1中的例子服务端只能为一个客户端程序服务,若要为多个客户端程序提供服务,就必须使用多线程方式处理客户端请求,例子如下:
(1).服务端程序:
import java.io.*; import java.net.*; class SocketServer extends Thread{ private Socket socket; private BufferedReader in; private PrintWriter out; public ServerSocket(Socket s)throws IOException{ socket = s; in = new BufferedReader(new InputStreamReader( socket.getInputStream())); //自动刷新的PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( socket.getOutputStream())), true); start(); } //多线程处理与客户端交互 public void run(){ try{ while(true){ String str = in.readLine(); if(str.equals(“END”)) break; System.out.println(“Echoing:” + str); out.println(str); } System.out.println(“closing…”); }catch(IOException e){ System.err.println(“IO Exception”); }finally{ try{ socket.close(); } catch(IOException e){ System.err.println(“Socket not closed”); } } } } public class MultiServer{ static final int PORT = 10500; public static void main(String[] args) throws IOException{ ServerSocket server = new ServerSocket(PORT); System.out.println(“Server Started”); try{ while(true){ Socket s = server.accept(); try{ //创建多线程处理与客户端交互操作 new SocketServer(s); }catch(IOException e){ s.close(); } } }finally{ server.close(); } } }
(2).客户端程序:
import java.net.*; import java.io.*; class SocketClient extends Thread{ private Socket socket; private BufferedReader in; private PrintWriter out; private static int counter = 0; private int id = counter++; private static int threadcount = 0; public int getThreadCount(){ return threadcount; } public SocketClient(InetAddress addr){ System.out.println(“Creating client:” + id); Threadcount++; try{ socket = new Socket(addr, SocketServer.PORT); }catch(IOException e){ System.err.println(“Create socket failed”); } try{ in = new BufferedReader(new InputStreamReader( socket.getInputStream())); //自动刷新的PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( socket.getOutputStream())), true); start(); }catch(IOException e){ try{ socket.close(); }catch(IOException ex){ System.err.println(“Socket not closed”); } } } //多线程方式处理与服务端的交互 \public void run(){ try{ for(int i = 0; i < 25; i++){ out.println(“Client ” + id + “ :” + i); String str = in.readLine(); System.out.println(str); } out.println(“END”); }catch(IOException e){ System.err.println(“IO Exception”); }finally{ try{ socket.close(); }catch(IOException e){ System.err.println(“Socket not closed”); } threadcount--; } } } public class MultiClient{ static final int MAX_THREADS = 40; public static void main(String[] args)throws IOException, InterruptedException{ InetAddress addr = InetAddress.getByName(null); while(true){ if(SocketClient. getThreadCount() < MAX_THREADS){ //创建一个Socket客户端线程 new SocketClient(addr); Thread.currentThread().sleep(1000); } } }
Socket客户端程序的端口由系统分配,在创建时只需要指定要连接的目标服务端端口即可。
3.UDP Socket编程小例子:
前面的两个例子是TCP的socket编程,对于可靠性要求不高,而对速度要求很高的情况,需要使用UDP,UDP的例子程序如下:
(1).服务端程序:
import java.net.*; public class UDPServer{ private static final int SERVER_PORT = 5000; private DatagramSocket dataSocket; private DatagramPacket dataPacket; private byte receiveByte[]; private String receiveStr; public static void main(String[] args){ try{ dataSocket = new DatagramSocket(SERVER_PORT); receiveByte = new byte[1024]; dataPacket = new DatagramPacket(receiveByte, receiveByte.length); receiveStr = “”; int i = 0; while(i == 0){ //接收客户端发送的数据 dataSocket.receive(dataPacket); i = dataPacket.getLength(); if(i > 0){ receiveStr = new String(receiveByte, 0, dataPacket.getLength()); System.out.println(receiveStr); //循环接收数据 i = 0; } } }catch(Exception e){ e.printStackTrace(); } } }
(2).客户端程序:
import java.io.*; import java.net.*; public class UDPClient{ private static final int CLIENT_PORT = 5001; private DatagramSocket dataSocket; private DatagramPacket dataPacket; private byte sendDataByte[]; private String sendStr; public static void main(String[] args){ try{ dataSocket = new DatagramSocket(CLIENT_PORT); sendDataByte = new byte[1024]; sendStr = “UDP方式数据测试”; sendDataByte = sendStr.getBytes(); //注意:端口是要发送的目标服务端端口 dataPacket = new DatagramPacket(sendDataByte, sendDataByte.length, InetAddress.getByName(“localhost”), UDPServer.SERVER_PORT); dataSocket.send(dataPacket); }catch(SocketExcption se){ se.printStackTrace(); } catch(IOExcption ie){ ie.printStackTrace(); } } }
DatagramSocket是用于发送和接收UDP数据包的套接字,DatagramPacket是用于表示UDP的数据包。UDP通信中不存在严格意义上的服务端和客户端,每个程序既可以是服务端,又可以同时是客户端,可以实现P2P对等的通信。