Java Socket

重要的Socket API:

 java.net.Socket继承于java.lang.Object,有八个构造器,其方法并不多,下面介绍使用最频繁的三个方法,

  . Accept方法用于产生”阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。”阻塞”是一个术语,它使程序运行暂时”停留”在这个地方,直到一个会话产生,然后程序继续;通常”阻塞”是由循环产生的。

  . getInputStream方法获得网络连接输入,同时返回一个InputStream对象实例。 
  . getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。

  注意:其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。

      JAVA API1.6 中文.CHM

InetAddress:

 1 import java.net.Inet4Address;
 2 import java.net.UnknownHostException;
 3 import java.util.Arrays;
 4 
 5 public class InetAddress {
 6 
 7     public static void main(String[] args) throws UnknownHostException{
 8         // 获取本机的InetAddress实例
 9         Inet4Address address=(Inet4Address) Inet4Address.getLocalHost();
10         System.out.println("计算名:"+address.getHostName());
11         System.out.println("IP地址:"+address.getHostAddress());
12         byte[] bytes=address.getAddress();//获取字节数组形式的IP地址
13         System.out.println("字节数组形式的IP:"+Arrays.toString(bytes));
14         System.out.println("直接输出InetAdress对象:"+address);//直接输出InetAdress对象
15         
16         //根据机器名获取InetAdress实例
17         Inet4Address address2=(Inet4Address) Inet4Address.getByName("DESKTOP-8J7SNCN");
18         System.out.println("计算名:"+address2.getHostName());
19         System.out.println("IP地址:"+address2.getHostAddress());
20         
21         
22         //根据IP地址获取InetAdress实例
23         Inet4Address address3=(Inet4Address) Inet4Address.getByName("169.254.38.168");
24         System.out.println("计算名:"+address3.getHostName());
25         System.out.println("IP地址:"+address3.getHostAddress());
26         
27     }
28 
29 }

url:

 1 import java.net.MalformedURLException;
 2 import java.net.URL;
 3 
 4 public class Url {
 5     public static void main(String[] args) {
 6         try{
 7             //创建一个URL实例
 8             URL imooc=new URL("http://www.imooc.com");
 9             //?后面表示参数,#后面表示锚点
10             URL url = new URL(imooc,"/index.html?username=tom#test");
11             System.out.println("协议 :"+ url.getProtocol());
12             System.out.println("主机 :"+ url.getHost());
13             //如果未指定端口号,则使用协议默认端口号
14             System.out.println("端口号 :"+ url.getPort());
15             System.out.println("文件路径 :"+ url.getPath());
16             System.out.println("文件名称 :"+ url.getFile());
17             System.out.println("相对路径 :"+ url.getRef());
18             System.out.println("查询字符串:"+ url.getQuery());
19         }catch(MalformedURLException e){
20             e.printStackTrace();
21         }
22 
23     }
24 
25 }
 1 import java.io.BufferedReader;
 2 import java.io.IOException;
 3 import java.io.InputStream;
 4 import java.io.InputStreamReader;
 5 import java.net.MalformedURLException;
 6 import java.net.URL;
 7 
 8 
 9 /*
10  * 使用URL读取网页内容
11  */
12 public class Url2 {
13     public static void main(String[] args) {        
14         try {
15             //创建URL实例
16             URL url=new URL("http://www.baidu.com");
17             //通过URL的openStream方法获取URL对象所表示的资源的字节输入流
18             InputStream is=url.openStream();
19             //将字节输入流转换为字符输入流
20             InputStreamReader isr=new InputStreamReader(is,"utf-8");
21             //为字节输入流添加缓冲
22             BufferedReader br=new BufferedReader(isr);
23             String data=br.readLine();
24             while(data!=null){
25                 System.out.println(data);
26                 data=br.readLine();
27             }
28             br.close();
29             isr.close();
30             is.close();
31         } catch (MalformedURLException e) {
32             e.printStackTrace();
33         } catch (IOException e) {
34             e.printStackTrace();
35         }
36     }
37 
38 }

重点!!!

通过 Socket 实现 TCP 编程

构造ServerSocket

构造方法:

  ServerSocket() ~创建非绑定服务器套接字。

  ServerSocket(int port) ~创建绑定到特定端口的服务器套接字。

  ServerSocket(int port, int backlog) ~利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。

  ServerSocket(int port, int backlog, InetAddress bindAddr) ~使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。

1.1 绑定端口

除了第一个不带参数的构造方法以外, 其他构造方法都会使服务器与特定端口绑定, 该端口有参数 port 指定. 例如, 以下代码创建了一个与 80 端口绑定的服务器:

1 ServerSocket serverSocket = new ServerSocket(80);  

1.2 设定客户连接请求队列的长度

当服务器进程运行时, 可能会同时监听到多个客户的连接请求. 例如, 每当一个客户进程执行以下代码:

1 Socket socket = new Socket("www.javathinker.org", 80); 

就意味着在远程 www.javathinker.org 主机的 80 端口上, 监听到了一个客户的连接请求.

例子:

服务器端:

 1 import java.io.IOException;
 2 import java.net.Inet4Address;
 3 import java.net.ServerSocket;
 4 import java.net.Socket;
 5 
 6 /*
 7  * 基于TCP协议的Socket通信,实现用户登陆
 8  * 服务器端
 9  */
10 public class Server {
11 
12     public static void main(String[] args) {
13         try {
14             // 创建一个服务器端的Socket,即ServerSocket,指定绑定的端口,并监听此端口
15             @SuppressWarnings("resource")
16             ServerSocket serverSocket=new ServerSocket(8888);
17             //记录客户端的数量
18             int count =0 ;
19             System.out.println("***服务器即将启动,等待客户端的连接***");
20             Socket socket=null;
21             while (true){
22                 //调用accept()方法开始监听,等待客户端的连接
23                 socket=serverSocket.accept();    
24                 //创建一个新的线程
25                 ServerThread serverThread =new ServerThread(socket);
26                 //启动线程
27                 serverThread.start();
28                 
29                 count++;//统计客户端的数量
30                 System.out.println("客户端的数量"+count);
31                 Inet4Address address=(Inet4Address) Inet4Address.getLocalHost();
32                 System.out.println("当前客户端的IP:"+address.getHostAddress());
33             }
34 
35         } catch (IOException e) {
36             // TODO Auto-generated catch block
37             e.printStackTrace();
38         }
39 
40     }
41 
42 }

客户端:

 1 import java.io.BufferedReader;
 2 import java.io.IOException;
 3 import java.io.InputStream;
 4 import java.io.InputStreamReader;
 5 import java.io.OutputStream;
 6 import java.io.PrintWriter;
 7 import java.net.Socket;
 8 import java.net.UnknownHostException;
 9 
10 /*
11  * 客户端
12  */
13 public class Client {
14     public static void main(String[] args) {
15         try {
16             //1.创建客户端Socket,指定服务器地址和端口
17             Socket socket =new Socket("localhost",8888);
18             //2.获取输出流,向服务器发送信息
19             OutputStream os =socket.getOutputStream();//字节输出流
20             PrintWriter pw= new PrintWriter(os);//将输出流包装为打印流
21             pw.write("用户名:admin;密码:123");
22             pw.flush();
23             socket.shutdownOutput();//关闭输出流
24             
25             //3.获取输入流,并读取服务器端的响应信息
26             InputStream is =socket.getInputStream();
27             BufferedReader br=new BufferedReader(new InputStreamReader(is));
28             String info=null;
29             while((info=br.readLine())!=null){
30                 System.out.println("我是客户端,服务器说: "+info);
31             }
32             socket.shutdownInput();//关闭输入流
33             
34             //4.关闭资源
35             br.close();
36             is.close();
37             pw.close();
38             os.close();
39             socket.close();
40         } catch (UnknownHostException e) {
41             e.printStackTrace();
42         } catch (IOException e) {
43             e.printStackTrace();
44         }
45     }
46 }

多线程:

 1 import java.io.BufferedReader;
 2 import java.io.IOException;
 3 import java.io.InputStream;
 4 import java.io.InputStreamReader;
 5 import java.io.OutputStream;
 6 import java.io.PrintWriter;
 7 import java.net.Socket;
 8 
 9 /*
10  * 服务器端线程服处理类
11  */ 
12 public class ServerThread extends Thread {
13     //和本线程相关的Socket
14     Socket socket=null;
15     public ServerThread(Socket socket){
16         this.socket=socket;
17     }
18     //线程执行的操作,响应客户端的请求
19     public void run(){
20         InputStream is = null;
21         InputStreamReader isr = null;
22         BufferedReader br = null;
23         OutputStream os = null;
24         PrintWriter pw = null;
25         try{
26             //获取一个输入流,并读取客户端所发送的登陆信息
27             is=socket.getInputStream();//字节输入流
28             isr =new InputStreamReader(is);//将字节流转换为字符流
29             br=new BufferedReader(isr);//为输入流添加缓冲
30             String info=null;
31             while((info=br.readLine())!=null){
32                 System.out.println("我是服务器,客户端说: "+info);
33             }
34             socket.shutdownInput();//关闭输入流
35     
36             //获取输入流,响应客户端的请求
37             os=socket.getOutputStream();
38             pw=new PrintWriter(os);
39             pw.write("欢迎您!");
40             pw.flush();//调用flush()方法将缓冲输出
41             socket.shutdownOutput();//关闭输出流
42         }catch(IOException e){
43             e.printStackTrace();
44         }finally{
45             //关闭资源
46             try {
47                 if(pw!=null){
48                     pw.close();
49                 }
50                 if(os!=null){
51                     os.close();
52                 }
53                 if(br!=null){
54                     br.close();
55                 }
56                 if(isr!=null){
57                     isr.close();
58                 }
59                 if(socket!=null){
60                     socket.close();
61                 }
62             } catch (IOException e) {
63                 // TODO Auto-generated catch block
64                 e.printStackTrace();
65             }
66         }
67     }
68     
69     
70 }

 通过 Socket 实现 UDP 编程

服务器端:

 1 import java.io.IOException;
 2 import java.net.DatagramPacket;
 3 import java.net.DatagramSocket;
 4 import java.net.Inet4Address;
 5 
 6 /*
 7  * 服务器端,基于UDP的用户登陆
 8  */
 9 public class UDPserver {
10     public static void main(String[] args) throws IOException {
11         //1.创建服务器端DatagramSocket,指定端口
12         DatagramSocket socket = new DatagramSocket(8800);
13         //2.创建数据报,用于接收客户端发送的数据
14         byte[] data =new byte[1024];//创建字节数组,指定接受的数据包的大小
15         DatagramPacket packet=new DatagramPacket(data, data.length);
16         //3.接受客户端发送的数据
17         System.out.println("***服务器已经启动,等待客服端发送数据***");
18         socket.receive(packet);//此方法在接收到数据报之前会一直阻塞
19         //4.读取数据
20         String info=new String(data,0,packet.getLength());
21         System.out.println("我是服务器,客户端说"+info);
22         /*
23          * 向服务器端响应数据
24          */
25         //1定义客户端的地址、端口、数据
26         Inet4Address address=(Inet4Address) packet.getAddress();
27         int port=packet.getPort();
28         byte[] data2="欢迎您!".getBytes();
29         //2.创建数据报,包含响应的数据信息
30         DatagramPacket packet2=new DatagramPacket(data2, data2.length, address,port);
31         //3.响应客户端
32         socket.send(packet2);
33         //4.关闭资源
34         socket.close();
35         
36         
37     }
38 }

客户端:

 1 import java.io.IOException;
 2 import java.net.DatagramPacket;
 3 import java.net.DatagramSocket;
 4 import java.net.Inet4Address;
 5 
 6 /*
 7  * 客户端
 8  */
 9 public class UDPclient {
10     public static void main(String[] args) throws IOException {
11         /*
12          * 向服务器端发送数据
13          */
14         //1.定义服务器的地址、端口号、数据
15         Inet4Address address=(Inet4Address) Inet4Address.getByName("localhost");
16         int port=8800;
17         byte[] data="用户名“admin;密码123".getBytes();
18         //2.创建数据报,包发送的数据信息
19         DatagramPacket packet=new DatagramPacket(data, data.length, address,port);
20         //3.创建DatagramPacket对象
21         DatagramSocket socket =new DatagramSocket();
22         //4.向服务器端发送数据报
23         socket.send(packet);
24         
25         /*
26          * 接受服务器端响应的数据
27          */
28         //1.创建数据报,用于接受服务器端相应的数据
29         byte[] data2=new byte[1024];
30         DatagramPacket packet2=new DatagramPacket(data2, data2.length);
31         //2.接受服务器相应的数据
32         socket.receive(packet2);
33         //3.读取数据
34         String reply =new String (data2,0,packet2.getLength());
35         System.out.println("我是客户端,服务器说: "+reply);
36         //4.关闭资源
37         socket.close();
38     }
39 }

TCP与UDP基本区别 

  1.基于连接与无连接
  2.TCP要求系统资源较多,UDP较少; 
  3.UDP程序结构较简单 
  4.流模式(TCP)与数据报模式(UDP); 
  5.TCP保证数据正确性,UDP可能丢包 
  6.TCP保证数据顺序,UDP不保证 
  
UDP应用场景:
  1.面向数据报方式
  2.网络数据大多为短消息 
  3.拥有大量Client
  4.对数据安全性无特殊要求
  5.网络负担非常重,但对响应速度要求高
 
具体编程时的区别
   1.socket()的参数不同 
 2.UDP Server不需要调用listen和accept 
 3.UDP收发数据用sendto/recvfrom函数 
 4.TCP:地址信息在connect/accept时确定 
 5.UDP:在sendto/recvfrom函数中每次均 需指定地址信息 
 6.UDP:shutdown函数无效

基于上述不同,UDP和TCP编程步骤也有些不同,如下:

TCP: TCP编程的服务器端一般步骤是: 

  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt(); * 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind(); 
  4、开启监听,用函数listen(); 
  5、接收客户端上来的连接,用函数accept(); 
  6、收发数据,用函数send()和recv(),或者read()和write(); 
  7、关闭网络连接; 
  8、关闭监听; 

TCP编程的客户端一般步骤是: 

  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
  4、设置要连接的对方的IP地址和端口等属性; 
  5、连接服务器,用函数connect(); 
  6、收发数据,用函数send()和recv(),或者read()和write(); 
  7、关闭网络连接;

UDP:与之对应的UDP编程步骤要简单许多,分别如下: 

  UDP编程的服务器端一般步骤是: 
  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind(); 
  4、循环接收数据,用函数recvfrom(); 
  5、关闭网络连接; 

UDP编程的客户端一般步骤是: 

  1、创建一个socket,用函数socket(); 
  2、设置socket属性,用函数setsockopt();* 可选 
  3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选 
  4、设置对方的IP地址和端口等属性; 
  5、发送数据,用函数sendto(); 
  6、关闭网络连接;

原文地址:https://www.cnblogs.com/superslow/p/9010811.html