1,多线程上传图片。
import java.io.*;
import java.net.*;
//定义客户端
class PicClient{
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("文件超过5M,上传失败");
return;
}
Socket s=new Socket("192.168.1.7",10007);
FileInputStream fis=new FileInputStream(file);
OutputStream out=s.getOutputStream();
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
out.write(buf,0,len);
}
s.shutdownOutput(); //告诉服务端数据已写完。
InputStream in=s.getInputStream();
byte[] bufIn=new byte[1024];
int num=in.read(bufIn);
System.out.println(new String(bufIn,0,num));
fis.close();
s.close();
}
}
//服务服务端,为了让多个客户端同时并发访问服务端,这是需要报服务器做成多线程。把客户端要在服务端执行的代码放入Run方法。
class PicThread implements Runnable{
private Socket s;
PicThread(Socket s){
this.s=s;
}
public void run(){
int count=1;
String ip=null;
try{
ip=s.getInetAddress().getHostAddress();
System.out.println(ip+"...connected");
InputStream in=s.getInputStream();
File file=new File(ip+".jpg"); //文件名存为IP地址。
//文件存在,则在文件名后面加(1)。
while(file.exists())
file=new File(ip+"("+(count++)+")"+".jpg");
FileOutputStream fos=new FileOutputStream(file);
byte[] buf=new byte[1024];
int len=0;
while((len=in.read(buf))!=-1){
fos.write(buf,0,len);
}
OutputStream out=s.getOutputStream();
out.write("上传成功".getBytes());
fos.close();
s.close();
}
catch (Exception e){
throw new RuntimeException(ip+"...上传失败");
}
}
}
class PicServer{
public static void main(String[] args)throws Exception {
ServerSocket ss=new ServerSocket(10007);
while(true){
Socket s=ss.accept();
new Thread(new PicThread(s)).start();
}
//ss.close();
}
}2,用户登录的TCP实现。
客户端通过键盘录入用户名,服务端对这个用户名进行校验。如果该用户存在,在服务端显示xxx,已登录,并在客户端显示xxx,欢迎光临。如果该用户不存在,在服务端显示xxx,尝试登陆,并在客户端显示xxx,该用户不存在。import java.io.*;
import java.net.*;class LoginClient{public static void main(String[] args)throws Exception{Socket s=new Socket("192.168.1.8",10008);BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));PrintWriter out=new PrintWriter(s.getOutputStream(),true);BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));
//最多就登陆三次,在客户端进行校验。for(int x=0;x<3;x++){String line=bufr.readLine();if(line==null)break;out.println(line);String info=bufIn.readLine();System.out.println("info:"+info);
//如果收到服务器返回的信息包含“欢迎”,则结束。通过设置结束标记来判断。if(info.contains("欢迎"))break;}bufr.close();s.close();}}
//服务器端使用多线程。class UserThread implements Runnable{private Socket s;UserThread(Socket s){this.s=s;}public void run(){String ip=s.getInetAddress().getHostAddress();System.out.println(ip+"...connected");try{
//登陆三次在服务器端进行限制。for(int x=0;x<3;x++){BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));String name=bufIn.readLine();if(name==null)break;BufferedReader bufr=new BufferedReader(new FileReader("User.txt"));PrintWriter out=new PrintWriter(s.getOutputStream(),true);String line=null;boolean flag=false;while((line=bufr.readLine())!=null){if(line.equals(name)){flag=true;break;}}
//设置一个标记判断来向客户端反馈不同的信息。if(flag){System.out.println(name+",已登录");out.println(name+",欢迎光临");break;}else {System.out.println(name+",尝试登录");out.println(name+",用户名不存在");}}s.close();}catch (Exception e) {throw new RuntimeException(ip+"校验失败");}}}class LoginServer{public static void main(String[] args)throws Exception{ServerSocket ss=new ServerSocket(10008);while(true){Socket s=ss.accept();new Thread(new UserThread(s)).start();}}}
3, 浏览器服务器。
自定义一个服务器。
ServerSocket ss=new ServerSocket(11000);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));PrintWriter out=new PrintWriter(s.getOutputStream(),true);out.println("客户端,你好");s.close();ss.close();控制台得到如下数据:即浏览器向服务器发送的信息,即HTTP消息头。
GET / HTTP/1.1Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/QVOD, application/QVOD,Accept-Language: zh-cn //通过浏览器Internet选项语言设置可以更改。User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; Foxy/1; Shuame)Accept-Encoding: gzip, deflateHost: 192.168.1.8:11000Connection: Keep-Alive //请求完保持连接,closed请求完自动关闭连接。利用得到的消息头我们可以模拟一个自定义的浏览器,向tomcat服务器请求数据。
Socket s=new Socket("192.168.1.8",8080);PrintWriter out=new PrintWriter(s.getOutputStream(),true);out.println("GET /myweb/demo.html HTTP/1.1");out.println("*/*");out.println("Accept-Language: zh-cn");out.println("Host:192.168.1.8:8080");out.println("Connection: closed");
以上为 HTTP 请求消息头字段。下面是在控制台打印服务器返回的结果。out.println();BufferedReader bufr=new BufferedReader(new InputStreamReader(s.getInputStream()));String line=null;while((line=bufr.readLine())!=null){System.out.println(line);}s.close();
由打印结果可知,tomcat服务器同样会为自定义浏览器服务,同时会反馈HTTP的响应消息头。
因此我们利用自定义的浏览器就可以随便访问任何网站,百度,新浪等。
当然我们也可以把浏览器做成图形界面,但唯一和缺憾是,无法解析标记语言和脚本语言,这也是浏览器的强大之处。
而在图形化界面中传入地址后,我们需要进行切割来获取对应的信息。
ta.setText("");String url=tf.getText();// http://192.168.1.8:8080/myweb/demo.htmlString protocol=url.substring(0,4);String str1=url.substring(url.indexOf("//")+2);String str2=str1.substring(0,str1.indexOf("/"));String[] arr=str2.split(":");String host=arr[0];int port=Integer.parseInt(arr[1]);String path=str1.substring(str1.indexOf("/"));
Socket s=new Socket(host,port);PrintWriter out=new PrintWriter(s.getOutputStream(),true);out.println("GET "+path+" "+protocol+"/1.1");out.println("*/*");out.println("Accept-Language: zh-cn");out.println("Host:"+str2);out.println("Connection: closed");如此切割非常繁琐,所以API中为我们提供了封装好的类,来进行以上操作,即URL类。
URL,统一资源定位符 ,UniformResourceLocator
URI,通用资源标识符,Uniform Resource Identifier
URI范围要更大些,包括条形码等。
URL对象的常用方法:URL url=new URL("http://192.168.1.9:8080/myweb/demo.html?name=zhangsan&age=33");System.out.println("getProtocol():"+url.getProtocol());System.out.println("getHost():"+url.getHost());System.out.println("getPort():"+url.getPort());System.out.println("getPath():"+url.getPath()); /myweb/demo.htmlSystem.out.println("getFile():"+url.getFile()); / myweb/demo.html?name=zhangsan&age=33"System.out.println("getQuery():"+url.getQuery()); name=zhangsan&age=33
//用?分割,用& 多信息连接,信息都是以键值对存在的。
利用URL类实现浏览器原理。ta.setText("");URL url=new URL(tf.getText());URLConnection conn=url.openConnection();InputStream in=conn.getInputStream();
byte[] buf=new byte[1024];int len=in.read(buf);ta.setText(new String(buf,0,len));
此时控制台输出的结果没有响应头,因为Socket用的TCP协议,工作在传输层,而URL工作在应用层,接收到的数据进行了拆包,所以响应头没有了。
注意两个小知识点:
1,new Socket(),没有指定主机和端口,这是它提供了一个方法connect方法,接收SocketAddress套节字即IP+端口。InetSocketAddress继承SocketAddress抽象类。
2,new ServerSocket(端口,backlog)。backlog是队列长度,即服务器的最大连接数。
4,域名解析。
C:\WINDOWS\system32\drivers\etc\hosts 用于本地存放ip 地址和域名的对应关系。
域名访问时先走本地,再DNS 。输入ip 地址则直接找对应的主机,不走DNS。