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编程传递文件