Socket编程就是通过Socket类来编写网络通信程序,对于网络通信程序,分为两个,一个是服务器程序,一个是客户端程序。服务器程序创建一个ServerSocket对象(服务器套接字),通过调用accept()方法来接受来自客户端的连接请求,客户端程序创建一个Socket对象来连接服务器,连接请求成功后,服务器同时创建一个新的Socket与客户端建立连接,服务器继续等待新的请求。
一、客户端向服务器发送消息
服务器端:
import java.net.*;
import java.io.*;
public class Server {
DataInputStream din;
DataOutputStream dout;
ServerSocket ss;
Socket sc;
public Server() {
try {
ss = new ServerSocket(9999); //创建服务器套接字
sc = ss.accept(); //等待来自客户端的连接请求
din = new DataInputStream(sc.getInputStream()); //获取数据输入流
String msg = din.readUTF();
System.out.println("客户端说:" + msg);
din.close();
sc.close();
}catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Server();
}
}
在上述服务器端代码中,通过accept来接收客户端的请求,这个请求是阻塞的,也就是说,如果没有客户端连接进来,服务器端就一直停留在接收状态,即不会执行接下来的代码,当客户端连接进来之后,通过数据输入流来读取客户端发来的消息,然后输出,结束时需关闭IO流和Socket。
客户端:
import java.net.*;
import java.io.*;
public class Client {
DataInputStream din;
DataOutputStream dout;
Socket sc;
public Client() {
try {
sc = new Socket("127.0.0.1", 9999); //连接服务器
dout = new DataOutputStream(sc.getOutputStream()); //获取数据输出流
String msg = "hello server";
dout.writeUTF(msg);
dout.close();
sc.close();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Client();
}
}
在上述客户端代码中也一样,通过Socket来连接服务器,连接成功后对服务器放送消息,结束时也需要关闭IO流和Socket。
上述程序,是简单的客户端向服务器发送消息,也就是一个简单的单向通信的程序。
二、服务器端和客户端互发消息
上面的只是客户端对服务器发送消息,现在来看一下两者之间可以互发消息的程序。
服务器端:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.*;
import java.io.*;
public class ChatServer extends JFrame implements ActionListener {
JButton jb = new JButton("发送");
JTextField jtf = new JTextField(20);
JTextArea jta = new JTextArea();
JScrollPane jsp = new JScrollPane(jta);
JPanel jp = new JPanel();
PrintWriter pw = null;
public ChatServer() {
this.setTitle("服务器端");
this.setSize(350, 250);
this.setResizable(false);
this.setVisible(true);
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
this.setLocation(500, 300);
jta.setDisabledTextColor(Color.black);
jb.addActionListener(this);
jp.add(jtf);
jp.add(jb);
jta.setEnabled(false);
this.add(jsp);
this.add(jp,"South");
try {
ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept();
InputStreamReader isr = new InputStreamReader(s.getInputStream());
BufferedReader br = new BufferedReader(isr);
pw = new PrintWriter(s.getOutputStream(), true);
while(true) {
String xinxi = br.readLine();
jta.append("客户端说:" + xinxi + "\n\r");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() == jb) {
String xinxi = jtf.getText();
jta.append("我对客户端说:" + xinxi + "\n\r");
pw.println(xinxi);
jtf.setText("");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new ChatServer();
}
}
客户端:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.*;
import java.io.*;
public class ChatClient extends JFrame implements ActionListener {
JButton jb = new JButton("发送");
JTextField jtf = new JTextField(20);
JTextArea jta = new JTextArea();
JScrollPane jsp = new JScrollPane(jta);
JPanel jp = new JPanel();
PrintWriter pw = null;
DataInputStream din;
DataOutputStream dout;
public ChatClient() {
this.setTitle("客户端");
this.setSize(350, 250);
this.setResizable(false);
this.setVisible(true);
this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
this.setLocation(500, 300);
jta.setDisabledTextColor(Color.black);
jb.addActionListener(this);
jp.add(jtf);
jp.add(jb);
jta.setEnabled(false);
jta.setLineWrap(true);
this.add(jsp);
this.add(jp,"South");
jtf.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == 10) {
String xinxi = jtf.getText();
jta.append("我对服务器说:" + xinxi + "\r\n");
try {
dout.writeUTF(xinxi);
} catch (IOException e1) {
e1.printStackTrace();
}
jtf.setText("");
}
}
});
try {
Socket s = new Socket("127.0.0.1", 8888);
din = new DataInputStream(s.getInputStream());
dout = new DataOutputStream(s.getOutputStream());
while(true) {
String xinxi = din.readUTF();
jta.append("服务器说:" + xinxi + "\r\n");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == jb) {
String xinxi = jtf.getText();
if(!xinxi.equals("")) {
jta.append("我对服务器说:" + xinxi + "\r\n");
try {
dout.writeUTF(xinxi);
} catch (IOException e1) {
e1.printStackTrace();
}
jtf.setText("");
}
else {
}
}
}
public static void main(String[] args) {
new ChatClient();
}
}
上述代码写的就是服务器端和客户端可以互发消息的一个程序,通过一个while死循环来不断接收来自客户端或者服务器端的消息,并且输出到JTextArea的文本域中,输入消息则是通过读取文本框中的内容,通过一个按钮将他发送到服务器端或者客户端,两段代码其实差的不多。
三、多个客户端同时连接到服务器端
一个服务器只能接收一个来自客户端的请求,这显然是不能满足我们的要求的,那么,如何来实现多个客户端同时连接到服务器端呢?这就要用到线程了,每当服务器接收到一个来自客户端的请求时,我们就创建一个线程,让这个线程去处理该客户端,我们可以这样做:
while(flag) {
try {
Socket sc = ss.accept();
ServerThread st = new ServerThread(sc);
st.start();
}catch(Exception e) {
e.printStackTrace();
}
}
这样就可以处理多客户端同时连接服务器端的问题了。但是呢?我们想要客户端与客户端之间发送消息该怎么办呢?那么就需要服务器端这个桥梁,一个客户端把消息发送到服务器端,服务器端再把消息发送到另一个客户端,那么服务器怎么知道我想要发送的消息是发送到这个客户端呢?那么可以这么做,我们创建一个Vector对象用来保存当前连接到服务器端的线程,然后当一个客户端想要发送消息给另一个客户端时,通过遍历Vector来找到那个客户端,然后就可以发送给那个客户端消息了。