黑马程序员_java之Socket网络编程

来源:互联网 发布:庄子 知乎 编辑:程序博客网 时间:2024/05/21 14:05
------- android培训、java培训、期待与您交流! ----------
网络架构:

C/S:Client/Server

客户端,服务端。

特点:

1,需要在客户端和服务端都需要按照编写的软件。

2,维护较麻烦。

好处:可以减轻服务端压力 ,如网游。

B/S:Browser/Server

浏览器,服务端。

1,客户端不用单独编写软件,因为客户端用的就是浏览器。

2,对于软件升级,只要考虑服务端即可。

弊端:所有的程序都运行在服务端,客户端的浏览器解析能力较弱。


网络模型 :

OSI模型

TCP/IP参考模型

网络通讯要素:

IP地址

端口号

传输协议


IP地址:InetAddress

网络中设备的标识

不易记忆,可用主机名

本地回环地址:127.0.0.1主机名:localhost

端口号:

用于标识进程的逻辑地址,不同进程的标识

有效端口:0-65535,其中0-1024系统使用或保留端口

传输协议:

通讯的规则,常见有:TCP,UDP

UDP

将数据及源和目的封装成数据包中,不需要建立连接

每个数据报的大小在限制在64k内

因无连接,是不可靠协议

不需要建立连接,速度快

TCP

建立连接,形成传输数据的通道。

在连接中进行大数据量传输

通过三次握手完成连接,是可靠协议

必须建立连接,效率会稍低


Socket就是为网络服务提供的一种机制

通信的两端都有Socket。

网络通信其实就是Socket间的通信。

数据在两个Socket间通过IO传输。


UDP传输

数据在两个Socket间通过IO传输。

DatagramSocket与DatagramPacket

建立发送端,接收端。

建立数据包。

调用Socket的发送接收方法。

关闭Socket。

发送端与接收端是两个独立的运行程序。

发送端

在发送端,要在数据包对象中明确目的地

IP及端口。

DatagramSocketds = new DatagramSocket();

byte[]by = “hello,udp”.getBytes();

DatagramPacketdp = new DatagramPacket(by,0,by.length,

              InetAddress.getByName(“127.0.0.1”),10000);

ds.send(dp);

ds.close();

接收端

在接收端,要指定监听的端口。

DatagramSocketds = new DatagramSocket(10000);

byte[]by = new byte[1024];

DatagramPacketdp = new DatagramPacket(by,by.length);

ds.receive(dp);

Stringstr = new String(dp.getData(),0,dp.getLength());

System.out.println(str+"--"+dp.getAddress());

ds.close();


TCP传输

Socket和ServerSocket

建立客户端和服务器端

建立连接后,通过Socket中的IO流进行数

据的传输

关闭socket

同样,客户端与服务器端是两个独立的应用

程序。

客户端

通过Socket建立对象并指定要连接的服务

端主机以及端口。

Sockets = new Socket(“192.168.1.1”,9999);

OutputStreamout = s.getOutputStream();

out.write(“hello”.getBytes());

s.close();

服务端

建立服务端需要监听一个端口

ServerSocketss = new ServerSocket(9999);

Sockets = ss.accept ();

InputStreamin = s.getInputStream();

byte[]buf = new byte[1024];

intnum = in.read(buf);

Stringstr = new String(buf,0,num);

System.out.println(s.getInetAddress().toString()+”:”+str);

s.close();

ss.close();


Tcp传输最容易出现的问题

客户端连接上服务端,两端都在等待,没

有任何数据传输。

通过例程分析:

因为read方法或者readLine方法是阻塞式。

解决办法:

自定义结束标记

使用shutdownInput,shutdownOutput方法


URL类对象:

String getFile() 
          获取此 URL 的文件名。 
 String getHost() 
          获取此 URL 的主机名(如果适用)。 
 String getPath() 
          获取此 URL 的路径部分。 
 int getPort() 
          获取此 URL 的端口号。 
 String getProtocol() 
          获取此 URL 的协议名称。 
 String getQuery() 
          获取此 URL 的查询部 
 URL url = new URL("http://192.168.1.254/myweb/demo.html?name=haha&age=30");
               System.out.println("getProtocol() :"+url.getProtocol());
               System.out.println("getHost() :"+url.getHost());
               System.out.println("getPort() :"+url.getPort());
               System.out.println("getPath() :"+url.getPath());
               System.out.println("getFile() :"+url.getFile());
               System.out.println("getQuery() :"+url.getQuery());

Socket网络编程示例代码:

1:UDP多线程的一个聊天示例

/*编写一个聊天程序。有收数据的部分,和发数据的部分。这两部分需要同时执行。那就需要用到多线程技术。一个线程控制收,一个线程控制发。
因为收和发动作是不一致的,所以要定义两个run方法。
而且这两个方法要封装到不同的类中。*/import java.io.*;import java.net.*;class Send implements Runnable{private DatagramSocket ds;public Send(DatagramSocket ds){this.ds = ds;}public void run(){try{BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));String line = null;while((line=bufr.readLine())!=null){byte[] buf = line.getBytes();DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10002);ds.send(dp);if("886".equals(line))break;}}catch (Exception e){throw new RuntimeException("发送端失败");}}}class Rece implements Runnable{private DatagramSocket ds;public Rece(DatagramSocket ds){this.ds = ds;}public void run(){try{while(true){byte[] buf = new byte[1024];DatagramPacket dp = new DatagramPacket(buf,buf.length);ds.receive(dp);String ip = dp.getAddress().getHostAddress();String data = new String(dp.getData(),0,dp.getLength());if("886".equals(data)){System.out.println(ip+"....离开聊天室");break;}System.out.println(ip+":"+data);}}catch (Exception e){throw new RuntimeException("接收端失败");}}}class  ChatDemo{public static void main(String[] args) throws Exception{DatagramSocket sendSocket = new DatagramSocket();DatagramSocket receSocket = new DatagramSocket(10002);new Thread(new Send(sendSocket)).start();new Thread(new Rece(receSocket)).start();}}

2:

用多线程解决

客户端通过键盘录入用户名。
服务端对这个用户名进行校验。 
如果该用户存在,在服务端显示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.254",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:
/*
需求:上传图片。
客户端。
1,服务端点。
2,读取客户端已有的图片数据。
3,通过socket 输出流将数据发给服务端。
4,读取服务端反馈信息。
5,关闭。
*/
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("图片格式错误,请重新选择");
                       return ;
               }
               if(file.length()>1024*1024*5)
               {
                       System.out.println("文件过大,没安好心");
                       return ;
               }    
               Socket s = new Socket("192.168.1.254",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();
        }
}
 
/*
服务端
这个服务端有个局限性。当A客户端连接上以后。被服务端获取到。服务端执行具体流程。
这时B客户端连接,只有等待。
因为服务端还没有处理完A客户端的请求,还有循环回来执行下次accept方法。所以
暂时获取不到B客户端对象。
那么为了可以让多个客户端同时并发访问服务端。
那么服务端最好就是将每个客户端封装到一个单独的线程中,这样,就可以同时处理多个客户端请求。
如何定义线程呢?
只要明确了每一个客户端要在服务端执行的代码即可。将该代码存入run方法中。
*/
class PicThread implements Runnable
{
        private Socket s;
        PicThread(Socket s)
        {
               this.s = s;
        }
        public void run()
        {
               int count = 1;
               String ip  = s.getInetAddress().getHostAddress();
               try
               {
                       System.out.println(ip+"....connected");
                       InputStream in = s.getInputStream();
                       File dir =  new File("d:\\pic");
                       File file = new File(dir,ip+"("+(count)+")"+".jpg");
                       while(file.exists())
                               file = new File(dir,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();
        }
}
 
4:
//利用GUI 和 URL 及Socket技术 实现一个模拟浏览器
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
class  MyIEByGUI2
{
        private Frame f;
        private TextField tf;
        private Button but;
        private TextArea ta;     
        private Dialog d;
        private Label lab;
        private Button okBut;
 
        MyIEByGUI2()
        {
               init();
        }
        public void init()
        {
               f = new Frame("my window");
               f.setBounds(300,100,600,500);
               f.setLayout(new FlowLayout());
               tf = new TextField(60);
               but = new Button("转到");
               ta = new TextArea(25,70);
               d = new Dialog(f,"提示信息-self",true);
               d.setBounds(400,200,240,150);
               d.setLayout(new FlowLayout());
               lab = new Label();
               okBut = new Button("确定"); 
               d.add(lab);
               d.add(okBut); 
               f.add(tf);
               f.add(but);
               f.add(ta);
               myEvent();
               f.setVisible(true);
        }
        private void  myEvent()
        {
               okBut.addActionListener(new ActionListener()
               {
                       public void actionPerformed(ActionEvent e)
                       {
                               d.setVisible(false);
                       }
               });
               d.addWindowListener(new WindowAdapter()
               {
                       public void windowClosing(WindowEvent e)
                       {
                               d.setVisible(false);
                       }
               });
               tf.addKeyListener(new KeyAdapter()
               {
                       public void keyPressed(KeyEvent e)
                       {
                               try
                               {
                                              if(e.getKeyCode()==KeyEvent.VK_ENTER)
                                      showDir();
                               }
                               catch (Exception ex)
                               {
                               }                 
                       }
               });
               but.addActionListener(new ActionListener()
               {
                       public void actionPerformed(ActionEvent e)
                       {
                               try
                               {
                                      showDir();
                               }
                               catch (Exception ex)
                               {
                               }                              
                       }
               }); 
               f.addWindowListener(new WindowAdapter()
               {
                       public void windowClosing(WindowEvent e)
                       {
                               System.exit(0); 
                       }
               });
        }
        private void showDir()throws Exception
        {
               ta.setText("");
               String urlPath = tf.getText();//http://192.168.1.254:8080/myweb/demo.html          
               URL url = new URL(urlPath);
               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)); 
        }
        public static void main(String[] args) 
        {
               new MyIEByGUI2();
        }
}