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(); } }
运行结果为:
以上代码只能实现从客户端发送消息到服务端的功能,如果客户端希望可以收到服务端的反馈信息,那应该怎么做呢?
分析:如果需要收到信息,那客户端应该也要获取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(); } }
运行结果为:
练习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 方法都打开一个新的连接。 */