高 级 网 络 编 程
实验报告
实验名称: 即时聊天工具 实验日期: 2011-1-5 学生姓名: 学生学号:
一、实验目的
加深对网络编程中客户端和服务器的包的传送和接收; 在实验中学习Socket网络编程。
二、实验环境
OS:Windows 编程语言:java 开发平台:MyEclipse
三、实验内容
1. 实现功能(消息:c-s-c
? 服务器
发送消息给客户端;
接收来自客户端发往服务器的消息;
记录客户端与服务器建立连接、断开连接,发送的所有消息的内容,还可以记录客户端与客户端之间发送文件的文件名; 文件:c-c)
? 客户端
发送消息给服务器(在ip一栏填上“服务器”);
接收来自服务器的消息;
公共聊天室:查看所有与服务器连接的客户端在公共聊天室发送的消息及发送消息费所有客户端;
私有聊天室:给特定ip的客户端发送消息及文件,接收来自客户端发送的消息及文件。
2. 设计思路
? 服务器:
Server是服务器的类,其中包含绘制窗口的函数creatUI()、连接函数
connect()。在连接函数中不停循环检测接收客户端的连接请求,每当有一个新的客户端连接时为它创建一个新的线程,同时将这个客户端放入一个数组列表clientlist中。
在重构的run()函数中,用while循环来不停的读入客户端发送的消息,当消息为“请求断开连接”时,将线程停止。
接收的消息结构为:地址+端口号+内容。
地址:如果地址为空,则表示群发消息,服务器将消息发给已连接了服务器的所有客户端;如果地址为服务器,则表示客户端发送给服务器的消息;剩下的就是客户端发送给客户端的消息了。
端口号:如果端口号为消息传送的端口号,则表示内容为客户端发送的消息;如果端口号为文件传送的端口号,则表示该客户端给另一个客户端发送邮件,服务器要发送提醒收件的消息到另一个客户端。
内容:如果端口号为消息传送的端口号,则表示内容为客户端发送的消息;如果端口号为文件传送的端口号,则表示传送的文件名;
? 客户端 Client是服务器的类,其中包含绘制窗口的函数creatUI()、连接函数
connect()。在连接函数中向服务器发送“请求连接”,然后创建一个进程,用while循环来不停的读取服务器发送的消息。
当关闭窗口的时候发送“请求断开”的消息,然后结束线程。
客户端分为两块,一块是公共聊天室、一块是私人聊天室。公共聊天室发送的消息所有连接了服务器的客户端都收到;私人聊天室的消息只会发送到指定ip的客户端那。
接收的消息结构为:私/公+内容
私/公:私表示接收到的消息属于私人消息,只能显示在私人聊天室中;公表示接收到的消息是公共消息,可以放到公共聊天室中。
四、实验结果分析及结论
1. 结果
服务器记录显示:ip为127.0.0.1的客户端请求连接、在聊天室的聊天发送两天聊天记录;
因为只有一台电脑,所以127.0.0.1只能向自己发送一条消息。
服务器向ip为127.0.0.1的客户端发送一条消息。
服务器记录显示:ip为127.0.0.1的客户端向127.0.0.1的客户端发送了一个名为music.txt的文件;
客户端显示:自己发送了一个music.txt的文件;
消息提示自己127.0.0.1的客户端给我发了一个名为music.txt的文件,自己成功接收。
2. 完善
在服务器端可以增加一个导出记录的功能,这样更方便管理员管理;
每次消息发送的时候加上发送的时间;
在客户端和服务器增加一个显示当前所有与服务器连接的客户端ip的列表,方便客户端用户查找。
3. 结论:
该即时聊天的程序可以正常工作
五、心得体会
在这次的网络实验加深了我对网络编程的理解和兴趣…………虽然说这个程序到现在还是有一些毛病,不过完成这个实验还是让人有成就感的。
六、代码如下:
//Client.java文件
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Label;
import java.awt.Panel;
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.FileInputStream; import java.io.FileOutputStream; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.swing.JFileChooser; import javax.swing.JFrame;
public class Client implements ActionListener {
private Socket client;
private TextArea txtArea;
private TextField txtField; private TextArea txtAreap; private TextField txtFieldp; private TextField txtIp;
private String serverIp = "127.0.0.1";//服务器的ip private int txtport = 8888;//消息传输的端口号 private int fileport = 9999;//文件传输的端口号 private InputStream instxt; private OutputStream outstxt; private DatagramSocket dsocket; private DatagramPacket dpacket; private boolean start; private TxtClientThread txtThread; public static void main(String[] args){ new Client(); } public Client(){ start = true; creatUI(); connect(); txtThread = new TxtClientThread(this); txtThread.start(); } public void creatUI(){ Frame f=new Frame("Client"); txtArea=new TextArea(); txtField=new TextField(45); txtAreap=new TextArea(); txtFieldp=new TextField(35); txtIp = new TextField(20); txtArea.setEditable(false); txtAreap.setEditable(false); Button sendfile = new Button("发送文件"); Button receivefile = new Button("接收文件"); Button send = new Button("发送"); Button sendp = new Button("发送"); TxtSendListener txtSendListener = new TxtSendListener(this); send.addActionListener(txtSendListener); PTxtSendListener ptxtSendListenner = new PTxtSendListener(this); sendp.addActionListener(ptxtSendListenner);
sendfile.setActionCommand("发送文件"); receivefile.setActionCommand("接收文件"); sendfile.addActionListener(this); receivefile.addActionListener(this); Panel chat = new Panel(); chat.setLayout(new BorderLayout()); chat.add(txtArea,BorderLayout.CENTER); Panel chat2 = new Panel(); chat2.setLayout(new FlowLayout()); chat2.add(txtField); chat2.add(send); chat.add(chat2,BorderLayout.SOUTH); Label lb_chat = new Label("公共聊天室"); chat.add(lb_chat,BorderLayout.NORTH); f.add(chat,"Center"); Panel pChat = new Panel(); pChat.setLayout(new BorderLayout()); Label lb_pChat = new Label("私人聊天室"); pChat.add(lb_pChat,BorderLayout.NORTH); pChat.add(txtAreap,BorderLayout.CENTER); Panel ipPanel = new Panel(); ipPanel.setLayout(new FlowLayout()); Label lb_ip = new Label("ip地址:"); ipPanel.add(lb_ip,FlowLayout.LEFT); ipPanel.add(txtIp); Panel pl = new Panel(); pl.setLayout(new BorderLayout()); pl.add(txtFieldp,BorderLayout.CENTER); pl.add(ipPanel,BorderLayout.SOUTH); pChat.add(pl,BorderLayout.SOUTH); Panel btnPanel = new Panel(); btnPanel.setLayout(new GridLayout(3,1,5,20)); btnPanel.add(sendp); btnPanel.add(sendfile); btnPanel.add(receivefile); pChat.add(btnPanel,BorderLayout.EAST);
f.add(pChat,"South");
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
close();
start = false;
System.exit(0);
}
});
f.setSize(400,600);
f.setLocation(600,0);
f.setVisible(true);
}
public void connect(){
try {
client = new Socket(serverIp,txtport);
instxt = client.getInputStream();
outstxt = client.getOutputStream();
/**********************请求建立连**************************************/
outstxt.write(("服务器+"+txtport+"+请求连接").getBytes());
dsocket = new DatagramSocket(fileport);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
String command = arg0.getActionCommand();
if(command.equals("发送文件")){
JFrame f = new JFrame("打开");
f.setSize(400,300);
JFileChooser chooser = new JFileChooser();
f.add(chooser); 接
int returnVal = chooser.showOpenDialog(f);
if(returnVal == JFileChooser.APPROVE_OPTION)
{
try
{
byte[] buf = new byte[10248];
FileInputStream fis = new FileInputStream(chooser.getSelectedFile());
DataInputStream dos =new DataInputStream(fis);
dos.read(buf);
dpacket = new DatagramPacket(buf,buf.length,InetAddress.getByName(txtIp.getText().trim()),fileport);
dsocket.send(dpacket);
txtAreap.append("\n自己向"+txtIp.getText().trim()+"用户传送文件:\n"+chooser.getCurrentDirectory()+"\\"
+chooser.getSelectedFile().getName()+"\n");
outstxt.write((txtIp.getText().trim()+"+"+fileport+"+"+chooser.getCurrentDirectory()+"\\"+
chooser.getSelectedFile().getName()).getBytes()); outstxt.flush();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}else if(command.equals("接收文件")){
JFrame f = new JFrame("保存");
f.setSize(400,300);
byte buf[] = new byte[10248];
dpacket = new DatagramPacket(buf,buf.length);
JFileChooser chooser = new JFileChooser();
f.add(chooser);
int a = chooser.showSaveDialog(f);
if(a ==JFileChooser.APPROVE_OPTION)
{
String fileName = chooser.getSelectedFile().getPath();
try
{
dsocket.receive(dpacket);
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(buf);
txtAreap.append("\n接收文件成功\n");
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
public void close(){
try {
/**********************请求断开连**************************************/
outstxt.write(("服务器+"+txtport+"+请求断开").getBytes()); txtThread.interrupt();
instxt.close();
outstxt.close();
client.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public TextArea getTxtArea() {
return txtArea;
}
public String getServerIp() {
return serverIp;
}
public TextField getTxtField() {
return txtField;
}
public TextField getTxtIp() {
return txtIp;
}
public int getTxtport() {
return txtport; 接
}
public int getFileport() {
return fileport;
}
public InputStream getInstxt() {
return instxt;
}
public OutputStream getOutstxt() {
return outstxt;
}
public TextArea getTxtAreap() {
return txtAreap;
}
public TextField getTxtFieldp() {
return txtFieldp;
}
public void setServerIp(String serverIp) {
this.serverIp = serverIp;
}
public boolean isStart() {
return start;
}
}
class TxtClientThread extends Thread implements Runnable{ private Client client;
public TxtClientThread(Client c){
client = c;
}
public void run(){
try {
byte[] buf;
InputStream ins = client.getInstxt(); while(client.isStart()){
buf = new byte[1024];
ins.read(buf);//读取服务器端发送的信息
String info = new String(buf);
if(info.startsWith("公")){
client.getTxtArea().append("\n"+info.substring(info.indexOf("+")+1)+"\n"); }else{
client.getTxtAreap().append("\n"+info.substring(info.indexOf("+")+1)+"\n"); }
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//公共消息发送
class TxtSendListener implements ActionListener{
private Client client;
public TxtSendListener(Client c){
client = c;
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
try {
byte[] buf = new byte[1024];//发送的消息不能超过1024个比特 String info;
/*******************************给所有在聊天室的客户端发送消息****************************************/
info = "" + "+" + client.getTxtport() + "+" + client.getTxtField().getText();
buf = info.getBytes();
client.getTxtArea().append("\n自己:\n"+client.getTxtField().getText()+"\n");
client.getTxtField().setText("");
client.getOutstxt().write(buf);
client.getOutstxt().flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//私有消息发送
class PTxtSendListener implements ActionListener{
private Client client;
public PTxtSendListener(Client c){
client = c;
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
try {
byte[] buf = new byte[1024];//发送的消息不能超过1024个比特 String info;
if(client.getTxtIp().getText().trim().equals("服务器")){
/*******************************给服务器发送消息****************************************/
info = "服务器"+"+"+client.getTxtport()+"+"+client.getTxtFieldp().getText();
buf = info.getBytes();
client.getOutstxt().write(buf);
client.getTxtAreap().append("\n自己向服务器发送消息:\n"+client.getTxtFieldp().getText()+"\n");
client.getTxtFieldp().setText("");
client.getOutstxt().flush();
}else if(!client.getTxtIp().getText().trim().equals("")){
/*******************************给指定ip的客户端发送消息****************************************/
info = client.getTxtIp().getText()+"+"+client.getTxtport()+"+"+client.getTxtFieldp().getText();
buf = info.getBytes();
client.getTxtAreap().append("\n自己向"+client.getTxtIp().getText().trim()+" 发送消息:\n"+client.getTxtField().getText()+"\n");
client.getTxtFieldp().setText(""); client.getOutstxt().write(buf); client.getOutstxt().flush(); }
} catch (IOException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
}
}
//Server.java文件
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Panel;
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.InputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class Server {
private ServerSocket server;
private Socket client;
private TextArea txtArea;
private TextField txtField;
private TextField txtIp;
private int porttxt = 8888;//文本传输端口
private ArrayList<Socket> clientlist;//存放和服务器建立连接的客户端的Socket
private ArrayList<TxtThread> threadlist;//存放和服务器建立连接的客户端的进程
//private TxtThread txtThread;
public static void main(String[] args){
new Server();
}
public Server(){
clientlist = new ArrayList<Socket>(20);//只能允许同时20个客户端与服务器连接
threadlist = new ArrayList<TxtThread>(20);//每个客户端一个进程 creatUI();
connect();
}
public void creatUI(){
Frame f=new Frame("Server");
txtArea=new TextArea();
txtArea.setEditable(false);
txtField=new TextField();
txtIp = new TextField(20);
Button send=new Button("发送");
ServerListener listener = new ServerListener(this);
send.addActionListener(listener);
Panel p=new Panel();
p.setLayout(new BorderLayout());
p.add(txtField,"Center");
p.add(send,"East");
f.add(txtArea,"North");
f.add(p,"Center");
Panel btnp = new Panel();
btnp.setLayout(new FlowLayout());
Label lbIp = new Label("Ip地址:");
btnp.add(lbIp);
btnp.add(txtIp);
f.add(btnp,"South");
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
f.setSize(400,400);
f.setLocation(100,0);
f.setVisible(true);
}
public void connect(){
try{
int count = 0;
server = new ServerSocket(porttxt);
while(true){
client = server.accept();
while(count < clientlist.size()){
//如果存在该线程,启动
if(client.getLocalAddress().getHostAddress().equals(clientlist.get(count).getLocalAddress().getHostAddress())){
threadlist.get(count).start();
}
count++;
}
//如果不存在存在该线程,创建线程并启动
if(count >= clientlist.size()){
TxtThread thread = new TxtThread(this);
thread.start();
threadlist.add(thread);
}
}
}catch(IOException e){
e.getStackTrace();
}
}
public void close(){
try {
server.close();
//txtThread.interrupt();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public TextArea getTxtArea() {
return txtArea;
}
public ArrayList<Socket> getClientlist() { return clientlist;
}
public TextField getTxtField() {
return txtField;
}
public Socket getClient() {
return client;
}
public int getPorttxt() {
return porttxt;
}
public TextField getTxtIp() {
return txtIp;
}
public ArrayList<TxtThread> getThreadlist() { return threadlist;
}
}
class TxtThread extends Thread implements Runnable{
Server server;
Socket clientA;
public TxtThread(Server s){
this.server = s;
clientA = server.getClient();
}
public void run(){
try {
byte[] buf;
InputStream ins = clientA.getInputStream();
String info;
while(true){
buf = new byte[1024];
ins.read(buf);//读取客户端发送的信息
System.out.println(new String(buf));
info = new String(buf);
String ip = info.substring(0, info.indexOf("+", 0));
String sport = info.substring(ip.length()+1, info.indexOf("+",ip.length()+1));
int iport = Integer.parseInt(sport);
String content = info.substring(ip.length()+sport.length()+2,info.length()-1).trim();
if(ip.equals("服务器")){
/*******************************请求建立连接****************************************/
//server.getClientlist().add(clientA);
if(content.equals("请求连接")){
server.getTxtArea().append("\nip:"+clientA.getLocalAddress().getHostAddress
()+" 请求连接\n服务器同意建立连接\n");
if(server.getClientlist().indexOf(clientA) == -1){//如果该客户端没有连接,放入服务器的clientlist中
server.getClientlist().add(clientA);
clientA.getOutputStream().write("私+服务器同意建立连接".getBytes());//服务器返回消息
clientA.getOutputStream().flush();
continue;
}else{
clientA.getOutputStream().write("私+已建立过连接".getBytes());//服务器返回消息
clientA.getOutputStream().flush();
continue;
}
}else if(content.trim().equals("请求断开")){
/*******************************请求断开连接****************************************/
int count;
if((count = server.getClientlist().indexOf(clientA)) != -1){//如果该客户端已连接,断开连接,同时移出clientlist
server.getTxtArea().append("\nip:"+clientA.getLocalAddress().getHostAddress
()+" 请求断开连接\n服务器同意断开连接\n");
clientA.close();
server.getClientlist().remove(clientA);
server.getThreadlist().remove(count);
continue;
}else{
clientA.getOutputStream().write("私+没有建立过连接,不能断开".getBytes());//服务器返回消息
clientA.getOutputStream().flush();
continue;
}
}else{
/*******************************接受客户端发往服务器的消息****************************************/
server.getTxtArea().append("\nip:"+clientA.getLocalAddress().getHostAddress
()+" 向服务器发送消息:\n"+content+"\n");
continue;
}
}else if(ip.equals("")){
/*******************************接受客户端发往聊天室的消息(消息群发)****************************************/
int count = 0;
Socket clientB;
while(count < server.getClientlist().size()){
clientB=server.getClientlist().get(count); count++;
//不发给A客服端
if(!clientB.getLocalAddress().getHostAddress().equals(clientA.getLocalAddress().getHostAddress())){
clientA.getOutputStream().write(("公+"+clientA.getLocalAddress().getHostAddress()+":/n"+content).getBytes()); clientA.getOutputStream().flush();
}
}
server.getTxtArea().append("\n"+clientA.getLocalAddress().getHostAddress()+"在聊天室发送消息:\n"+content+"\n");
continue;
}else{
if(iport != server.getPorttxt()){
/*******************************服务器记录A客户端向B客户端发文件****************************************/
server.getTxtArea().append("\n"+clientA.getLocalAddress().getHostAddress()+ "向"+ip+" 用户发送文件:"+content+"\n");
int count = 0;
Socket clientB;
while(count < server.getClientlist().size()){ clientB=server.getClientlist().get(count); count++;
if(clientB.getLocalAddress().getHostAddress().equals(ip)){
//如果B客户端也与服务器建立连接,通知B客户端接收文件
clientB.getOutputStream().write(("私+"+clientA.getLocalAddress().getHostAddress()+"向您发送文件,请接收:\n"+content).getBytes());
clientB.getOutputStream().flush();
server.getTxtArea().append("\n"+clientA.getLocalAddress().getHostAddress()+ "向"+ip+" 用户发送消息成功!:\n"+content+"\n");
break;
}
}
if(count > server.getClientlist().size()){
//回复A客户端B没有建立连接
clientA.getOutputStream().write(("私+"+ip+"用户没有与服务器建立连接").getBytes());
server.getTxtArea().append("\n"+clientA.getLocalAddress().getHostAddress()+ "向"+ip+" 用户发送文件失败!:"+content+"\n"); continue;
}
continue;
}else{
/*******************************转发A客户端发往B客户端的消息****************************************/
int count=0;
Socket clientB;
while(count < server.getClientlist().size()){
clientB=server.getClientlist().get(count); count++;
if(clientB.getLocalAddress().getHostAddress().equals(ip)){
//如果B客户端也与服务器建立连接,则将消息转发给B
clientB.getOutputStream().write(("私+"+clientA.getLocalAddress().getHostAddress()+"向您发送消息:\n"+content).getBytes());
clientB.getOutputStream().flush();
server.getTxtArea().append("\n"+clientA.getLocalAddress().getHostAddress()+ "向"+ip+" 用户发送消息成功!:\n"+content+"\n");
break;
}
}
if(count > server.getClientlist().size()){
//回复A客户端B没有建立连接
clientA.getOutputStream().write(("私+"+ip+"用户没有与服务器建立连接").getBytes());
server.getTxtArea().append("\n"+clientA.getLocalAddress().getHostAddress()+ "向"+ip+" 用户发送消息失败!:"+content+"\n"); continue;
}
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class ServerListener implements ActionListener{
private Server server;
public ServerListener(Server s){
server = s;
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
int count = 1;
Socket client;
try {
while(count <= server.getClientlist().size()){
client=server.getClientlist().get(count-1);
count++;
if(client.getLocalAddress().getHostAddress().equals(server.getTxtIp().getText().trim())){
/*******************************服务器发往消息给客户端****************************************/
//如果B客户端与服务器建立连接,则将消息转发给B
client.getOutputStream().write(("私+服务器向自己发送消息:\n"+server.getTxtField().getText()+"\n").getBytes());
client.getOutputStream().flush();
server.getTxtArea().append("\n服务器向"+server.getTxtIp().getText()+
"用户发送消息成功!:\n"+server.getTxtField().getText()+"\n");
server.getTxtField().setText("");
return;
}
}
server.getTxtArea().append("\n服务器向"+server.getTxtIp().getText()+
"用户发送消息失败!:"+server.getTxtField().getText()+"\n"); }catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}