socket编程学习笔记

socket编程:

1、网络基础知识

  两台计算机通过网络进行通信,首先两台计算机要有唯一的标识,即唯一的IP地址。其次他们要有共同的语言用来交流即协议。再者,每套主机要有相应的端口号。 

  TCP/IP协议:
  --TCP/IP协议是目前世界上应用最为广泛的协议,是以TCP和IP为基础的不同层次上多个协议的集合,也称:TCP/IP协议族 或 TCP/IP协议栈
  --TCP的全称:Transmission Control Protocol 传输控制协议
  --IP的全称:Internet Protocol 互联网协议

  TCP/IP模型
    一般分为五层:
      5-应用层:HTTP 超文件传输协议 FTP 文件传输协议 SMTP 简单邮件传送协议 Telnet远程登录服务
      4-传输层:TCP/IP协议
      3-网络层
      2-数据链路层
      1-物理层:网线,双绞线,网卡 

  IP地址---《探索Linux的网络世界》为实现网络中不同计算机之间的通信,每台机器都必须有一个唯一的标识---IP地址   
  IP地址格式:数字型,如:192.168.0.1
  IP地址的长度:32位的二进制  

端口
  端口号
    1、用于区分不同应用程序
    2、端口号范围为0~65535,其中0~1023为系统所保留
    3、IP地址和端口号就组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP的基础。
    4、常见的一些协议使用的端口号
      http:80 ftp:21 telnet:23

  Java中的网络支持
  针对网络通信的不同层次,Java提供的网络功能有四大类:
    1、InetAddress类:用于标识网络上的硬件资源。
    2、URL:统一资源定位符 通过URL可以直接读取或写入网络上的数据。
    3、Sockets(TCP编程):使用TCP协议实现网络通信的Socket相关的类。
    4、Datagram(UDP编程):使用UDP协议,将数据保存在数据报中,通过网络进行通信。

2、InetAdress类
  java.net.InetAddress
  所有已实现的接口:Serializable
  直接已知子类:Inet4Address,Inet6Address
  没有构造方法。
(1)InetAddress类用于标识网络上的硬件资源,表示互联网协议(IP)地址。
栗子又来啦!!!

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

会输出计算机名,IP地址,以及字节数组形式的IP。

输出:(计算机名纯属自己杜撰,如有雷同,纯属巧合,IP地址也是)

计算机名:MyTeam
IP地址:192.16.54.161
字节数组形式的IP:[-80,16,54,-70]
MyTeam/192.16.54.161
计算机名:MyTeam.it2102.fhjd.com.cn
IP地址:192.16.54.161

3、URL
  3-1、URL(Uniform Resource Locator)统一资源定位符,表示Internet上某一资源的地址
小栗子来理解url的常用方法:

 1 import java.net.URL;
 2     import java.net.MalformedURLException;
 3     //URL的常用方法
 4     public class UURL{
 5         public static void main(String[] args){
 6             try{
 7                 //创建一个URL实例
 8                 URL study=new URL("http://www.study.com");
 9                 //?后面表示参数,#后面表示锚点
10                 URL url=new URL(study,"/index.html?username=tom#test");
11                 System.out.println("协议:"+url.getProtocol());//输出:协议:http
12                 System.out.println("主机:"+url.getHost());
13                 //如果未指定端口号,则根据协议的不同,使用默认的端口号,此时getPort()方法的返回值为-1.
14                 System.out.println("端口:"+url.getPort());
15                 System.out.println("文件路径:"+url.getPath());//输出:文件路径:/index.html
16                 System.out.println("文件名:"+url.getFile());//输出:/index.html?username=tom
17                 System.out.println("相对路径:"+url.getRef());//输出:test
18                 System.out.println("查询字符串:"+url.getQuery());//输出:username=tom
19             }catch(MalformedURLException e){
20                 e.printStackTrace();
21             }
22             
23         }
24     }

3-2、使用URL读取网页内容
  1、通过URL对象的openStream()方法可以得到指定资源的输入流。
  2、通过流来读取、访问网络上的数据。
!!!看这里

 1 import java.net.MalformedURLException;
 2     import java.net.URL;
 3     import java.io.InputStream;
 4     import java.io.InputStreamReader;
 5     import java.io.BufferedReader;
 6     import java.io.IOException;
 7     public class Test{
 8         public static void main(String[] args){
 9             try{
10                 //创建一个URL实例
11                 URL url=new URL("http://www.baidu.com");
12                 //通过URL的openStream方法获取URL对象所表示的资源的字节输入
13                 InputStream is=url.openStream();
14                 //将字节输入流转换为字符输入流
15                 InputStreamReader isr=new InputStreamReader(is,"utf-8");
16                 //为流添加缓冲,提高读取的效率
17                 BufferedReader br =new BufferedReader(isr);
18                 String data=br.readLine();//读取数据
19                 while(data!=null){//循环读取数据
20                     System.out.println(data);//输出数据
21                     data=br.readLine();
22                     }
23                     br.close();
24                     isr.close();
25                     is.close();
26             }catch(MalformedUrlException e){
27                 e.printStackTrace();
28             }catch(IOException e){
29                 e.printStackTrace();
30             }
31         }
32     }

4、Socket通信
  TCP协议是面向连接、可靠的、有序的、以字节流的方式发送数据。
  基于TCP协议实现网络通信的类:客户端的Socket类,服务器端的ServerSocket类
Socket通信模型
    Server
建立服务器倾听socket                                                           Client
等待并接收连接请求------------建立连接---------创建连接socket向服务端发送请求
接收请求后创建连接socket
--------------------------------------------------------------------------------------------------------------------
InputStream----------------------------------------------OutputStream
                                                  开始通信
OutputStream-------------------------------------------InputStream
-------------------------------------------------------------------------------------------------------------------
关闭socket及相关资源--------结束通信-------------关闭socket及相关资源

Socket通信实现步骤
  1、分别在服务器端和客户端创建ServerSocket和Socket
  2、打开连接到Socket的输入/输出流,进行数据通信
  3、按照协议对Socket进行读/写操作
  4、关闭输入输出流,关闭Socket

ServerSocket:
  ServerSocket类位于java.net包中,直接已知子类是SQLServerSocket,此类实现服务器套接字。
  服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
  服务器套接字的实际工作由SocketImpl类的实例执行。应用程序可以更改创建套接字实现的套接字工厂来配置它自身,从而创建适合本地防火墙的套接字。

构造方法:
  ServerSocket(int port)----创建绑定到特定端口的服务器套接字。
方法摘要:
  accept()-----侦听并接受到此套接字的连接。
  bind()-------将ServerSocket绑定到特定地址(IP地址和端口号)
  close()------关闭此套接字。
  getChannel()------返回与此套接字关联的唯一ServerSocketChannel对象(如果有)
  getInetAddress()------返回此服务器套接字的本地地址。
  getLocalPort()--------返回此套接字在其上侦听的端口。
  getLocalSocketAddress()----返回此套接字绑定的端点的地址,如果尚未绑定则返回null。
  getReceiveBufferSize()---获取此ServerSocket的SO_RCVBUF选项的值,该值是将用于从此ServerSocket接受的套接字的建议缓冲区大小。

Socket:
  位于java.net包中,直接已知子类是:SSLSocket,此类实现客户端套接字(也可以就叫“套接字”)。套接字
是两台机器间通信的端点。
  套接字的实际工作由SocketImpl类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,创建适合本地防火墙的套接字。

构造方法:
  Socket(InetAddress address,int port)----创建一个流套接字并将其连接到指定IP地址的指定端口号。
  Socket(String host,int port)-----创建一个流套接字并将其连接到指定主机上的指定端口号。
方法摘要:
  bind(SocketAddress bindpoint)---将套接字绑定到本地地址。
  close()-----关闭此套接字。
  connect(SocketAddress endpoint,int timeout)------将此套接字连接至服务器,并制定一个超时值。
  getChannel()--------返回与此数据报套接字关联的唯一的SocketChannel对象(如果有)。
  getInetAddress()-----返回套接字连接的地址。
  getInputStream()-----返回此套接字的输入流。
  getKeepAlive()-------测试是否启用SO_KEEPALIVE.
  shutdownInput()------此套接字的输入流置于“流的末尾”。
  shutdownOutput()------禁用此套接字的输出流。

实现用户登录的步骤:
  基于TCP协议的Socket通信,实现用户登录
服务器端:

 1 import java.net.ServerSocket;
 2     import java.net.Socket;
 3     import java.io.InputStream;
 4     import java.io.InputStreamReader;
 5     import java.io.IOException;
 6     import java.io.OutputStream;
 7     import java.io.PrintWriter;
 8     public class Server{
 9         public static void main(String[] args){
10             try{
11                 //1、创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
12                 ServerSocket serverSocket=new ServerSocket(8888);
13                 //2、调用accept()方法开始监听,等待客户端的连接
14                 System.out.println("***服务器即将启动,等待客户端的连接***");
15                 serverSocket.accept();
16                 Socket socket=serverSocket.accept();
17                 //3、获取输入流,并读取客户端信息
18                 InputStream is=socket.getInputStream();//字节输入流
19                 InputStreamReader isr=new InputStreamReader(is);//将字节流转换为字符流
20                 BufferedReader br=new BufferedReader(isr);//为输入流添加缓冲
21                 String info=null;
22                 while((info=br.readLine())!=null){//循环读取客户端的信息
23                     System.out.println("我是服务器,客户端说:"+info);
24                 }
25                 socket.shutdownInput();//关闭输入流
26                 //4、获取输出流,响应客户端的请求
27                 OutputStream os=socket.getOutputStream();
28                 PrintWriter pw=new PrintWriter(os);//包装为打印流
29                 pw.write("欢迎您!");
30                 pw.flush();//调用flush()方法将缓冲输出
31                 //5、关闭相关资源
32                 br.close();
33                 isr.close();
34                 is.close();
35                 pw.close();
36                 os.close();
37                 socket.close();
38                 serverSocket.close();
39             }catch(IOException e){
40                 e.printStackTrace();
41             }
42         }
43     }

客户端:

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

注:服务器必须早于客户端启动

5、使用多线程实现多客户端的通信
基本步骤:
  1、服务器端创建ServerSocket,循环调用accept()等待客户端连接
  2、客户端创建一个socket并请求和服务器端连接
  3、服务器端接受客户端请求,创建socket与该客户建立专线连接
  4、创建连接的两个socket在一个单独的线程上对话
  5、服务器端继续等待新的连接
服务器线程处理类:

 1 import java.net.Socket;
 2     import java.io.InputStream;
 3     import java.io.IOException;
 4     import java.io.BufferedReader;
 5     import java.io.InputStreamReader;
 6     import java.io.OutputStream;
 7     import java.io.PrintWriter;
 8 
 9     public class ServerThread extends Thread{
10         //与本线程相关的Socket
11         Socket socket=null;
12 
13         public ServerThread(Socket socket){
14             this.socket=socket;
15         }
16 
17         //线程执行的操作,响应客户端的请求
18         public void run(){
19             InputStream is=null;
20             InputStreamReader isr=null;
21             BufferedReader br=null;
22             OutputStream os=null;
23             PrintWriter pw=null;
24             try{
25                 //获取输入流,读取客户端的信息
26                 is=socket.getInputStream();//字节输入流
27                 isr=new InputStreamReader(is);//将字节流转换为字符流
28                 br=new BufferedReader(isr);//为输入流添加缓冲
29                 String info=null;
30                 while((info=br.readLine())!=null){//循环读取客户端的信息
31                     System.out.println("我是服务器,客户端说:"+info);
32                 }
33                 socket.shutdownInput();//关闭输入流
34                 //获取输出流,响应客户端的请求
35                 os=socket.getOutputStream();
36                 pw=new PrintWriter(os);//包装为打印流
37                 pw.write("欢迎您!");
38                 pw.flush();//调用flush()方法将缓冲输出
39             }catch(IOException e){
40                 e.printStrackTrace();
41             }finally{
42                 //关闭相关资源
43                 try{
44                     if(pw!=null) pw.close();
45                     if(os!=null) os.close();
46                     if(br!=null) br.close();
47                     if(isr!=null) isr.close();
48                     if(is!=null) is.close();
49                     if(socket!=null) socket.close();
50                 }catch(IOException e){
51                     e.printStrackTrace();
52                 }
53             }
54         }
55     }

服务器端:

 1     import java.net.ServerSocket;
 2     import java.net.Socket;
 3     import java.io.InputStream;
 4     import java.io.InputStreamReader;
 5     import java.io.IOException;
 6     import java.io.OutputStream;
 7     import java.io.PrintWriter;
 8     public class Server{
 9         public static void main(String[] args){
10             try{
11                 //1、创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
12                 ServerSocket serverSocket=new ServerSocket(8888);
13                 Socket socket=null;
14                 //记录客户端的数量
15                 int count=0;
16                 System.out.println("***服务器即将启动,等待客户端的连接***");
17                 //循环监听等待客户端的连接
18                 while(true){
19                 //调用accept()方法开始监听,等待客户端的连接
20                 socket=serverSocket.accept();
21                 //启动一个线程,通过当前线程与客户端进行通信
22                 ServerThread serverThread=new ServerThread(socket);
23                 //启动线程
24                 serverThread.start();
25 
26                 count++;//统计客户端的数量
27                 System.out.println("客户端的数量:"+count);
28                 InetAddress address=socket.getInetAddress();
29                 System.out.println("当前客户端的IP:")+address.getHostAddress());
30                 }
31             }catch(IOException e){
32                 e.printStackTrace();
33             }
34         }
35     }

客户端:

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

6、基于UDP的socket编程
  UDP协议(用户数据报协议)是无连接的、不可靠的、无序的。
  UDP协议以数据报作为数据传输的载体
  使用UDP进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后再将数据报发送出去。
  相关操作类:
  DatagramPacket:表示数据报包,数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序传递做出保证。

构造方法摘要:
  DatagramPacket(byte[] buf, int length):构造DatagramPacket,用来接收长度为length的数据包。
  DatagramPacket(byte[] buf,int length,InetAddress address,int port):构造数据报包,用来将长度为length的包发送至指定主机上的指定端口号。
  DatagramPacket(byte[] buf,int offset,int length):构造DatagramPacket,用来接收长度为length的包,在缓冲区指定了偏移量为offset
  DatagramSocket:进行端到端通信的类,表示用来发送和接收数据报包的套接字。
  数据报套接字是包投递服务的发送或接收点。每个在数据报套接字上发送或接收的包都是自由的。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序。

构造方法摘要:
  DatagramSocket():构造数据报套接字并将其绑定到本地主机上任何可用的端口。
  DatagramSocket(DatagramSocketImpl impl):创建带有指定DatagramSocketImpl的未绑定数据报套接字。
  DatagramSocket(int port,InetAddress laddr):创建数据报套接字,将其绑定到指定的本地地址。
方法摘要:
  close():关闭此数据报套接字。
  bind():将此DatagramSocket绑定到特定的地址和端口。
  getLocalAddress():返回此套接字连接的地址
  getpPort():返回此套接字的端口
  send():从此套接字发送数据报包
  receive():从此套接字接收数据报包

基于UDP的用户登录:
服务器端实现步骤:

 1 import java.io.IOException;
 2     import java.net.DatagramPacket;
 3     import java.net.DatagramSocket;
 4     import java.net.InetAddress;
 5 
 6     public class UDPServer{
 7         public static void main(String[] args) throws IOException{
 8         /*
 9          *接受客户端发送的数据
10          */
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         InetAddress address=packet.getAddress();
27         int port=packet.getPort();
28         byte[] data2="欢迎您!".getBytes();
29         //2、创建数据报、包含响应的数据信息
30         DatagramPacket packet=new DatagramPacket(data2,data2.length,address,port)
31         //3、响应客户端
32         socket.send(packet2);
33         //4、关闭资源
34         socket.close();
35         }
36     }

客户端实现步骤:

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

重点:Socket通信,基于TCP的Socket通信
经验和技巧:
  多线程的优先级,是否关闭输入流和输出流,使用TCP通信传输对象,socket编程传递文件



 

猜你喜欢

转载自www.cnblogs.com/gree/p/8821750.html