• 1. Socket编程Socket编程 InetAddress URL 网络应用工作流程 套接字 Socket工作步骤 Socket客户端服务器工作图 ServerSocket和Socket 创建客户端、服务器数据发送接收实例 扫描端口 设置连接超时时间和请求队列 设置数据缓冲区 创建多线程服务器 为客户的分配线程 使用JDK线程池方式创建多线程服务器 使用线程池注意点 UDP UDP工作步骤及通信 总结
  • 2. Socket编程Socke编程就是指网络编程。服务器:提供信息资源的计算机或服务器。 客户机:请求信息资源的计算机或程序。某些情况下当客户机和服务器互相请求,互相提供信息的时候,很难区分谁是服务器,谁是客户机。传输协议: TCP (Transmission Control Protocol,传输控制协议):“面向连接 ”,可靠的、基于字节流的传输层(Transport layer)通信协议 。如:打电话。 面向连接服务要经过三个阶段:数据传数前,先建立连接,连接建立后再传输数据,数据传送完后,释放连接。面向连接服务,可确保数据传送的次序和传输的可靠性。 UDP(User Datagram Protocol ,用户数据包协议):“面向非连接”,在正式通信前不必与对方先建立连接,不管对方状态就直接发送。如:写信。 无连接服务的特点是:无连接服务只有传输数据阶段。消除了除数据通信外的其它开销。 IP( Internet Protocol ):计算机网络相互连接进行通信而设计的协议。互联网地址或Internet地址。是用来唯一标识互联网上计算机的逻辑地址。 相当于电话号码。
  • 3. InetAddressInetAddress代表了一个网络目标地址对象,封装了主机名和数字类型的地址信息。InetAddress ia=InetAddress.getLocalHost(); //获得本机IP地址情况 System.out.println(ia.getHostName()); //得到主机名 System.out.println(ia.getHostAddress()); //得到主机地址 InetAddress ia2=InetAddress.getByName("T1"); //通过主机名获得信息 System.out.println(ia2.getHostAddress()); InetAddress ia3=InetAddress.getByAddress(new byte[]{(byte)192,(byte)168,0,86}); //通过主机IP获得信息 System.out.println(ia3.getHostName()); InetAddress ia4=InetAddress.getByName("www.163.com"); //根据域名到DNS查询Ip System.out.println("163 IP:"+ia4.getHostAddress());
  • 4. URL实现网页静态化类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。利用他可以获得网络资源,读取WWW服务器上的数据。这里我们将读取到的数据转换为流,然后保存到本地(类似网页静态化)。BufferedReader br=null;FileWriter fw=null; try { URL u=new URL("http://mail.163.com/"); //获得网络资源 InputStream fis=u.openStream(); //获得URL的输入数据流 InputStreamReader fr=new InputStreamReader(fis); br=new BufferedReader(fr); //读取并封装字符流 fw=new FileWriter("D://163.html"); //保存为文件 String s=null; while((s=br.readLine())!=null){ //操作 System.out.println(s); fw.write(s); } } catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}finally{ try { br.close();fw.flush();fw.close();} catch (IOException e) {e.printStackTrace();} }
  • 5. 网络应用工作流程网络应用工作流程我们用写信来举例: IP确定位置(相当于寄信的地址),端口确定该位置上的服务(具体是寄给哪个人的信)。 IP可以定位到具体的计算机,端口代表计算机上提供的具体服务。 IP与端口的组合得出一个套接字(将应用程序和端口连接起来),达到完全分辨网络上运行的程序的作用。
  • 6. TCP套接字为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。 Socket可以看作是通信连接两端的收发器。Socket可以看成在两个程序进行通讯连接中的一个端点。Socket所要完成的通信就是基于连接的通信,建立连接所需的程序分别运行在客户端和服务器端。 套接字连接就是客户端的套接字对象和服务器端的套接字对象通过输入/输出流连接在一起。套接字,Socket是网络上运行的程序之间双向通信链路的端点。
  • 7. TCP套接字Socket部分 通过将这3个参数结合起来,与一个Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。 在Java中,套接字(通信端点)被抽象为类,我们只需要创建Socket类的对象,就可以使用套接字。 Socket中有输入输出流对象,同时能实现服务器之间的数据传输。一个程序(Program B )将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket(Program A)中,使这段信息能传送到其他程序中。
  • 8. Socket工作步骤建立连接 1.在服务器端通过指定一个用来等待的连接的端口号创建一个 ServerSocket实例. 2.在客户端通过主机和端口号创建一个 socket实例,连到服务器上. 3.ServerSocket类的accept方法使服务器处于阻塞状态,等待用户请求 数据通信 建立socket连接后,还应该建立输入输出数据流,客户程序可以向Socket写入请求,服务器程序处理请求,并把处理结果通过Socket返回给客户端,完成通过虚拟通道的数据通信; 拆除连接 通信结束,将所建立的虚拟连接全部拆除。注意: 在服务器端指定的端口的时候,要注意该端口是否可用,是否已经被占用。 1~1023已经被系统占用了,应使用1024~65535之间的端口号,避免发生冲突。常用网络服务端口 : http://blog.chinaunix.net/u/29321/showart_376270.html
  • 9. Socket客户端服务器工作图
  • 10. ServerSocket和Socket创建ServerSocket代表服务器端套接字。 Socket代表客户端套接字。ServerSocket负责监听TCP连接请求,并为每个连接创建新的Socket实例。就是说,服务端需要同时处理ServerSocket和Socket,而客户端只需要使用Socket实例。创建ServerSocket(服务端套接字): ServerSocket(int port) 创建绑定到特定端口的服务器套接字。 ServerSocket(int port, int backlog)创建绑定到特定端口的服务器套接字,并指定最大连接数。创建Socket(客户端套接字): Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
  • 11. 发送和接受流式数据为了能随时监听客户端的请求,可以使用accept()方法用来接收客户机程序的连接请求,返回值是一个Socket类型的对象。程序运行到这里处于等待状态。Socket对象创建成功后,就可以在客户机与服务器之间建立一个连接,并通过这个连接在两个端口之间传送数据。 OutputStream os = socket.getOutputStream() 返回此套接字的输出流,发送数据。 InputStream is = socket.getInputStream() 返回此套接字的输入流,接收数据。 下面我们创建一个“回声”的例子: 客户端向服务器端发送数据,然后服务器端返回客户端发送的数据。每一个Socket存在时都占用一定的资源,在通信完成后,应该断开服务器端或客户端上运行的应用程序,即断开其虚拟连接并释放所占用的系统资源。 close()方法就能关闭当前套接字,释放资源。
  • 12. 创建服务器端创建服务器端public class EhcoServer { ServerSocket serverSocket; private final int PORT=8888; //端口 public EhcoServer() throws IOException{ serverSocket=new ServerSocket(PORT); //创建服务器端套接字 System.out.println("服务器启动。"); } //servic()方法 public static void main(String[] args) throws IOException { new EhcoServer().servic(); //启动服务 } }
  • 13. Servicepublic void servic(){ Socket socket=null; while(true){ try { socket = serverSocket.accept(); //等待并取出用户连接,并创建套接字 System.out.println("新连接,连接地址:"+socket.getInetAddress()+":"+socket.getPort()); //客户端信息 //输入流,读取客户端信息 BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream())); //输出流,向客户端写信息 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); PrintWriter pw=new PrintWriter(bw,true); //装饰输出流,true,每写一行就刷新输出缓冲区,不用flush String info=null; //接收用户输入的信息 while((info=br.readLine())!=null){ System.out.println(info); //输出用户发送的消息 pw.println("you said:"+info); //向客户端返回用户发送的消息,println输出完后会自动刷新缓冲区 if(info.equals("quit")){ //如果用户输入“quit”就退出 break; } } } //如果客户端断开连接,则应捕获该异常,但不应中断整个while循环,使得服务器能继续与其他客户端通信 catch (IOException e) {e.printStackTrace();}finally{ if(null!=socket){try {socket.close(); //断开连接 } catch (IOException e) {e.printStackTrace();}} } } }
  • 14. 创建客户端创建客户端public class EhcoClient { static final int PORT=8888; //连接端口 static final String HOST="192.168.0.5"; //连接地址 Socket socket; public EhcoClient() throws UnknownHostException, IOException{ socket=new Socket(HOST,PORT); //创建客户端套接字 } //send()方法 public static void main(String[] args) throws UnknownHostException, IOException { new EhcoClient().send(); } }
  • 15. sendpublic void send() { try { //客户端输出流,向服务器发消息 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //客户端输入流,接收服务器消息 BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter pw=new PrintWriter(bw,true); //装饰输出流,及时刷新 Scanner in=new Scanner(System.in); //接受用户信息 String msg=null; while((msg=in.next())!=null){ pw.println(msg); //发送给服务器端 System.out.println(br.readLine()); //输出服务器返回的消息 if(msg.equals("quit")){ break; //退出 } } } catch (IOException e) { e.printStackTrace(); }finally{ if(null!=socket){try { socket.close(); //断开连接 } catch (IOException e) { e.printStackTrace(); }} } }
  • 16. 测试可以看到,服务器端显示客户端套接字的地址和本机一致,端口为4371——由操作系统随即产生的,在客户端每创建一个Socket对象,操作系统就会为客户分派一个端口。 在编写Socket程序时,只需要显式的为服务器程序中的ServerSocket设置端口,客户端的端口不必关心。
  • 17. 扫描端口public static void main(String[] args) { String host = "192.168.0.5"; //要扫描的主机 int scope = 1024; //要扫描的端口范围 ScannerPort.scan(host, scope); //开始扫描 } public static void scan(String host, int scope) { if (scope <= 0 || scope > 65535) {System.out.println("端口必须在0-65536之间");return;} Socket socket = null; for (int port = 1; port < scope; port++) { try { socket = new Socket(host, port); System.out.println("端口:" + port + ",被占用"); } catch (UnknownHostException e) {System.out.println("主机无法识别!"); } catch (IOException e) {System.out.println("端口:" + port + "不能连接!"); } finally { if (null != socket) { try {socket.close();} catch (IOException e) {e.printStackTrace();} } } } }ScannerPort:
  • 18. 设置连接超时时间客户端的Socket在请求服务器连接时,可能需要等待一段时间,默认是一直等待永不超时,直到成功或异常。 超时:由于网络延迟,网络阻塞等原因,造成服务器并未及时响应客户端的一种现象 如果希望限定等待时间,则可以使用如下方式:socket=new Socket(); InetSocketAddress socketAddress=new InetSocketAddress(HOST,PORT); //创建Socket地址信息 socket.connect(socketAddress,3000); //设置超时时间为3000毫秒当超过连接时间,仍然未能与服务器连接上时,就会抛出:java.net.SocketTimeoutException连接超时异常。socket=new Socket(); InetSocketAddress socketAddress=new InetSocketAddress("www.baidu.com",80); socket.connect(socketAddress,1); //设为1增大超时几率
  • 19. 设置请求队列在服务器端监听客户连接请求时,可以设定客户端连接请求的队列长度.//代表创建2个队列长度的客户端请求。 serverSocket=new ServerSocket(PORT,2);默认操作系统会有一个队列的长度限制,一般为50。当队列请求大于最大容量时,服务器进程会拒绝新的连接请求。只有当服务器进程通过accpt()方法从队列中取出连接请求,腾出空位,队列才能加入其它新的连接请求。 注意:服务器指定的队列长度不能大于系统限定的最大长度或者小于等于0,这些情况下都会默认使用系统队列长度。
  • 20. 创建服务器端int port=8888; ServerSocket serverSocket; static int i=0; public MyServer() throws IOException{ serverSocket=new ServerSocket(port); //使用系统默认队列长度 50 System.out.println("服务启动。"); } public void service(){ Scanner i=new Scanner(System.in); String msg=i.next(); //让程序等待,服务一直存在 } public static void main(String[] args) throws InterruptedException, IOException { new MyServer(). service(); }这里没有调用服务器Socket的accept()方法,队列中的连接请求永远不会被取出。
  • 21. 创建客户端public static void main(String[] args) throws InterruptedException { final int backlog=100; String host="192.168.0.5"; int port=8888; Socket[] sockets=new Socket[backlog]; for(int i=0;i
  • 22. 测试测试默认系统队列长度:serverSocket=new ServerSocket(port,3);测试指定队列长度:
  • 23. 测试static int i=0; public void service(){ while(true){ Socket socket=null; try { i++; socket=serverSocket.accept(); //接受到此套接字的连接 System.out.println("Server> 新连接"+i+":"+socket.getInetAddress()+" "+socket.getPort()); } catch (IOException e) {e.printStackTrace(); }finally{if(null!=socket){try {socket.close();} catch (IOException e) {e.printStackTrace();}} } } }调用服务器端service()方法。做如上修改之后,服务器与端口绑定后,就在while循环中不断执行serverSocket.accept()方法,从队列中取出连接,腾出空位,以容纳新的连接请求。
  • 24. setSoTimeout设置服务器端超时时间当服务器调用accpet()方法,等待客户连接。 超过指定时间仍然没有客户端连接,则抛出java.net.SocketTimeoutException: Accept timed out异常。 设置为0,代表无限期等待。public static void main(String[] args) throws IOException, InterruptedException { ServerSocket serverSocket = new ServerSocket(8888); serverSocket.setSoTimeout(3000); //设置等待客户连接时间不超过3秒 serverSocket.accept(); }3秒钟之后抛出异常。
  • 25. setReceiveBufferSize()设置数据缓冲区对于传输块大的连续数据块,可以设置较大缓冲区,减少数据传输次数,提高效率。 而对于交互频繁单次数据量较小的通信(Telnet和网游),则应采用小的缓冲区,确保及时把小批量数据发送给对方。ServerSocket serverSocket2=new ServerSocket(); serverSocket2.setReceiveBufferSize(128*1024); //设置缓存区大小为128K serverSocket2.bind(new InetSocketAddress(8888)); 所以这里我们先构造了一个空的ServerSocket,然后设置选项,最后才绑定端口。 1.构造未绑定端口的的ServerSocket 2.设置选项 3.绑定端口serverSocket.setReceiveBufferSize(1024);当缓冲区大于64KB时,则必须按如下方式,在绑定端口前设置才有效:在ServerSocket中有些选项必须在服务器绑定端口之前设置才有效。
  • 26. 创建多线程服务器在前面我们第一个服务器与客户端通信的例子中,当服务器接受到客户端请求后,通信连接打开。 大家也许已经发现,当如果有多个客户端同时连接服务器的时候,这些客户就必须排队等待服务器响应。 服务器无法同时与多个客户通信。一个具有好的并发性的服务器必须符合两个条件: 1.能同时接收并处理多个客户端连接 2.能对每个客户,给出及时的响应 服务器同时处理的客户端连接数目越多,并且对每个客户做出响应速度越快,就表明并发性能越高。通过前面的学习,我们应该能想到,如果主线程负责接收客户连接,然后为每个客户分配一个工作线程,负责处理具体的客户连接。那么这个问题就可以得到很好的解决了。
  • 27. 为每个客户分配一个线程服务器主线程接收客户请求,每次接收到一个客户请求,就创建一个工作线程,负责与客户通信。public void service(){ while(true){ Socket socket=null; try { socket=serverSocket.accept(); Thread work=new Thread(new Handler(socket)); //为客户连接创建工作线程 work.start(); } catch (IOException e) { e.printStackTrace(); } } }Handler是一个线程执行,run()方法负责与单个客户通信。
  • 28. Handlerpublic class Handler implements Runnable { //负责与单个客户通信的线程 private Socket socket; BufferedReader br; BufferedWriter bw; PrintWriter pw; public Handler(Socket socket) { this.socket = socket; } public void initStream() throws IOException{ //初始化输入输出流对象方法 br=new BufferedReader(new InputStreamReader(socket.getInputStream())); bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); pw=new PrintWriter(bw,true); } public void run() { //执行的内容 try { initStream(); //初始化输入输出流对象 String info=null; while(null!=(info=br.readLine())){ System.out.println(info); pw.println("you said:"+info); //返回用户发送的消息 if(info.equals("quit")){ //如果用户输入“quit”就退出 break; } } } catch (IOException e) {e.printStackTrace();}finally{if(null!=socket){try {socket.close(); } catch (IOException e) {e.printStackTrace();}}} } }
  • 29. 使用线程池为每个线程分配一个新的工作线程有以下缺点: 1.创建,销毁线程的性能开销很大。 2.毫无控制创建大量线程,导致系统内存空间不足。线程池正是为了解决这些缺点。 线程池:在这个池里,预先创建了一些工作线程,它们不断从工作队列中取出任务,然后执行任务,当线程执行完一个任务时,就会继续执行工作队列中的下一个任务。线程池的优点包括: 1.减少创建,销毁线程的次数,每个工作线程都能重用,执行多个任务。 2.可根据系统的承载能力,调整线程池中的线程数目,防止消耗过量导致系统崩溃。
  • 30. JDK线程池java.util.concurrent包提供了现成的线程池实现。ExecutorService:代表一个线程池维护对象。 Executors.newFixedThreadPool(int nThreads):创建一个包含固定数目的线程池对象,空闲线程一直保留。 executorService.execute(Runnable command) : 执行给定的执行命令ExecutorService executorService; //线程池 final int POOL_SIZE=4; //单个处理器线程池工作线程数目 public EchoServer3() throws IOException{ //启动服务器 serverSocket=new ServerSocket(PORT); //创建线程池 //Runtime的availableProcessors()方法返回当前系统可用处理器的数目 //由JVM根据系统的情况来决定线程的数量 executorService=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*POOL_SIZE); System.out.println("服务器启动。"); }注意:工作线程并不是越多越好。适量的工作线程能提高服务器并发性能,但超出负荷反而会降低并发性能。使得多数客户无法快速得到服务器响应。
  • 31. 使用JDK线程池方式创建多线程服务器public void servic(){ while(true){ try { socket = serverSocket.accept(); //等待用户连接 executorService.execute(new Handler(socket)); //把执行交给线程池来维护 } catch (IOException e) { e.printStackTrace(); } } }这里使用线程池维护对象的execute方法来执行工作任务。 我们只需要规定线程池的最大线程数量,其他的就都不需要关心了。如果将线程池的数量设置为1: executorService=Executors.newFixedThreadPool(1); 然后开两个客户端,访问可以发现,和我们没有使用多线程没有区别,这正说明了线程池中只有一个线程。测试:
  • 32. 使用线程池注意点虽然使用线程池能大大提高服务器的并发性能,但他也存在一定的风险,比如多线程程序最容易产生的并发和死锁问题。1、如果任务A需要同步等待任务B的结果,那么A不适合加入到线程池的工作队列中。防止这些如A一样的线程因不能获得B的结果,可能导致死锁。所以不一定要避免这种情况的发生,即使得线程任务尽量职责单一。 2、假设工作线程执行过程中被阻塞(如等待接收用户输入数据,但用户一直不输入,他已经离开了),导致这个工作线程一直阻塞,不可用,名存实亡。如果加入线程池中的所有工作线程都处于这种阻塞状态,那么线程池就无法处理新任务。 解决策略: 调用ServerSocket的setSoTimeout()方法,设置等待客户连接超时时间。 对于每个客户连接的Socket,调用Socket的setSoTimeout方法,设置等待用户发数据的超时时间 3、如果任务抛出了异常或错误,但没有捕获处理,该线程就会异常终止。如果所有线程都异常终止,则导致线程池最终变空,无法处理新任务。这种情况称为线程泄露。 解决策略: 对于FixedThreadPool(固定线程池),如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。 所以可以认为不存在线程泄露。
  • 33. UDPUDP(User Datagram Protocol,用户数据报协议)是传输层的另一种协议,比TCP具有更高的传输速度,但不可靠。ServerSocket和Socket建立在TCP协议基础上。TCP协议是安全协议,可以保证数据安全有序(如丢失损坏时重发,恢复数据正确顺序)的到达接收方。 这种可靠传输是有代价的,建立销毁TCP连接花费很长时间,传输数据速度降低。如果双方通信时间很短,传输数据很少,建立和销毁TCP连接的代价就相当高。UDP发送的数据单元称为数据包。 当网络传输UDP数据包时,无法保证各个数据报一定到达目的地,也无法保证各个数据报按发送顺序达到目的地。 传输途中可能丢失数据。UDP只适用于一次传输少量的数据,或对数据可靠性要求不高的环境。
  • 34. UDP工作简介java.util.DatagramSocket负责接收和发送UDP数据包。java.util.DatagramPacket表示UDP数据包。TCP连接的两个Socket端点之间有一条虚拟通信线路。 UDP协议是无连接协议,客户端与服务器端不存在一对一对应关系,两者无需建立连接,就能交换数据包。接收数据:receive(DatagramPacket dst)。 发送数据:send(DatagramPacket dst)。下面,我们使用UDP实现我们之前客户端向服务器发送消息,服务器返回信息,客户端输出。
  • 35. 服务器端服务器端public class MyServer { int port=8888; DatagramSocket socket; public MyServer() throws SocketException{ socket=new DatagramSocket(port); //服务端DatagramSocket System.out.println("服务器启动。"); } public void service() throws IOException{ while(true){ DatagramPacket dp=new DatagramPacket(new byte[512],512); socket.receive(dp); //接收客户端信息 String msg=new String(dp.getData(),0,dp.getLength()); //获取客户端信息 System.out.println(dp.getAddress()+":"+dp.getPort()+">"+msg); dp.setData(("you said:"+msg).getBytes()); socket.send(dp); //回复数据 } } public static void main(String[] args) throws SocketException, IOException { new MyServer().service(); } }
  • 36. 客户端客户端public class MyClient { int remotePort=8888; //服务器端口 String remoteIp="192.168.0.5"; //服务器IP DatagramSocket socket; //客户端DatagramSocket public MyClient() throws SocketException{ socket=new DatagramSocket(); //随机可用端口,又称匿名端口 } public void send() throws IOException{ Scanner in=new Scanner(System.in); SocketAddress socketAddres=new InetSocketAddress(remoteIp,remotePort); //服务器端地址 while(true){ String s=in.next(); //获取用户输入 byte[] info=s.getBytes(); //创建数据包,指定服务器地址 DatagramPacket dp=new DatagramPacket(info,info.length,socketAddres); socket.send(dp); //向服务器端发送数据包 DatagramPacket inputDp=new DatagramPacket(new byte[512],512); socket.receive(inputDp); //接受服务器返回的信息 String msg=new String(inputDp.getData(),0,inputDp.getLength()); System.out.println(msg); if(s.equals("quit")){ break; } } socke.colse(); } }
  • 37. 测试结果测试结果可以看到,DatagramSocket之间的通信都是通过DatagramPacket(数据包)来承当数据传输载体的。 载体中指定了数据内容和要发送的字节数。 DatagramSocket通过载体,获得和发送数据。 而作为接收数据的DatagramPacket无需指定地址(IP和端口);发送数据的DatagramPacket则必须设定数据到达的目的地址。注意:UDP发送数据长度应视环境而定,如果网络非常不可靠,则要选择小的数据量;如果网络非常可靠,而且传输速度很快,就可以尽可能使用大数据包。 大多数网络下,8K(8192字节)是一个折中方案。
  • 38. UDP工作步骤及指定通信对象1.创建DatagramSocket 2.创建发送或接收的数据包(DatagramPacket),填入数据 3.发送(send)和接收(recevie)创建好的数据包,传递数据 3.关闭连接如果要对特定地址进行数据交换,如点对点通信。 可以使用connection(InetAddress host,int port)来限制当前DatagramSocket只对参数指定的主机和UDP端口收发数据。socket=new DatagramSocket(port); //服务端DatagramSocket SocketAddress socketAddress = new InetSocketAddress("192.168.0.5", 9999); // 指定连接地址 socket.connect(socketAddress);在服务端指定通信对象客户端使用随机端口和指定9999端口测试:socket=new DatagramSocket(); //随机可用端口 System.out.println(socket.getLocalPort()); //当前端口
  • 39. 总结本节课对Socket及其相关知识进行了学习和了解。 并通过以不同方式创建TCP程序,优化了网路编程的任务处理。 同时在最后学习了UDP协议,及创建基于UDP协议的测试程序。