import java.util.HashMap; import java.util.Map; import Frame.Show; /** * 服务器主线程 * @author DQ */ public class Server { private int port; static Map<String, ServerThread> thread_map = new HashMap<String, ServerThread>(); public Server(int port) { this.port = port; } public void setupServer() { try { java.net.ServerSocket server = new java.net.ServerSocket(port); while (true) { java.net.Socket client = server.accept(); ServerThread thread = new ServerThread(client); thread.start(); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { Show s = new Show(); //Server s = new Server(9999); //s.setupServer(); } } import java.io.*; import java.util.Iterator; import java.util.Set; /** * 服务器处理得到的消息 * * @author Administrator * */ public class ServerThread extends Thread { private InputStream in; private OutputStream out; java.net.Socket client; private String myname =""; public ServerThread(java.net.Socket client) { this.client = client; try { in = client.getInputStream(); out = client.getOutputStream(); } catch (Exception e) { e.printStackTrace(); } } public void run() { try { this.send("欢迎登录!"); while (true) { String msg = this.readMsg();//得到消息 this.resp(msg);//处理得到消息,并做响应 } } catch (Exception e) { e.printStackTrace(); } } /** * 处理客户机发来的消息 * @param msg 一整条完整的消息 */ public void resp(String msg) { String type = this.getContent(msg, "type"); String resp = ""; //处理登录信息 if (type.equals("login")) { String name = this.getContent(msg, "name"); String pwd = this.getContent(msg, "pwd"); int state = -1; //如果登录成功 if(UserLogin.isLogin(name, pwd)){ myname = name; //将线程加入映射 Server.thread_map.put(name, this); state = 0; //给该客户发送登录反馈的消息 resp = "<msg><type>resplogin</type><state>"+state+"</state></msg>"; this.send(resp); //给所有客户发送该客户上线的消息 resp ="<msg><type>onLine</type><user>"+myname+"</user></msg>"; this.broadCast(resp); //给该客户发送在线用户列表 String names = this.getAll() ; resp ="<msg><type>bodyList</type><users>"+names+"</users></msg>"; this.send(resp); }else{ //给该客户发送登录反馈的消息 resp = "<msg><type>resplogin</type><state>"+state+"</state></msg>"; this.send(resp); } } //处理注册信息 if (type.equals("register")) { String name = this.getContent(msg, "name"); String pwd = this.getContent(msg, "pwd"); String repwd = this.getContent(msg, "repwd"); int state = -1; if(UserLogin.isResiger(name, pwd, repwd)){ state = 0; } //给该客户发送注册反馈的消息 resp = "<msg><type>respregi</type><state>"+state+"</state></msg>"; this.send(resp); } if (type.equals("chat") || type.equals("file")) { //得到目的的名字 String toName = this.getContent(msg, "to"); //将消息给目的端 Server.thread_map.get(toName).send(msg); } if (type.equals("broadcast")) { //将消息发给所以客户 this.broadCast(msg); } if (type.equals("offLine")) { Server.thread_map.remove(myname); //给所有客户发送该客户下线的消息 resp ="<msg><type>offLine</type><user>"+myname+"</user></msg>"; this.broadCast(resp); } } /**将处理应答发返回给该客户*/ public void send(String resp){ try { out.write(resp.getBytes()); out.flush(); } catch (IOException e) { e.printStackTrace(); } } /**给其他的所有的用户发送消息 */ public void broadCast(String msg){ Set<String> set = Server.thread_map.keySet(); Iterator<String> it = set.iterator(); while(it.hasNext()){ String name = it.next(); Server.thread_map.get(name).send(msg); } } /**得到在线用户列表 */ public String getAll(){ String names = ""; Set<String> set = Server.thread_map.keySet(); Iterator<String> it = set.iterator(); while(it.hasNext()){ String name = it.next(); if(name.equals(myname)){ continue; } names += ","+name ; } return names; } /** * 读取一个完整的msg * @return读到的完整的msg * @throws IOException */ public String readMsg() throws IOException { StringBuffer buffer = new StringBuffer(); int a = in.read(); String msg = ""; while (true) { char c = (char) a; buffer.append(c); msg = buffer.toString().trim(); if (msg.endsWith("</msg>")) { break; } a = in.read(); } msg = new String(msg.getBytes("ISO-8859-1"),"GBK").trim(); ///////this.send(msg);//回显,用于测试 return msg; } /** * 解析msg,得到标记中的内容 * @param msg 一条完整的消息 * @param type 标记名 * @return 标记中的内容 */ public String getContent(String msg, String type) { String content = ""; int begin = msg.indexOf("<" + type + ">"); int end = msg.indexOf("</" + type + ">"); begin = begin + 2 + type.length(); content = msg.substring(begin, end).trim(); return content; } } /** * 存放用户信息,服务器判断登录 * @author Administrator * */ public class UserLogin { static java.util.Map<String,String> user_map = new java.util.HashMap<String, String>();//存放用户名密码 /** * 判断登录是否成功 * @param name 用户名 * @param pwd 密码 * @return */ public static boolean isLogin(String name,String pwd){ if(user_map.containsKey(name) && user_map.get(name).equals(pwd)){ return true; } return false; } /** * 判断注册是否成功,成功后加入映射 * @param name * @param pwd * @return */ public static boolean isResiger(String name,String pwd,String repwd){ //用户名未被注册,并且密码重复正确,则申请成功 if((!user_map.containsKey(name)) && pwd.equals(repwd)){ user_map.put(name, pwd); return true; } return false; } } /** * 客户机处理线程类 * * @author Administrator */ public class Connect extends Thread { private InputStream in; private OutputStream out; Socket client; private JTextArea jta_recv;// 接收框 private JTextArea jta_send;// 发送框 private JComboBox jcb;// 用户显示框 public void run() { try { while (true) { this.deal(); } } catch (Exception e) { e.printStackTrace(); } } /** 登录成功后,处理服务器发回的消息 */ public void deal() throws IOException { String msg = this.readMsg(); String type = this.getContent(msg, "type"); // 聊天时 if (type.equals("chat")) { String from = this.getContent(msg, "from"); String word = this.getContent(msg, "content"); jta_recv.append(from + ":" + word + "\r\n"); } // 群聊时 if (type.equals("broadcast")) { String from = this.getContent(msg, "from"); String word = this.getContent(msg, "content"); jta_recv.append(from + "对大家说:" + word + "\r\n"); } //接到传文件请求 if(type.equals("file")){ } // 有用户上线时 if (type.equals("onLine")) { String userName = this.getContent(msg, "user"); jta_recv.append(userName + "上线了!\r\n"); jcb.addItem(userName); } // 有用户下线时 if (type.equals("offLine")) { String userName = this.getContent(msg, "user"); jta_recv.append(userName + "下线了!\r\n"); jcb.removeItem(userName); } // 登录时发送用户列表时 if (type.equals("bodyList")) { String users = this.getContent(msg, "users"); // 解析以,分割的在线用户名, java.util.StringTokenizer stk = new StringTokenizer(users, ","); while (stk.hasMoreTokens()) { String every = stk.nextToken(); jcb.addItem(every + " "); } } } /** 通过IP地址和端口号,与服务器建立链接 */ public boolean conn(String ip, int port) { try { client = new Socket(ip, port); in = client.getInputStream(); out = client.getOutputStream(); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** 发送登录消息,读取回应,返回结果 */ public boolean log(String name, String pwd) { String state = ""; try { String msg = "<msg><type>login</type><name>" + name + "</name><pwd>" + pwd + "</pwd></msg>"; this.send(msg); String resp = this.readMsg(); state = this.getContent(resp, "state"); } catch (Exception e) { e.printStackTrace(); } return state.equals("0"); } /** 发送注册消息,读取应答,返回结果 */ public boolean reg(String name, String pwd, String repwd) { String state = ""; try { String msg = "<msg><type>register</type><name>" + name + "</name><pwd>" + pwd + "</pwd><repwd>" + repwd + "</repwd></msg>"; this.send(msg); String resp = this.readMsg(); state = this.getContent(resp, "state"); } catch (Exception e) { e.printStackTrace(); } return state.equals("0"); } /** 发送聊天消息 */ public void chat(String fromUser, String toUser, String content) { String msg = "<msg><type>chat</type><from>" + fromUser + "</from><to>" + toUser + "</to><content>" + content + "</content></msg>"; this.send(msg); } /** 发送群聊消息 */ public void chat(String fromUser, String content) { String msg = "<msg><type>broadcast</type><from>" + fromUser + "</from><content>" + content + "</content></msg>"; this.send(msg); } /** 发送传送文件的消息 */ public void sendFile(String fromUser, String to, String fileName,String path) { String msg = "<msg><type>file</type><from>" + fromUser + "</from><to>" + to + "</to><name>" + fileName + "</name><path>"+path+"</path></msg>"; this.send(msg); } /** 发送下线消息,并关闭套接字 */ public void offLine(String user) { String msg = "<msg><type>offLine</type><user>" + user + "</user></msg>"; this.send(msg); this.close_client(); } /** 关闭套接字 */ public void close_client() { try { client.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** 给服务器发送一条消息 */ public void send(String msg) { try { out.write(msg.getBytes()); } catch (IOException e) { e.printStackTrace(); } } /** 读取服务器发来的一个完整的msg */ public String readMsg() throws IOException { StringBuffer buffer = new StringBuffer(); int a = in.read(); String msg = ""; while (true) { char c = (char) a; buffer.append(c); msg = buffer.toString().trim(); if (msg.endsWith("</msg>")) { break; } a = in.read(); } msg = new String(msg.getBytes("ISO-8859-1"), "GBK").trim(); return msg; } /** 解析msg,得到标记中的内容** msg一条完整的消息 type标记名 */ public String getContent(String msg, String type) { String content = ""; int begin = msg.indexOf("<" + type + ">"); int end = msg.indexOf("</" + type + ">"); begin = begin + 2 + type.length(); content = msg.substring(begin, end).trim(); return content; } /** 得到接收框和发送框 */ public void setArea(JTextArea jta_recv, JTextArea jta_send, JComboBox jcb) { this.jta_recv = jta_recv; this.jta_send = jta_send; this.jcb = jcb; } }
通信知识学习的关键就是要搞清楚客户端和服务器各自的功能和责任,理解建立连接过程中套接字和输入输出流的工作原理。在实际写代码过程中,把客户端和服务器分别放在两个project下,先用图的形式在纸上画清楚客户机和服务器之间交互的过程,例如:
|———| --------发起注册请求------------->|———|
| 客 |<-----回复注册请求应答---------- | 服 |
| 户 |--------发起登录请求-------------->| 务 |
|—端—| <-------回复登录请求应答--------| 器 |
显而易见,发起连接请求的是客户端,等待连接并回复的是服务器。二者的工作机制是建立在TCP/IP协议上的。
首先将服务器绑定在可提供连接的端口上,创建一个服务器,只需一句代码:
ServerSocket server = new ServerSocket(int port);// port:调用的端口号
端口号的范围是0~65535(2的16次幂个),其中0~1023号端口为周知端口,就是常用端口,例如:FTP运用的端口是21,邮件传输时运用的端口号是25,web网页用到的端口号是80,等。我们自己在写服务器的时候要用大于1023的端口号,避免端口被占用而不能成功的创建服务器。
服务器创建之后等待客户端发起链接。
Socket client = server.accept();//在此处阻塞,直到客户端发起链接,为了可以和多个客户端建立链接,而不是一个服务器只能服务于一个客户端,我们通常把等待客户机建立连接的过程放到一个循环中。
while(true){
Socket client = server.accept();
}
客户端与服务器建立连接也只需一句代码:
Socket s = new Socket(String ip,int port);//ip:服务器的ip地址;port:服务器调用的端口号
之后便是在服务器和客户机直接创建输入输出流。
//服务器端的输入输出流
InputStream in = client.getInputStream();
OutputStream out = client.getOutputStream();
//读取客户端发来的信息
In.read();
//服务器给客户端发回响应
String str ="服务器说,收到你信息了!";
Out.write(str.getBytes[]);
//客户端的输入输出流
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
//向客户端发一条消息
String str ="我是客户端,我们已经建立链接了!";
Out.write(str.getBytes[]);
//读取服务器发来的信息
In.read();
现在,我们已经建立好服务器和客户端,并且可以简单的通信了。之后要考虑的是客户机和服务器直接传递消息的协议。定义协议的目的是让服务器知道客户发来的请求是用来干什么的,让服务器明白应该怎样处理该消息,同时对于客户端来说,可以更好的理解服务器返回的应答该怎么处理。现定义协议如下:
<msg> < !-- 用于登录-->
<type>login</type>
<name>用户的名字</name>
<pwd>密码</pwd>
</msg>
<msg> < !-- 用于注册-->
<type>register</type>
<name>用户的名字</name>
<pwd>密码</pwd>
<repwd>重复密码</repwd>
</msg>
<msg> < !-- 用于登录应答-->
<type>resplogin</type>
<state>0</state> //状态为0时表示登录成功
</msg>
<msg> < !-- 用于注册应答-->
<type>respregi</type>
<state>0</state> //状态为0时表示登录成功
</msg>
<msg> < !-- 用于上线通知-->
<type>onLine</type>
<user>username</user>
</msg>
<msg> < !-- 用于下线通知-->
<type>offLine</type>
<user>username</user>
</msg>
<msg> < !-- 用于一对一聊天-->
<type>chat</type>
<from>用户的名字</from>
<to>用户的名字</to>
<content>聊天内容</content>
</msg>
<msg> < !-- 用于一对多聊天-->
<type>broadcast</type>
<from>用户的名字</from>
<content>聊天内容</content>
</msg>
<msg> <!--传文件的协议,未定义完-->
<type> file</type>
<state><state>
</msg>
以上协议的用法举例如下:当客户端点击登录按钮,发送登录请求时,获取登录界面输入的用户名name、密码pwd,然后包装成协议中的登录类型:
String msg ="<msg><type>login</type><name>"+name+
"</name><pwd>"+pwd +"</pwd></msg>";
Out.write(msg.getBytes());//发一整句话发出去
服务器接受到信息时,先进行解析,读取一整句msg,再读取type中的类型,之后进行处理。
在安装了SDK,搭建好android的环境之后,写了一个登录程序在虚拟机上进行测试。由于对写android程序不够熟悉,导致经常会被一个小问题卡住。例如:程序顺序的问题导致程序不能正常运行、报类型转换错误、空指针异常等。
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class server3 { public void startServer(int port, String path) { try { ServerSocket sc = new ServerSocket(port); System.out.println("服务器已启动,侦听客户端..."); //while (true) { Socket client = sc.accept(); System.out.println("ip:" + client.getInetAddress()); System.out.println(); InputStream in = client.getInputStream(); OutputStream out = client.getOutputStream(); DataInputStream dis = new DataInputStream(in); DataOutputStream dos = new DataOutputStream(out); FileInputStream file = new FileInputStream(path); DataInputStream image = new DataInputStream(file); int a = file.available(); byte[] b = new byte[a+1]; image.read(b); dos.writeInt(a); dos.write(b); System.out.println("b发送成功!"); //} } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { server3 s = new server3(); s.startServer(7777,"E:/01.jpg"); } } import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.widget.ImageView; public class third extends Activity { // 定义各个stream private InputStream in; private DataInputStream dis; private DataOutputStream dos; private OutputStream out; private ImageView view; // 初始化 public void init(String ip,int port) { try { Socket s = new Socket(ip, port); in = s.getInputStream(); out = s.getOutputStream(); dos = new DataOutputStream(out); dis = new DataInputStream(in); } catch (Exception e) { e.printStackTrace(); } } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); view =(ImageView)this.findViewById(R.id.view); this.init("192.168.1.101",7777); byte[] b = new byte[6185]; try { int i = 0; dis.readInt(); while(true){ byte a = dis.readByte(); b[i] = a; i++; } } catch (IOException e1) { e1.printStackTrace(); } System.out.println(b.length+b[3]); Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length); view.setImageBitmap(bitmap); //view.setImageDrawable(drawable); } }
<!--EndFragment-->