黑马程序员---网络编程(TCP传输)

TCP传输是面向连接的,所以必须建立服务端和客户端。

客户端:

class  TcpClient
{
	public static void main(String[] args) throws Exception
	{
		//1.创建客户端的Socket服务,指定目的主机和端口
		Socket s=new Socket("127.0.0.1",10003);

		//2.为了发送数据,应该获取Socket流中的输出流
		OutputStream out=s.getOutputStream();

		out.write("tcp chuanshu ".getBytes());
	
		s.close();

	}
}

 

服务端:

/*
服务端
1。建立服务端的Socket服务,ServerSocket,并监听一个端口
2.获取链接过来得客户端对象
通过ServerSocket的accept方法,没有建立链接的话,会等待,因为该方法是阻塞式的
3.客户端如果发送过来数据,那么服务端要使用对应的客户端对象,并且获取到该客户端对象
的读取流来读取发送过来的数据
4.关闭服务端

*/
class TcpServer
{
	public static void main(String[] args)throws Exception
	{
		//1.建立Socket服务并监听一个端口
		ServerSocket ss=new ServerSocket(10003);

		//2.通过accept方法获取链接过来的客户端对象
		Socket s=ss.accept();
		String ip=s.getInetAddress().getHostAddress();
		System.out.println(ip+".......connected");


		//3.获取客户端发过来的数据,需要使用客户端的读取流来读取数据
		InputStream in=s.getInputStream();
		byte[] buf=new byte[1024];
		int len=in.read(buf);

		System.out.println(new String(buf,0,len));

		s.close();
	}

}


运行结果为:

黑马程序员---网络编程(TCP传输)

 

以上代码只能实现从客户端发送消息到服务端的功能,如果客户端希望可以收到服务端的反馈信息,那应该怎么做呢?

分析:如果需要收到信息,那客户端应该也要获取Socket对象的读取流,同时服务端应该获取Socket对象的输出流

此时,代码表示如下:

客户端:

class  TcpClient2
{
	public static void main(String[] args) throws Exception
	{
		//创建Socket对象,指定目的主机和端口
		Socket s=new Socket("127.0.0.1",10004);

		//为了发送数据,应该获取Socket流中的输出流
		OutputStream out=s.getOutputStream();

		out.write("服务端,你好啊".getBytes());

		//为了获取到服务端发送过来得数据,应该获取Socket流中的读取流
		InputStream in=s.getInputStream();

		byte[] buf=new byte[1024];

		int len=in.read(buf);

		System.out.println(new String(buf,0,len));

		s.close();

	}
}

服务端:

class TcpServer2
{
	public static void main(String[] args)throws Exception
	{
		//创建ServerSocket服务,并且监听一个端口
		ServerSocket ss=new ServerSocket(10004);

		//通过accept方法,获取链接过来的客户端对象
		Socket s=ss.accept();

		String ip=s.getInetAddress().getHostAddress();

		System.out.println(ip+"......connected");

		//获取客户端发过来的数据,需要使用客户端对象的读取流来读取数据
		InputStream in=s.getInputStream();
		byte[] buf=new byte[1024];

		int len=in.read(buf);
		System.out.println(new String(buf,0,len));

		//为了给客户端回馈信心,需要使用客户端对象的输出流来写入信息
		OutputStream out=s.getOutputStream();
		out.write("收到信息。。".getBytes());

		s.close();


	}

}

运行结果为:

黑马程序员---网络编程(TCP传输)

练习1:需求为建立一个文本转换服务器

/*
需求:建立一个文本转换服务器
要求是:客户端给服务端发送文本,服务端会将文本转换成大写然后返回给客户端,
而且客户端可以不断的进行文本转换,当客户端输入over时停止转换
*/

import java.net.*;
import java.io.*;

//对于客户端来说,源为键盘,目的为Socket流


class  TcpClient3
{
	public static void main(String[] args) throws Exception
	{
		//建立Socket服务,指定目的主机和目的端口
		Socket s=new Socket("127.0.0.1",10005);

		//为了发送数据,应该获取Socket流中的输出流,为了提高效率,使用缓冲区
		BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		//为了接收从服务端发送过来的数据,还应该获取Socket流中的读取流
		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

		//为了满足客户端可以不断进行文本转换的需求,应该可以键盘输入
		//为了提高效率,使用缓冲区
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));
		String line=null;
		while((line=bufr.readLine())!=null)
		{
			if("over".equals(line))
				break;
			bufOut.write(line);
			bufOut.newLine();//如果不写入换行符,导致服务端的read方法一直阻塞
			bufOut.flush();//写入缓冲区的数据需要刷新
			String str=bufIn.readLine();
			System.out.println(str);
		}

		bufr.close();
		s.close();

	}
}

//对于服务端来说,源为Socket流,目的为Socket流
class TcpServer3
{
	public static void main(String[] args)throws Exception
	{
		//建立ServerSocket服务,并且指定监听一个端口
		ServerSocket ss=new ServerSocket(10005);

		//通过accept方法,获取链接过来的客户端对象
		Socket s=ss.accept();

		String ip=s.getInetAddress().getHostAddress();

		System.out.println(ip+"....connected");

		//为了获取到客户端发送过来的数据,并且进行处理,需要使用客户端对象的读取流对象来读取数据
		//同时将处理好的数据返回给客户端
		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

		BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		String line=null;
		while((line=bufIn.readLine())!=null)
		{
			System.out.println(line);
			bufOut.write(line.toUpperCase());
			bufOut.newLine();//如果不写入换行符,导致客户端的read方法一直阻塞
			bufOut.flush();
		}

	}

}
/*
思考:1.为什么输入over以后,客户端结束,服务端也跟着结束了呢?
在客户端中,跳出循环以后执行bufr.close()语句和s.close()语句,
在close()方法中实际上是向Socket流中加了一个-1(结束标记)
服务器端的read方法读到-1,表示已经读到了流末尾,因此循环结束
2.使用PrintWriter/PrintStream简化代码
BufferedWriter bfwOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));//对Socket的输出流使用缓冲技术
bfwOut.write(line);
bfwOut.newLine();//如果不写入行终止符,导致服务端的bfrIn.readLine()一直堵塞
bfwOut.flush();//写入缓冲区的数据需要刷新
可以简化成:
PrintWriter pw=new PrintWriter(Socket.getOutputStream(),true)//自动刷新
pw.println(line.toUpperCase());
*/
 

TCP复制文件:

import java.net.*;
import java.io.*;

/*
对于客户端,源为硬盘上的文件,目的为Socket流
*/

class TcpClient4 
{
	public static void main(String[] args) throws Exception
	{
		
		//源为硬盘上的文件,需要建立IO流来读取文件上的内容
		BufferedReader bufr=new BufferedReader(new FileReader("D:\\j\\day22\\AwtDemo.java"));

		//目的为Socket流,需要建立Socket服务,指定目的主机和端口,并获取Socket对象的输出流
		Socket s=new Socket("127.0.0.1",10006);
		BufferedWriter bufOut=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

		//逐行读取文件上的内容并发送到服务端
		String line=null;
		while((line=bufr.readLine())!=null)
		{
			bufOut.write(line);
			bufOut.newLine();
			bufOut.flush();
		}

		bufr.close();
		s.close();

	}
}

/*
对于服务端,源为Socket流,目的为硬盘上的文件
*/
class TcpServer4 
{
	public static void main(String[] args) throws Exception
	{
		//源为Socket流,所以要获取客户端对象,来获取客户端的读取流
		ServerSocket ss=new ServerSocket(10006);

		Socket s=ss.accept();
		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

		//目的为硬盘上的文本文件,建立IO流将数据写入文件中
		BufferedWriter bufw=new BufferedWriter(new FileWriter("123.txt"));

        String ip=s.getInetAddress().getHostAddress();
		bufw.write(ip+".....connected");
		
		String line=null;
		while((line=bufIn.readLine())!=null)
		{
			bufw.write(line);
			bufw.newLine();
			bufw.flush();
		}
		bufw.close();

		
	}
}

上传图片:

import java.io.*;
import java.net.*;
/*
需求为上传图片

*/

class  PicClient
{
	public static void main(String[] args) throws Exception
	{
		//建立Socket服务,并且指定目的主机和端口
		Socket s=new Socket("127.0.0.1",10007);

		//对图片进行读取,需要字节流读取对象,为了提高效率,进行转换,使用缓冲区
		BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("IMG_0696.jpg"));

		//将读取到的图片信息发送给服务端,应该获取Socket对象的输出流
		BufferedOutputStream bufOut=new BufferedOutputStream(s.getOutputStream());

		byte[] buf=new byte[1024];

		int len=0;

		while((len=bufis.read(buf))!=-1)
		{
			//写入信息是不包含结束标记,所以服务端没有办法结束读取的循环,会一直阻塞
			bufOut.write(buf,0,len);
			bufOut.flush();

		}

		//为了告诉服务端,数据已经传输完毕
		s.shutdownOutput();

		//为了获取到服务端发送过来的反馈信息,需要获取Socket对象的读取流
		BufferedInputStream bufIn=new BufferedInputStream(s.getInputStream());

		byte[] buf2=new byte[1024];
		int len2=bufIn.read(buf2);
		System.out.println(new String(buf2,0,len2));
		bufis.close();
		s.close();

	}
}


class PicServer
{
	public static void main(String[] args)throws Exception
	{
		//建立ServerSocket对象,并且监听一个端点
		ServerSocket ss=new ServerSocket(10007);

		//通过accept方法,获取客户端对象的读取流,读取客户端发送过来的信息
		Socket s=ss.accept();

		BufferedInputStream bufIn=new BufferedInputStream(s.getInputStream());

		//将客户端发送过来的数据存储在文件中,需要输出流对象
		BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("123.jpg"));

		byte[] buf=new byte[1024];

		int len=0;
		while((len=bufIn.read(buf))!=-1)
		{
			bufos.write(buf,0,len);
			bufos.flush();
		}
		
		//将反馈信息返回给客户端,需要Socket对象的输出流
		BufferedOutputStream bufOut=new BufferedOutputStream(s.getOutputStream());

		bufOut.write("已经收到信息".getBytes());
		bufOut.flush();
		
		bufos.close();
		s.close();
		ss.close();
		

	}
}

客户端并发上传图片:

import java.io.*;
import java.net.*;

/*
为了让多个客户端可以同时并发的访问服务端
那么服务端最好就是将每个客户端封装到一个单独的线程中去,这样,
就可以同时处理多个客户端请求

如何定义多线程呢?
只要明确了每一个客户端要在服务端执行的代码即可
将该代码存入run方法中即可。
*/
class  PicClient2
{
	public static void main(String[] args) throws Exception
	{
		
		if(args.length!=1)
		{
			System.out.println("请选择一个JPG文件");
			return;
		}
		//使用了主函数传值
		File file=new File(args[0]);

		if(!(file.exists()&&file.isFile()))
		{
			System.out.println("您输入的不存在或不是文件");
			return;
		}

		if(!file.getName().endsWith(".jpg"))
		{
			System.out.println("您上传的文件类型错误,请上传Jpg文件");
			return;
		}

		if(file.length()>1024*1024*5)
		{
			System.out.println("您上传的文件过大,请上传小于5兆的文件");
			return;
		}

		//建立Socket服务,并且指定目的主机和端口
		
		Socket s=new Socket("127.0.0.1",10007);
		

		//对图片进行读取,需要字节流读取对象,为了提高效率,使用缓冲区
		BufferedInputStream bufis=new BufferedInputStream(new FileInputStream(file));

		//将读取到的图片信息发送给服务端,应该获取Socket对象的输出流
		BufferedOutputStream bufOut=new BufferedOutputStream(s.getOutputStream());

		byte[] buf=new byte[1024];

		int len=0;

		while((len=bufis.read(buf))!=-1)
		{
			//写入信息是不包含结束标记,所以服务端没有办法结束读取的循环,会一直阻塞
			bufOut.write(buf,0,len);
			bufOut.flush();

		}

		//为了告诉服务端,数据已经传输完毕
		s.shutdownOutput();

		//为了获取到服务端发送过来的反馈信息,需要获取Socket对象的读取流
		BufferedInputStream bufIn=new BufferedInputStream(s.getInputStream());

		byte[] buf2=new byte[1024];
		int len2=bufIn.read(buf2);
		System.out.println(new String(buf2,0,len2));
		bufis.close();
		s.close();

	}
}

class PicThread implements Runnable
{
	private Socket s;
	PicThread(Socket s)
	{
		this.s=s;
	}

	public void run()
	{
		String ip=s.getInetAddress().getHostAddress();
		int count=1;
		try
		{
			BufferedInputStream bufIn=new BufferedInputStream(s.getInputStream());

			//根据获取到的IP地址来存为文件名。如果该IP已经发送过一次文件,则会出现覆盖现象
			
			File file=new File(ip+"("+count+")"+".jpg");
			//判断该IP是否已经发送过文件,如果有,则建立该IP(2)号文件,依次类推
			while(file.exists())
				file=new File(ip+"("+(++count)+")"+".jpg");

			//将客户端发送过来的数据存储在文件中,需要输出流对象
			BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream(file));

			byte[] buf=new byte[1024];

			int len=0;
			while((len=bufIn.read(buf))!=-1)
			{
				bufos.write(buf,0,len);
				bufos.flush();
			}
			
			//将反馈信息返回给客户端,需要Socket对象的输出流
			BufferedOutputStream bufOut=new BufferedOutputStream(s.getOutputStream());

			bufOut.write("已经收到信息".getBytes());
			bufOut.flush();
			
			bufos.close();
			s.close();
		}
		catch (Exception e)
		{
			throw new RuntimeException("上传失败了");
		}
	}

}



class PicServer2
{
	public static void main(String[] args)throws Exception
	{
		//建立ServerSocket对象,并且监听一个端点
		ServerSocket ss=new ServerSocket(10007);
		while(true)
		{

			//通过accept方法,获取客户端对象的读取流,读取客户端发送过来的信息
			Socket s=ss.accept();
			
			new Thread(new PicThread(s)).start();
		}
	}
}
 

TCP客户端并发登陆:

/*
客户端通过键盘录入用户民
服务端对这个用户名进行校验

如果该用户存在,在服务端显示xxx,登录成功
并在客户端显示欢迎登陆系统

如果该用户不存在,在服务端显示xxx,尝试登陆.
并在客户端显示xxx,该用户不存在.

最多登录三次(登录成功不在登录,不成功依然可以登录)
*/


/*
思想:
 对于每个连接成功,进行登录的用户
 服务端对每个用户单独用一个线程执行相同的动作(即三次验证)
*/
import java.net.*;
import java.io.*;



class LoginClient
{
	public static void main(String[] args) throws Exception
	{
		//首先创建Socket对象,并且指定目的主机和端口
		Socket s=new Socket("127.0.0.1",10008);
		
		//为了获取到键盘录入的用户名信息,需要键盘录入
		BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));

		//为了将信息发送给服务端,需要获取Socket对象的输出流
		PrintWriter pw=new PrintWriter(s.getOutputStream(),true);

		//为了获取到服务端发送过来得反馈信息,需要获取Socket对象的读取流
		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

		//将键盘录入的信息,发送给服务端,同时获取反馈信息
		for(int x=0;x<3;x++)
		{
			String user=bufr.readLine();
			//如果输入的信息为空,则跳出循环
			if(user==null)
				break;
			else pw.println(user);

			String info=bufIn.readLine();
			System.out.println("info:"+info);
			//如果已经登陆成功,则跳出循环
			if(info.contains("欢迎"))
				break;
			
		}

		bufr.close();
		s.close();
			
	}
}


class LoginThread implements Runnable
{

	private Socket s;
	LoginThread(Socket s)
	{
		this.s=s;
	}

	public void run()
	{
		String ip=s.getInetAddress().getHostAddress();
		try
		{
			for(int x=0;x<3;x++)
			{
				//为了获取到客户端发送过来的数据,需要Socket对象的读取流
				BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
				String user=bufIn.readLine();

				//为了将收到的数据和文件里面的比对,应该建立读取流读取文件里面的数据
				BufferedReader bufr=new BufferedReader(new FileReader("user.txt"));
				
				
				//为了将反馈信息发送给客户端,需要Socket对象的输出流
				PrintWriter pw=new PrintWriter(s.getOutputStream(),true);

				String line=null;
				
				//定义一个标记,标记用户是否登陆
				boolean flag=false;

				while((line=bufr.readLine())!=null)
				{
					if(line.equals(user))
					{
						flag=true;
						break;
					}
				}

				if(flag==true)
				{
					System.out.println(user+"登陆成功");
					pw.println("欢迎登陆系统");
					break;
				}
				else
				{
					System.out.println(user+"尝试登陆");
					pw.println("您所输入的用户名不存在,请重新输入");
				}
			}

			s.close();
			
		}

		catch (Exception e)
		{
			throw new RuntimeException(ip+"校验失败");
		}
	}
		
}

class LoginServer
{
	public static void main(String[] args)throws Exception
	{
		//建立ServerSocket服务,并且监听一个端口
		ServerSocket ss=new ServerSocket(10008);

		while(true)
		{
			Socket s=ss.accept();

			new Thread(new LoginThread(s)).start();
		}
	}
}

如果将浏览器作为客户端,自定义服务端.

则服务端应该:
import java.net.*;
import java.io.*;

class ServerDemo 
{
	public static void main(String[] args) throws Exception
	{
		ServerSocket ss=new ServerSocket(11000);

		Socket s=ss.accept();
		System.out.println(s.getInetAddress().getHostAddress());

		InputStream in=s.getInputStream();
		byte[] buf=new byte[1024];
		int len=in.read(buf);

		System.out.println(new String(buf,0,len));


		PrintWriter out=new PrintWriter(s.getOutputStream(),true);

		out.println("<font color=‘red‘ size=‘7‘> 客户端你好</font>");
		
		s.close();
		ss.close();
	}
}

在自己的浏览器中输入:http://IP地址:端口号
或者可以在cmd中使用telnet口令,telnet协议为TCP/IP协议中的一员,是Internet远程登陆服务的标准协议和主要方式
在终端使用者的电脑上使用telnet程序,用它链接到服务器,终端使用者可以在telnet中输入命令,这些命令会在服务器上运行,
就像直接在服务器控制台上一样
 

自定义浏览器。Tomcat服务器

import java.net.*;
import java.io.*;


class  MyIE
{
	public static void main(String[] args) throws Exception
	{
		Socket s=new Socket("192.168.123.7",8080);

		PrintWriter out=new PrintWriter(s.getOutputStream(),true);

		out.println("GET /myweb/myIe.html HTTP/1.1");
		out.println("Accept:*/*");//表示可以接收所有类型
		out.println("Accept-Language: zh-CN");//文字为简体中文
		out.println("Accept-Encoding: gzip,deflate,sdch");
		out.println("Host: 192.168.123.7:11000");
		out.println("Connection: keep-alive");

		out.println();
		out.println();

		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

		String line=null;
		while((line=bufIn.readLine())!=null)
		{
			System.out.println(line);
		}

		s.close();
	}
}


/*
192.168.123.7
GET / HTTP/1.1
Host: 192.168.123.7:11000
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/28.0.1500.95 Safari/537.36 SE 2.X MetaSr 1.0
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
*/

自定义图形化浏览器

import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;

class  MyIEByGUI
{
	private Frame f;
	private Button but;
	private TextField tf;
	private TextArea ta;

	MyIEByGUI()
	{
		init();
	}

	public void init()
	{
		f=new Frame("my IE");
		f.setBounds(300,200,600,500);
		f.setLayout(new FlowLayout());

		but=new Button("转到");
		tf=new TextField(40);
		ta=new TextArea(20,50);

		f.add(tf);
		f.add(but);
		f.add(ta);

		myEvent();


		f.setVisible(true);

	}

	public void myEvent()
	{
		f.addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		});

		but.addMouseListener(new MouseAdapter()
		{
			public void mouseClicked(MouseEvent e)
			{
				try
				{
					showHtml();
				}
				catch (Exception ex)
				{
					throw new RuntimeException();
				}
				
			}
		});

	}

	public void showHtml()throws Exception
	{
		ta.setText("");
		String url=tf.getText();//http://192.168.123.7:8080/myweb/myIe.html

		int index1=url.indexOf("//")+2;

		int index2=url.indexOf("/",index1);
		String str=url.substring(index1,index2);
		String path=url.substring(index2);
		
		String[] str2=str.split(":");
		int port=Integer.parseInt(str2[1]);
		Socket s=new Socket(str2[0],port);

		PrintWriter out=new PrintWriter(s.getOutputStream(),true);

		out.println("GET "+path+" HTTP/1.1");
		out.println("Accept:*/*");//表示可以接收所有类型
		out.println("Accept-Language: zh-CN");//文字为简体中文
		out.println("Accept-Encoding: gzip,deflate,sdch");
		out.println("Host: 192.168.123.7:11000");
		out.println("Connection:close");

		out.println();
		out.println();

		BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));

		String line=null;
		while((line=bufIn.readLine())!=null)
		{
			ta.append(line+"\r\n");
		}

		s.close();
	}






	public static void main(String[] args) 
	{
		
		new MyIEByGUI();

	}
}

URL---URLConnection

类URL代表一个统一资源定位符,它是指向互联网资源的“指针”。
URL可以根据一个路径字符串将路径封装成对象,也可以使用协议,主机,端口,文件来封装对象
import java.net.*;

class URLDemo 
{
	public static void main(String[] args) throws MalformedURLException
	{
		URL url=new URL("http://192.168.123.7:8080/myweb/myIe.html");

		//获取协议
		System.out.println("getProtocol():"+url.getProtocol());
		//获取该URL的主机名
		System.out.println("getHost():"+url.getHost());
		//获取该URL的路径部分
                System.out.println("getPath():"+url.getPath());
		//获取该URL的端口号
		System.out.println("getPort():"+url.getPort());
		//获取该URL的文件名,打印结果为:/myweb/myIe.html
		System.out.println("getFile():"+url.getFile());
		//获取该URL的查询部,打印结果为:null
		System.out.println("getQuery():"+url.getQuery());


	}
}

/*
创建URL对象是,会出现MalformedURLException异常,防止传进去的路径为乱码,不是正确的路径

如果,URL url=new URL("http://192.168.123.7:8080/myweb/myIe.html?name=haha&age=30");
此时,如果获取文件名应该为/myweb/myIe.html?name=haha&age=30
      如果获取查询部应该为name=haha&age=30

如果路径中不指定端口号,则端口号为-1,当URL类解析完url之后,会将port设为默认端口80

*/

URLConnection
import java.net.*;
import java.io.*;


class URLConnectionDemo 
{
	public static void main(String[] args) throws Exception
	{
		URL url=new URL("http://192.168.123.7:8080/myweb/myIe.html");
		URLConnection conn=url.openConnection();

		InputStream in=conn.getInputStream();
		byte[] buf=new byte[1024];

		int len=in.read(buf);
		System.out.println(new String(buf,0,len));
	}
}


/*
public URLConnection openConnection()
返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。 
每次调用此 URL 的协议处理程序的 openConnection 方法都打开一个新的连接。 
*/

黑马程序员---网络编程(TCP传输)

上一篇:go 判断数组下标是否存在


下一篇:EOJ 1444 In Danger