一、Socket(套接字)
连接原理:
1、Socket客户端:
Socket s=new Socket(ip,port);打开一个套接字,它是网络编程中的一个抽象概念,负责启动该程序内部和外部之间的通信。如果连接失败,将会抛出一个UnknownHostException异常;如果存才其他问题,将会抛出IOException异常。
InputStream inStream=s.getInputStream();用于接收数据。 OutputStream outStream=s.getOutputStream();用于发送数据。
2、套接字超时
有两种超时情况:
1):从套接字读取信息时,在有数据可供访问之前,读操作将会被堵塞。如类似in.nextLine();
解决办法:调用setSoTimeout方法设置一个超时值。即调用s.setSoTimeout(1000);
2):Socket(String host,int port)会一直堵塞,直到建立了到达机主的初始连接为止。(实例化Socket时会堵塞)
解决办法:先构建一个无连接的套接字,然后再使用一个超时来进行连接的方法。即s.connect(new InetSocketAddress(host,port),timeout);
3、Socket服务器:
它可以向客户端发送消息,一旦启动服务器程序,它便等待某个客户端连接到它的端口。ServerSocket用于建立套接字。
ServerSocket s=new ServerSocket(port);创建一个端口 Socket incoming=s.accept();告诉服务器不停等待,直到有客户端连接到这个端口,该方法会返回一个Socket对象,用这个对象得到输入、输出流。 InputStream inStream=incoming.getInputStream();接收数据 OutputStream outStream=incoming.getOutputStream();发送数据
如果通过套接字发送文本,可以将流转换成扫描器和写入器。
Scanner in=new Scanner(inStream); PrintWriter out=new PrintWriter(outStream,true); System.out.println("hello....");向客户端发送消息
最后要关闭连接进来的套接字。
incoming.close();关闭套接字
一个简单的服务器向客户端发送数据报的例子1:
1 package socket; 2 import java.net.*; 3 public class server { 4 public static void main(String[] args)throws Exception{ 5 DatagramSocket server=new DatagramSocket(3000); 6 String str="hello"; 7 DatagramPacket packet=new DatagramPacket(str.getBytes(),str.length(),InetAddress.getLocalHost(),9000); 8 server.send(packet); 9 server.close(); 10 } 11 }
package socket; import java.net.*; import java.io.*; public class client { public static void main(String[] args)throws IOException{ byte[] buf=new byte[1024]; DatagramPacket packet=new DatagramPacket(buf,1024); DatagramSocket client=new DatagramSocket(9000); client.receive(packet); String str=new String(buf,0,packet.getLength()); System.out.println(packet.getAddress().getHostName()+":"+str); client.close(); } }
结果为:Feng:hello
例子2:简单的聊天设计
服务器端:
1 package socket; 2 3 4 import java.awt.BorderLayout; 5 import java.awt.event.ActionEvent; 6 import java.awt.event.ActionListener; 7 import java.io.BufferedReader; 8 import java.io.InputStreamReader; 9 import java.io.PrintStream; 10 import java.net.InetAddress; 11 import java.net.ServerSocket; 12 import java.net.Socket; 13 14 import javax.swing.JButton; 15 import javax.swing.JFrame; 16 import javax.swing.JPanel; 17 import javax.swing.JScrollPane; 18 import javax.swing.JTextArea; 19 import javax.swing.JTextField; 20 21 @SuppressWarnings("serial") 22 public class Server3 extends JFrame{ 23 static JTextArea area; 24 JTextField field; 25 JButton button; 26 static PrintStream writer; 27 public Server3(){ 28 this.setTitle("服务器"); 29 this.setSize(400,500); 30 area = new JTextArea(25,30); 31 area.setEditable(false); 32 field = new JTextField(20); 33 button = new JButton("提交"); 34 JPanel panel = new JPanel(); 35 JScrollPane sp = new JScrollPane(area); 36 this.add(sp,BorderLayout.CENTER); 37 panel.add(field); 38 panel.add(button); 39 this.add(panel,BorderLayout.SOUTH); 40 this.setVisible(true); 41 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 42 43 button.addActionListener(new ActionListener(){ 44 45 @Override 46 public void actionPerformed(ActionEvent e) { 47 String text = field.getText(); 48 writer.println(text); 49 area.append("我:"+text+"\n"); 50 field.setText(""); 51 } 52 }); 53 } 54 public static void main(String[] args) throws Exception { 55 Server3 s = new Server3(); 56 ServerSocket server = new ServerSocket(8899); 57 System.out.println("开始监听..."); 58 Socket socket = server.accept(); 59 InetAddress address = socket.getInetAddress(); 60 String name = address.getLocalHost().getHostName(); 61 System.out.println(name+"已连接"); 62 BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); 63 writer = new PrintStream(socket.getOutputStream(), true); 64 while (true) { 65 String line = null; 66 line = reader.readLine(); 67 if (line != null) { 68 area.append("客户端:"+line+"\n"); 69 } 70 71 } 72 } 73 74 }
客户端:
1 package socket; 2 import java.awt.BorderLayout; 3 import java.awt.event.ActionEvent; 4 import java.awt.event.ActionListener; 5 import java.io.BufferedReader; 6 import java.io.InputStreamReader; 7 import java.io.OutputStream; 8 import java.io.PrintStream; 9 import java.io.PrintWriter; 10 import java.net.Socket; 11 12 import javax.swing.JButton; 13 import javax.swing.JFrame; 14 import javax.swing.JPanel; 15 import javax.swing.JScrollPane; 16 import javax.swing.JTextArea; 17 import javax.swing.JTextField; 18 19 public class Client3 extends JFrame{ 20 21 static JTextArea area; 22 JTextField field; 23 JButton button; 24 static PrintWriter writer; 25 public Client3(){ 26 this.setTitle("客户端"); 27 this.setSize(400,500); 28 area = new JTextArea(25,30); 29 area.setEditable(false); 30 field = new JTextField(20); 31 button = new JButton("提交"); 32 JScrollPane sp = new JScrollPane(area); 33 JPanel panel = new JPanel(); 34 this.add(sp,BorderLayout.CENTER); 35 panel.add(field); 36 panel.add(button); 37 this.add(panel,BorderLayout.SOUTH); 38 this.setVisible(true); 39 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 40 41 button.addActionListener(new ActionListener(){ 42 43 @Override 44 public void actionPerformed(ActionEvent e) { 45 String text = field.getText(); 46 writer.println(text); 47 area.append("我:"+text+"\n"); 48 field.setText(""); 49 } 50 51 }); 52 } 53 public static void main(String[] args) throws Exception{ 54 Client3 c = new Client3(); 55 Socket socket = new Socket("127.0.0.1",8899); 56 OutputStream out = socket.getOutputStream(); 57 BufferedReader reader1 = new BufferedReader(new InputStreamReader(socket.getInputStream())); 58 writer = new PrintWriter(out,true); 59 System.out.println("已经成功和服务器连接..."); 60 while(true){ 61 String line = reader1.readLine(); 62 area.append("服务器:"+line+"\n"); 63 } 64 } 65 66 }
4、为多个客户端服务
当多个客户端连接到服务器时,要用到线程机制。每当程序建立一个新的套接字连接,也就是当调用accept时,将启动一个新的线程来处理服务器和该客户端之间的连接(一个服务器一个线程),而主程序将返回并等待下一个连接。即执行如下命令:
while(true){ Socket incoming=s.accpet(); Runnable r=new ThreadEchoHandler(incoming); Thread t=new Thread(r); t.start(); }
例子:
1 package socket; 2 import java.net.*; 3 import java.io.*; 4 import java.util.*; 5 public class server1 { 6 public static void main(String[] args)throws Exception{ 7 try{int i=1; 8 ServerSocket s=new ServerSocket(9000); 9 while(true){ 10 Socket incoming=s.accept(); 11 PrintWriter out=new PrintWriter(incoming.getOutputStream(),true); 12 Runnable r=new ThreadedEchoHandler(incoming); 13 Thread t=new Thread(r); 14 t.start(); 15 i++; 16 Scanner in=new Scanner(System.in); 17 while(in.hasNextLine()){ 18 System.out.println(in.nextLine()); 19 } 20 } 21 }catch(Exception e){e.printStackTrace();} 22 } 23 } 24 class ThreadedEchoHandler implements Runnable{ 25 private Socket s; 26 public ThreadedEchoHandler(Socket i){ 27 s=i; 28 } 29 public void run(){ 30 try{ 31 try{ 32 Scanner in=new Scanner(s.getInputStream()); 33 String str=null; 34 while(true){ 35 str=in.nextLine(); 36 System.out.println("客户端说:"+str); 37 } 38 } 39 finally{s.close();} 40 } 41 catch(Exception e){e.printStackTrace();} 42 } 43 }
1 package socket; 2 import java.net.*; 3 import java.io.*; 4 import java.util.*; 5 public class clent1 { 6 public static void main(String[] args)throws Exception{ 7 try{int i=1;Socket s=new Socket("localhost",9000); 8 while(true){ 9 PrintWriter out=new PrintWriter(s.getOutputStream(),true); 10 Runnable r=new ThreadedEchoHandler1(s); 11 Thread t=new Thread(r); 12 t.start(); 13 i++; 14 Scanner in=new Scanner(System.in); 15 while(in.hasNextLine()){ 16 System.out.println(in.nextLine()); 17 } 18 } 19 }catch(Exception e){e.printStackTrace();} 20 } 21 } 22 class ThreadedEchoHandler1 implements Runnable{ 23 private Socket s; 24 public ThreadedEchoHandler1(Socket i){ 25 s=i; 26 } 27 public void run(){ 28 try{ 29 try{ 30 Scanner in=new Scanner(s.getInputStream()); 31 String str=null; 32 while(true){ 33 str=in.nextLine(); 34 System.out.println("客户端"+s+"说:"+str); 35 } 36 } 37 finally{s.close();} 38 } 39 catch(Exception e){e.printStackTrace();} 40 } 41 }
5、半关闭
当我们在向服务器发送数据时,但并不知道要传输多少数据。在向文件写数据时,我们需要在写入后关闭文件即可。但是,如果关闭了一个套接字,那么与服务器就立刻断开了,因而无法读取服务器响应了。用半关闭的方法解决上述问题。可以通过关闭一个套接字的输出流来表示发送给服务器的请求数据已经结束,但是必须保持输入流处于打开状态。
socket.shutdownOutput();关闭输出流。
例如:客户端发送hello给服务器后,关闭输出流,服务器收到后,关闭输入流,等待5秒后,向客户端发送ECHO hello。