充分理解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的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
打包成JAR文件方式:
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打包在一起么。。。。
第一个项目,所以各种迷惑。。。。。