充分理解Socket---简单的聊天程序的代码

充分理解Socket  

   1.什么是socket 

   所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过"套接字"向网络发出请求或者应答网络请求。 

   以J2SDK-1.3为例, Socket和ServerSocket类库位于java.net包中。 ServerSocket用于服务器端 Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。 

   重要的Socket API: 

   java.net.Socket继承于java.lang.Object,有八个构造器,其方法并不多,下面介绍使用最频繁的三个方法,其它方法大家可以见JDK-1.3文档。 

   . Accept方法用于产生"阻塞",直到接受到一个连接,并且返回一个客户端的Socket对象实例。"阻塞"是一个术语,它使程序运行暂时"停留"在这个地方,直到一个会话产生,然后程序继续;通常"阻塞"是由循环产生的。 

   . getInputStream方法获得网络连接输入,同时返回一个IutputStream对象实例,。 

   . getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。 

   注意:其中getInputStream和getOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。 

    2.如何开发一个Server-Client模型的程序 

   开发原理: 

   服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。 

   客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口


简单的聊天程序示例。用到多线程、流、socket编程,用到Frame。

server端程序如下:

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class ChatServer {
    boolean started = false;
    ServerSocket ss = null;
	Socket s = null;
	String ip;
	List<Client> clients = new ArrayList<Client>();
	//int count;

	//开启服务器
    public void startServer(){
    	try{
		ss = new ServerSocket(8888);
		started = true;
		System.out.println("服务器已启动!");
		} catch (BindException e1){
			System.out.println("端口被占用,请关闭后重试");
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			    while(started){
				s = ss.accept();
				System.out.println("一个客户已连接");
				Client c = new Client(s);
				new Thread(c).start();
				clients.add(c);
			}
		}catch (IOException e) {
				e.printStackTrace();
			}finally {
				try {
					ss.close();//关闭此套接字。 在 accept() 中所有当前阻塞的线程都将会抛出 SocketException。 
					//如果此套接字有一个与之关联的通道,则关闭该通道。

				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		
	    }

	public static void main(String[] args) {
		new ChatServer().startServer();
	}
  
   class Client implements Runnable {   
	    private Socket s;
		private DataInputStream dis = null;
		private DataOutputStream dos = null;
		private boolean flag = false;
		
        public Client(Socket s){
        	this.s = s;
        	try {
				dis = new DataInputStream(s.getInputStream());
				dos = new DataOutputStream(s.getOutputStream());
				flag = true;
			} catch (IOException e) {
				e.printStackTrace();
			}
        }
        
        @Override  
    	public void run() {      
                 
                  
            //把当前用户发来的信息发送给所有用户.   
           try { 
        	   while (flag) {    
					String str = dis.readUTF();
					for (int i = 0; i < clients.size();i++) {   
                    Client c = clients.get(i);
                    //获得当前用户的IP   
            ip=s.getInetAddress().getHostAddress();   
                    c.send(str);
                }
				}
           }catch (EOFException e) {
				System.out.println("客户端关闭了!");
			} catch (IOException e) {
				e.printStackTrace();
			}finally {
					try {
						if(dis != null) dis.close();
						if(dos != null) dos.close();
						if(s != null)  {
							s.close();
						}
						
					} catch (IOException e1) {
						e1.printStackTrace();
					}	
				}     
            }
    
    public void send(String str){
    	try{
    			dos.writeUTF(ip+"说:");
				dos.writeUTF(str+'\n');
			} catch (IOException e) {
				clients.remove(this);
			}
    	}
    }
   }
  


client端代码如下:

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.swing.JFrame;

public class ChatClient extends JFrame {
	TextArea textArea = new TextArea();// 创建一个文本域
	TextField textField = new TextField();// 创建一个文本框
	Button button_send = new Button("发送");
	
	Thread t = new Thread(new RecvThread());
	Socket s = null;
	DataInputStream dis = null;
	DataOutputStream dos = null;
	String str = null;
	boolean flag = false;
    
	public void launchFrame(){
		this.setLocation(400, 400);
		//this.setSize(300, 300);
		//button_send.setSize(20, 20);
	
		this.add(textArea,BorderLayout.NORTH);
		this.add(textField,BorderLayout.SOUTH);
		this.add(button_send,BorderLayout.EAST);
			//this.setResizable(true);
		pack();
		this.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent arg0) {
				disConnect();
				System.exit(0);
			}
			
		});
		//this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		//匿名内部类对发送按钮进行响应,发送信息
		button_send.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				str = textField.getText().trim();
				//textArea.setText(str);
				textField.setText("");	
				
				try {
					dos.writeUTF(str);
					dos.flush();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
		}
		}
		);
		setVisible(true);
		connect();		
		t.start();
	}
	
	//从服务器接收信息
	class RecvThread implements Runnable{
		@Override
		public void run() {
			try {
				while(flag){
				str = dis.readUTF();
				textArea.setText(textArea.getText() + str + '\n');
				}
			}catch (SocketException e) {
				System.out.println("退出了,bye!");
			} catch (EOFException e) {
				System.out.println("退出了,bye - bye!");
			} catch (IOException e) {
				e.printStackTrace();
			} 
		}
	}
	
	//连接到服务器
	public void connect(){
		try {
			s = new Socket("127.0.0.1",8888);
			flag = true;
			System.out.println("已经连接到服务器");
			dis = new DataInputStream(s.getInputStream());
			dos = new DataOutputStream(s.getOutputStream());	
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}	
	}
	
	//断开连接
	public void disConnect(){
		try {
			dis.close();
			dos.close();
			s.close();
		
		} catch(SocketException e2){
			System.out.println("客户端关闭了,bye");
			}catch (EOFException e1){
			System.out.println("客户端关闭了,bye");//catch还有先后顺序啊?
		}
		catch (IOException e) {
			System.out.println("连接已经断开");
			e.printStackTrace();
		}
	}
	

	public static void main(String[] args) {
		new ChatClient().launchFrame();
	}

}

打包成JAR文件方式:

        在MyEclipse中,选中项目名,右击--export--jar--打包成client和server两个JAR文件,运行的时候,先运行server再运行client,IP设置为127.0.0.1,所以打开两个客户端,自己和自己通信。

假如我想和A通信,那么设置为A的IP,然后他打开server和client,我也打开一个server和client,就可以通信了。


自己刚学这方面知识,所以程序只能简单的通信,有以下疑问:

      1、我我每次关掉窗口,通信结束的时候,只能通过杀进程的方式关闭么。要不然下一次好像起不来啊?

      2、QQ的源代码哪里可以下载到么?

      3、是不是我这样的程序用到的是TCP ,因为程序中有建立连接这个环节啊

      4、不知道我打包JAR文件的方式是否正确。不能把client和server打包在一起么。。。。


第一个项目,所以各种迷惑。。。。。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值