java socket 编程实现网络交谈和同步操作
目标
用java socket编程和多线程机制实现网络交谈和同步操作。
1、程序网络结构图
2、流程图
3、原理
①Java Swing编程
该网络交谈小程序使用了Java Swing编程技术,实现画图、编辑消息等功能;
②多线程
该程序使用了多线程技术,在程序运行过程中产生了多个线程。在创建客户端时,为每个客户端开启一个线程,这样客户端之间可以看成是相互独立的。
③Socket编程
该程序使用了socket编程技术,主进程(即服务器)创建一个服务器端的socket,指定绑定的端口,并监听该端口。当有客户端socket向该端口发送消息时,服务器端接收消息并将该客户端放入一个队列中,该队列中存放当前访问该端口的所有客户端。当服务器向客户端发送消息时,遍历该队列中的所有客户端,同时向所有客户端发送消息。当客户端有消息发送到服务器时,服务器接收该消息,并将该消息转发给客户端队列中的所有客户端,实现消息共享。
注意:
ConnectionException : 对于客户进程, 如果它发出的连接请求被加入到服务器的请求连接队列中, 就意味着客户与服务器的连接建立成功, 客户进程从 Socket 构造方法中正常返回. 如果客户进程发出的连接请求被服务器拒绝, Socket 构造方法就会抛出 ConnectionException.
必须在服务器进程通过 ServerSocket 的 accept() 方法从请求连接队列中取出连接请求, 并返回一个Socket 对象后, 服务器进程这个Socket 对象才与客户端的 Socket 对象形成一条通信线路.
相关博客:http://blog.csdn.net/qq_23473123/article/details/51461894
4、程序的输入与输出
输入:在“画图”窗口中绘图,在“聊天”窗口中发送消息。
输出:服务器和各客户端同步共享窗口内容。
5、程序运行截图
a. 如下图所示,运行程序,可看到名为“ycc的通信小程序”的窗口,该窗口包含一个下拉框,一个画图面板和一个聊天面板;
b. 如下图所示,点击下拉框上的“文件”按钮,弹出“添加客户端”按钮,单击该按钮可添加客户端;
c. 如下图所示,这里添加了三个客户端,在服务器或任意客户端上画图,其它窗口可同步显示该图片;
d. 如下图所示,点击“聊天”面板,在服务器或任意客户端窗口的编辑框中编辑消息并点击发送按钮,其它窗口均可显示该消息;
6、代码实现
①程序结构
②客户端Client.java
package networkTalk;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import networkTalk.Client;
public class Client extends JFrame{
public JTextArea area; //聊天窗口的输入框
JTextField field; //聊天窗口的显示框
JButton button;
//这里不能用static,每个客户端必须是独立的
public PrintWriter writer;
public Graphics g; //用来画图的面板
public JPanel contentPanel1;
public Client() {
//使用java swing的一些控件
this.setTitle("客户端");
this.setSize(500, 500);
JMenu jMenu = new JMenu("文件"); //菜单对象
JMenuItem t = new JMenuItem("添加客户端");//菜单项
jMenu.add(t);
JMenuBar jMenuBar = new JMenuBar(); //创建菜单工具栏
jMenuBar.add(jMenu);
this.setJMenuBar(jMenuBar);
JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.LEFT);//设置选项卡坐标
contentPanel1 = new JPanel();//创建多个容器
JPanel contentPanel2 = new JPanel();
jTabbedPane.add("画图",contentPanel1);
jTabbedPane.add("聊天",contentPanel2);
this.add(jTabbedPane,BorderLayout.CENTER);
area= new JTextArea(20,30);
field = new JTextField(20);
button=new JButton("提交");
JScrollPane sp =new JScrollPane(area);
contentPanel2.add(sp,BorderLayout.CENTER);
contentPanel2.add(field);
contentPanel2.add(button);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//聊天界面中消息发送按钮的监听事件
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
//获取编辑框中的内容
String text = field.getText();
//输出流,传递给服务器
writer.println(text);
//显示框中显示内容
area.append("我:"+text+"\n");
//编辑设为空
field.setText("");
}
});
//“添加客户端”按钮的监听事件,点击该按钮添加一个客户端
t.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
Runnable myRunnable = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
try {
Client.main();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Thread thread = new Thread(myRunnable);
thread.start();
}
});
//在画图面板中监听鼠标消息
contentPanel1.addMouseMotionListener(new MouseMotionListener() {
int x=0;
int y=0;
@Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
// 获取鼠标坐标
x = e.getX();
y = e.getY();
//将消息传给服务器
writer.println(x + "*" + y);
g = contentPanel1.getGraphics(); //这里也用到了
g.fillOval(x, y, 1, 1);
}
});
}
public static void main() throws Exception{
Client c =new Client();
//创建客户端socket,指定服务器地址和端口
Socket socket =new Socket("127.0.0.1",9995);
//获取输出流,向服务器发送消息
OutputStream out = socket.getOutputStream(); //输出流
c.writer=new PrintWriter(out,true); //将输出流包装成打印流
System.out.println("成功连接服务器!");
//获取输入流,并读取服务器端的响应信息
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
while(true){
//获取客户端消息
String line = reader.readLine();
String a[] = new String[2];
//画图时传回来的值是x*y,调用split方法后a的长度为2,聊天时没有*符号,a的长度为1
a = line.split("\\*");
if(a.length == 1){
//聊天
c.area.append("客户端:"+a[0]+"\n");
}else {
//画图
int x = Integer.parseInt(a[0]);
int y = Integer.parseInt(a[1]);
c.g = c.contentPanel1.getGraphics();
c.g.fillOval(x, y, 1, 1);
}
}
}
}
①服务器端Server.java
package networkTalk;
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import networkTalk.Server;
public class Server extends JFrame{
static JTextArea area; //聊天窗口的输入框
JTextField field; //聊天窗口的显示框
JButton button;
static PrintStream writer; //打印流,设置为全局变量
static Graphics g; //画图
static JPanel contentPanel1; //用来画图的面板
static ArrayList<Socket> sList; //socket请求队列
public Server(){
//使用java swing的一些控件
this.setTitle("ycc的通信小程序");
this.setSize(500,500);
JMenu jMenu = new JMenu("文件"); //菜单对象
JMenuItem t = new JMenuItem("添加客户端");
jMenu.add(t);
JMenuBar jMenuBar = new JMenuBar(); //创建菜单工具栏
jMenuBar.add(jMenu);
this.setJMenuBar(jMenuBar);
JTabbedPane jTabbedPane = new JTabbedPane(JTabbedPane.LEFT);//设置选项卡坐标
contentPanel1 = new JPanel(); //创建多个容器
JPanel contentPanel2 = new JPanel();
jTabbedPane.add("画图",contentPanel1);
jTabbedPane.add("聊天",contentPanel2);
this.add(jTabbedPane,BorderLayout.CENTER);
area = new JTextArea(20,30);
area.setEditable(false);
field = new JTextField(20);
button = new JButton("提交");
JScrollPane sp = new JScrollPane(area);
contentPanel2.add(sp,BorderLayout.CENTER);
contentPanel2.add(field);
contentPanel2.add(button);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//在画图面板中监听鼠标消息
contentPanel1.addMouseMotionListener(new MouseMotionListener() {
//X,Y表示鼠标当前坐标
int x=0;
int y=0;
@Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
@Override
public void mouseDragged(MouseEvent e) {
// 获取鼠标坐标
x = e.getX();
y = e.getY();
//writer.println(x + "*" + y);
//将消息广播给各个客户端
for (Socket s : sList) {
try {
//System.out.println(s);
PrintStream writer1;
//获取输出流
writer1 = new PrintStream(s.getOutputStream(),true);
//传给客户端
writer1.println(x + "*" + y);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
//调用graphics画图
g = contentPanel1.getGraphics();
g.fillOval(x, y, 1, 1);
}
});
//“添加客户端”按钮的监听事件,点击该按钮添加一个客户端
t.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
//为客户端开启一个线程
Runnable myRunnable = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
try {
Client.main();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
//启动线程
Thread thread = new Thread(myRunnable);
thread.start();
}
});
//聊天界面中消息发送按钮的监听事件
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String text =field.getText();
// writer.println(text);
//广播
for (Socket s : sList) {
try {
System.out.println(s);
PrintStream writer1;
writer1 = new PrintStream(s.getOutputStream(),true);
writer1.println(text);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
area.append("我:"+text+"\n");
field.setText("");
}
});
}
/*=================================================================================*/
public static void main(String[] args) throws Exception{
Server s = new Server();
//创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
ServerSocket server =new ServerSocket(9995);
System.out.println("服务端准备完毕!开始监听请求!");
Server.sList = new ArrayList<Socket>();
for(;;)
{
//调用accept()方法开始监听,等待客户端的连接
Socket socket = server.accept();
//这个socket传给下面开启的线程
//这里接收到的socket要在下面开启的线程中用到
//添加到socket队列中
sList.add(socket);
//开启多线程
class MyThread implements Runnable
{
private Socket m_Socket;
public void setSocket(Socket s)
{
m_Socket = s;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
//下面的代码放到一个单独的线程中,每次accept到新的socke都开一个线程
InetAddress address =m_Socket.getInetAddress();
String name = address.getLocalHost().getHostName();
System.out.println(name+"已成功连接");
//getOutputStream方法使连接的另一端得到输入
writer=new PrintStream(m_Socket.getOutputStream(),true);
//获取从客户端传回来的值
BufferedReader reader = new BufferedReader(new InputStreamReader(m_Socket.getInputStream()));
while(true){
String line=null;
line=reader.readLine();
String a[] = new String[2];
//画图时传回来的值是x*y,调用split方法后a的长度为2,聊天时没有*符号,a的长度为1
a = line.split("\\*");
//收到的数据发送给slist里面的其它客户端,广播
for (Socket s : sList) {
System.out.println(s);
if (m_Socket != s) {
PrintStream writer1;//新的 writer,发给其他客户端
writer1 = new PrintStream(s.getOutputStream(),true);
writer1.println(line);
}
}
if(a.length == 1){
//聊天
area.append("客户端:"+a[0]+"\n");
}else {
//画图
int x = Integer.parseInt(a[0]);
int y = Integer.parseInt(a[1]);
g = contentPanel1.getGraphics();
g.fillOval(x, y, 1, 1);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
MyThread myRunnable = new MyThread();
myRunnable.setSocket(socket);
Thread thread = new Thread(myRunnable);
thread.start();
}
}
}