通信阶段已经开课好长一段时间了,但具体的知识点还不是非常的了解,这儿和大家一起学习下……
一)简单服务器的实现
在实现过程中出现的知识点:
1 ServerSocket server=new ServerSocket(int port);服务器对象的建立;(java.net.ServerSocket对象)
2 java.net.Socket client=server.accept();让服务器进入等待状态,等待客户机去连接
3 从Socket连结对象上调用方法得到输入输出流:
OutputStream out=client.getOutputStream();
InputStream ins=client.getInputStream();
4 注意从流中读取字节时有“阻塞”;telnet命令相当一个简单的客户端……
1.new ServerSocket(9090)
2. While(true){
Socket client=server.accept();(两处等待(阻塞))
5 服务器中写消息:
a 将ServerSocket的accept()调用户在一个循环中调用:进行一个客户机,当与这个客户机通信完毕后,服务器就再次进入循环中,重新调用accept等待下一下客户机连结进入,如下代码示例:
while(true){
Socket client=server.accept();//让服务器在while中等待:阻塞状态
b 在代码中我们是调用字符串的getBytes()方法,得到组成这个字符串的字节数组:
String s="Hello!";
byte[] data=s.getBytes();
再将字节转换成字符串:String src=new String(data);
具体的实现代码:
/**
* 简单Echo服务器实现
* @author linhongwei
*/
public class ChatServer {
/**
* 在指定端口上启动一个服务器
* @param port:服务器所以的端口
*/
public void setUpServer(int port){
try{
//1.建立绑定在指定端口上的服务器对象
java.net.ServerSocket server=new java.net.ServerSocket(port);
System.out.println("服务器创建成功!"+port);
//3.当有客户机连结上来时,等待方法就会返回,返回一个代表与客户机连结的对象
while(true){ //让服务器进处循环等待状态
java.net.Socket client=server.accept();
System.out.println("Incoming clieng:
"+client.getRemoteSocketAddress());
//调用处理连结对象的方法去处理连结对象
processChat(client);
}
}catch(Exception ef){
ef.printStackTrace();
}
}
/**
* 处理连结对象:读取客户机发来的字符串,回送给客户机
* @param client:与客户机的连结对象
*/
private void processChat(java.net.Socket client)
throws Exception{
OutputStream out=client.getOutputStream();
InputStream ins=client.getInputStream();
String s="你好,欢迎来到服务器!\r\n";
byte[] data=s.getBytes();//取得组成这个字符串的字节
out.write(data); //用输出对象发送!
out.flush();//强制输出
//调用读取字符串的方法,从输入流中读取一个字符串
String inputS=readString(ins);
while(!inputS.equals("bye")){
System.out.println("客户机说: "+inputS);
s="服务器收到:"+inputS+"\r\n";
data=s.getBytes();//取得组成这个字符串的字节
out.write(data); //用输出对象发送!
out.flush();//强制输出
inputS=readString(ins); //读取客户机的下一次输入
}
s="你好,欢迎再来!\r\n";
data=s.getBytes();
out.write(data);
out.flush();
client.close();//半闭与客户机的连结
}
/**
* 从输入流对象中读取字节,拼成一个字符串返加
* 如果读到一个字节值为13,则认为以前的是一个字符串
* @param ins:输入流对象
* @return :从流上(客户机发来的)读到的字符串
*/
private String readString(InputStream ins)
throws Exception{
//创建一个字符串缓冲区
StringBuffer stb=new StringBuffer();
char c =0;
while(c!=13){
//遇到一个换行,就是一句话
int i= ins.read();//读取客户机发来的一个字节
c=(char)i;//将输入的字节转换为一个Char
stb.append(c);
}
//将读到的字节组转为字符串,并调用trim去掉尾部的空格
String inputS=stb.toString().trim();
return inputS;
}
//程序入口
public static void main(String[] args) {
ChatServer cs=new ChatServer();
cs.setUpServer(9090);
}
}
二)多线程服务器的实现:
在独立线程中,循环从连上读取字符串,读到”bye”后,结束通信,线程退出.
其实现代码:
/**
* 服务器端处理与客户机连结对象的线程类封装
* @authorlinhongwei
*/
public class ServerThread extends Thread{
private java.net.Socket client;//线程对象要处理的连结变量
private OutputStream out;//输出流对象
/**创建这个线程对象时,传入一个它要处理的连结对象*/
public ServerThread(java.net.Socket sc){
this.client=sc;
}
//将发送消息的代码包装到一个方法中
public void sendMsg2Me(String msg)
throws Exception{
byte[] data=msg.getBytes();
out.write(data); //用输出对象发送!
out.flush();//强制输出
}
public void run(){
//在线程run中调用处理连结的方法
processChat(this.client);
//处理方法执行完毕后,线程自己即退出...
}
//处理客户机进入的连结对象
private void processChat(java.net.Socket client){
try{
out=client.getOutputStream(); //得到一个输出/输入流对象
InputStream ins=client.getInputStream();
String s="你好,欢迎来到服务器!\r\n";
this.sendMsg2Me(s);//发送消息
String inputS=readString(ins);//读取客户机发来的
while(!inputS.equals("bye")){
s="我收到了你的话啦 "+inputS+"\r\n";
this.sendMsg2Me(s); //将这个字符串发送给客户机对象
inputS=readString(ins);//再次读取
}
s="你好,欢迎再来!\r\n";
this.sendMsg2Me(s);
client.close();
}catch(Exception ef){
ef.printStackTrace();
}
}
/**
* 从输入流对象中读取字节,拼成一个字符串返加
* 如果读到一个字节值为13,则认为以前的是一个字符串
* @param ins:输入流对象
* @return :从流上(客户机发来的)读到的字符串
*/
private String readString(InputStream ins) throws Exception{
StringBuffer stb=new StringBuffer();//创建一个字符串缓冲区
char c =0;
while(c!=13){
//遇到一个换行,就是一句话
int i= ins.read();//读取客户机发来的一个字节
c=(char)i;//将输入的字节转换为一个Char
stb.append(c);
}
//将读到的字节组转为字符串
String inputS=stb.toString().trim();
return inputS;
}
}
然后,编写启动服务器,等待客户机连结进入的服务器类,其中的关键代码如下:
/**
* 在指定端口上启动一个服务器
* @param port:服务器所以的端口
*/
public void setUpServer(int port){
try{
//1.建立绑定在指定端口上的服务器对象
java.net.ServerSocket server=new java.net.ServerSocket(port);
System.out.println("服务器创建成功!"+port);
while(true){ //让服务器进处循环等待状态
java.net.Socket client=server.accept();
//创建了一个线程对象时传入进入的连结
ServerThread st=new ServerThread(client);
st.start();//启动这个线程,去处理连结
System.out.println("己启动了一个线程去处理这个连结对象了 ");
}
}catch(Exception ef){
ef.printStackTrace();
}
}
三) 群聊服务器实现:
实现过程:
1 注册登录……
2 判断登录成功与否……
3 进入聊天室等待……
4 向服务器和其他客户机发消息
5 当"bye”退出或短线时,离开聊天室
服务器端的代码需要五个类:
ChatServer.java:创建服务器并启动,等待连结,将进入的连结交给处理线程对象去处理;
ServerThread.java:处理Socket对象的线程类,每进入一个连结,就需要创建一个这个类的对象,在其run方法中负责与对应客户央的处理逻辑;从服务器的角度看:一个ServerThread对象,就对应了一个客户端;
ChatTools.java:服务器端的的辅助类,负责将某个客户机发来的消息转发给其它的客户机;在其中就必须保持一个队列对象;队列中存放了服务器生成的每一个ServerThread对象;
DaoTools.java:数据访问和验证类,负责生成模拟数据,并验证客户端帐号;
UserInfo.java:用户数据模型类,每个UserInfo类对象保存一个用户的帐号,身份信息;
客户端是搭档实现的,在这就不多说了……
四)XMPP:
什么样的格式算做一条消息?在以前是通过\r\n区分的;当我们以xml格式定义时,规定如下:
所以的消息包含在
<msg> . . . 具体的消息内容. . . </msg>
这样的格式中,即每条消息必须是以<msg>开头,以</msg>结尾。
那么如何区分一条消息是登陆还是聊天消息呢?
规定<msg>. . . </msg>xml串内的第一个元素必须是<type>类型关键字</type>,由类型关键
字区分是何种消息;<type>标记后的内容,则是具体消息格式的定义。
8种消息完整的定义格式如下:
1.登陆请求
<msg>
<type>login</type>
<name>用户</name>
<pwd>密码值</pwd>
</msg>
2.登陆应答:
<msg>
< type>loginResp</type>
<state>登陆结果</state><!--0:成功,其它:失败的错误码-->
</msg>
3.注册消息:
<msg>
<type>reg</type>
<name>用户</name>
<pwd>密码值</pwd>
</msg>
4.注册应答:
<msg>
<type>regResp</type>
<state>注册结果</state><!--0:成功,其它:失败的错误码-->
</msg>
5.聊天消息
<msg>
<type>chat</type>
<sender>发送者名字</sender><!--发送者名字-->
<reciver>接收者名字</reciver><!--接收者名字,如果为空,表示对所有用户发-->
<content>文本消息内容</content><发送的消息内容
</msg>
6.在线用户表消息
<msg>
<type>budyList</type>
<users>用户1,用户2,...</users><!--在线用户名字,多个以,分开-->
</msg>
7.上线消息:
<msg>
<type>onLine</type>
<user>用户2</user><!--上线者名字-->
</msg>
8.下线消息
<msg>
<type>offLine</type>
<user>用户2</user><!--上线者名字-->
</msg>
在本次操作中我们定下的协议时:
1.登陆
// 1.登陆请求:发送用户名,密码
this.setMsg2Server("<login><name>" + name + "</name><pwd>" + pwd+"</pwd></login><end>");
2.登陆回复
if (!loginState) {
// 不存在这个用户帐号则重新输入
setMessage("<loginResp>No Name</loginResp><end>");
} else if (isLogin) {
// 已登陆,则
setMessage("<loginResp>Logining</loginResp><end>");
} else {
setMessage("<loginResp>OK</loginResp><end>");
}
}
3.消息
sth.setMessage("<msg><sender>" + uName + "</sender><body>" + msg+ "</body></msg><end>");
一)简单服务器的实现
在实现过程中出现的知识点:
1 ServerSocket server=new ServerSocket(int port);服务器对象的建立;(java.net.ServerSocket对象)
2 java.net.Socket client=server.accept();让服务器进入等待状态,等待客户机去连接
3 从Socket连结对象上调用方法得到输入输出流:
OutputStream out=client.getOutputStream();
InputStream ins=client.getInputStream();
4 注意从流中读取字节时有“阻塞”;telnet命令相当一个简单的客户端……
1.new ServerSocket(9090)
2. While(true){
Socket client=server.accept();(两处等待(阻塞))
5 服务器中写消息:
a 将ServerSocket的accept()调用户在一个循环中调用:进行一个客户机,当与这个客户机通信完毕后,服务器就再次进入循环中,重新调用accept等待下一下客户机连结进入,如下代码示例:
while(true){
Socket client=server.accept();//让服务器在while中等待:阻塞状态
b 在代码中我们是调用字符串的getBytes()方法,得到组成这个字符串的字节数组:
String s="Hello!";
byte[] data=s.getBytes();
再将字节转换成字符串:String src=new String(data);
具体的实现代码:
/**
* 简单Echo服务器实现
* @author linhongwei
*/
public class ChatServer {
/**
* 在指定端口上启动一个服务器
* @param port:服务器所以的端口
*/
public void setUpServer(int port){
try{
//1.建立绑定在指定端口上的服务器对象
java.net.ServerSocket server=new java.net.ServerSocket(port);
System.out.println("服务器创建成功!"+port);
//3.当有客户机连结上来时,等待方法就会返回,返回一个代表与客户机连结的对象
while(true){ //让服务器进处循环等待状态
java.net.Socket client=server.accept();
System.out.println("Incoming clieng:
"+client.getRemoteSocketAddress());
//调用处理连结对象的方法去处理连结对象
processChat(client);
}
}catch(Exception ef){
ef.printStackTrace();
}
}
/**
* 处理连结对象:读取客户机发来的字符串,回送给客户机
* @param client:与客户机的连结对象
*/
private void processChat(java.net.Socket client)
throws Exception{
OutputStream out=client.getOutputStream();
InputStream ins=client.getInputStream();
String s="你好,欢迎来到服务器!\r\n";
byte[] data=s.getBytes();//取得组成这个字符串的字节
out.write(data); //用输出对象发送!
out.flush();//强制输出
//调用读取字符串的方法,从输入流中读取一个字符串
String inputS=readString(ins);
while(!inputS.equals("bye")){
System.out.println("客户机说: "+inputS);
s="服务器收到:"+inputS+"\r\n";
data=s.getBytes();//取得组成这个字符串的字节
out.write(data); //用输出对象发送!
out.flush();//强制输出
inputS=readString(ins); //读取客户机的下一次输入
}
s="你好,欢迎再来!\r\n";
data=s.getBytes();
out.write(data);
out.flush();
client.close();//半闭与客户机的连结
}
/**
* 从输入流对象中读取字节,拼成一个字符串返加
* 如果读到一个字节值为13,则认为以前的是一个字符串
* @param ins:输入流对象
* @return :从流上(客户机发来的)读到的字符串
*/
private String readString(InputStream ins)
throws Exception{
//创建一个字符串缓冲区
StringBuffer stb=new StringBuffer();
char c =0;
while(c!=13){
//遇到一个换行,就是一句话
int i= ins.read();//读取客户机发来的一个字节
c=(char)i;//将输入的字节转换为一个Char
stb.append(c);
}
//将读到的字节组转为字符串,并调用trim去掉尾部的空格
String inputS=stb.toString().trim();
return inputS;
}
//程序入口
public static void main(String[] args) {
ChatServer cs=new ChatServer();
cs.setUpServer(9090);
}
}
二)多线程服务器的实现:
在独立线程中,循环从连上读取字符串,读到”bye”后,结束通信,线程退出.
其实现代码:
/**
* 服务器端处理与客户机连结对象的线程类封装
* @authorlinhongwei
*/
public class ServerThread extends Thread{
private java.net.Socket client;//线程对象要处理的连结变量
private OutputStream out;//输出流对象
/**创建这个线程对象时,传入一个它要处理的连结对象*/
public ServerThread(java.net.Socket sc){
this.client=sc;
}
//将发送消息的代码包装到一个方法中
public void sendMsg2Me(String msg)
throws Exception{
byte[] data=msg.getBytes();
out.write(data); //用输出对象发送!
out.flush();//强制输出
}
public void run(){
//在线程run中调用处理连结的方法
processChat(this.client);
//处理方法执行完毕后,线程自己即退出...
}
//处理客户机进入的连结对象
private void processChat(java.net.Socket client){
try{
out=client.getOutputStream(); //得到一个输出/输入流对象
InputStream ins=client.getInputStream();
String s="你好,欢迎来到服务器!\r\n";
this.sendMsg2Me(s);//发送消息
String inputS=readString(ins);//读取客户机发来的
while(!inputS.equals("bye")){
s="我收到了你的话啦 "+inputS+"\r\n";
this.sendMsg2Me(s); //将这个字符串发送给客户机对象
inputS=readString(ins);//再次读取
}
s="你好,欢迎再来!\r\n";
this.sendMsg2Me(s);
client.close();
}catch(Exception ef){
ef.printStackTrace();
}
}
/**
* 从输入流对象中读取字节,拼成一个字符串返加
* 如果读到一个字节值为13,则认为以前的是一个字符串
* @param ins:输入流对象
* @return :从流上(客户机发来的)读到的字符串
*/
private String readString(InputStream ins) throws Exception{
StringBuffer stb=new StringBuffer();//创建一个字符串缓冲区
char c =0;
while(c!=13){
//遇到一个换行,就是一句话
int i= ins.read();//读取客户机发来的一个字节
c=(char)i;//将输入的字节转换为一个Char
stb.append(c);
}
//将读到的字节组转为字符串
String inputS=stb.toString().trim();
return inputS;
}
}
然后,编写启动服务器,等待客户机连结进入的服务器类,其中的关键代码如下:
/**
* 在指定端口上启动一个服务器
* @param port:服务器所以的端口
*/
public void setUpServer(int port){
try{
//1.建立绑定在指定端口上的服务器对象
java.net.ServerSocket server=new java.net.ServerSocket(port);
System.out.println("服务器创建成功!"+port);
while(true){ //让服务器进处循环等待状态
java.net.Socket client=server.accept();
//创建了一个线程对象时传入进入的连结
ServerThread st=new ServerThread(client);
st.start();//启动这个线程,去处理连结
System.out.println("己启动了一个线程去处理这个连结对象了 ");
}
}catch(Exception ef){
ef.printStackTrace();
}
}
三) 群聊服务器实现:
实现过程:
1 注册登录……
2 判断登录成功与否……
3 进入聊天室等待……
4 向服务器和其他客户机发消息
5 当"bye”退出或短线时,离开聊天室
服务器端的代码需要五个类:
ChatServer.java:创建服务器并启动,等待连结,将进入的连结交给处理线程对象去处理;
ServerThread.java:处理Socket对象的线程类,每进入一个连结,就需要创建一个这个类的对象,在其run方法中负责与对应客户央的处理逻辑;从服务器的角度看:一个ServerThread对象,就对应了一个客户端;
ChatTools.java:服务器端的的辅助类,负责将某个客户机发来的消息转发给其它的客户机;在其中就必须保持一个队列对象;队列中存放了服务器生成的每一个ServerThread对象;
DaoTools.java:数据访问和验证类,负责生成模拟数据,并验证客户端帐号;
UserInfo.java:用户数据模型类,每个UserInfo类对象保存一个用户的帐号,身份信息;
客户端是搭档实现的,在这就不多说了……
四)XMPP:
什么样的格式算做一条消息?在以前是通过\r\n区分的;当我们以xml格式定义时,规定如下:
所以的消息包含在
<msg> . . . 具体的消息内容. . . </msg>
这样的格式中,即每条消息必须是以<msg>开头,以</msg>结尾。
那么如何区分一条消息是登陆还是聊天消息呢?
规定<msg>. . . </msg>xml串内的第一个元素必须是<type>类型关键字</type>,由类型关键
字区分是何种消息;<type>标记后的内容,则是具体消息格式的定义。
8种消息完整的定义格式如下:
1.登陆请求
<msg>
<type>login</type>
<name>用户</name>
<pwd>密码值</pwd>
</msg>
2.登陆应答:
<msg>
< type>loginResp</type>
<state>登陆结果</state><!--0:成功,其它:失败的错误码-->
</msg>
3.注册消息:
<msg>
<type>reg</type>
<name>用户</name>
<pwd>密码值</pwd>
</msg>
4.注册应答:
<msg>
<type>regResp</type>
<state>注册结果</state><!--0:成功,其它:失败的错误码-->
</msg>
5.聊天消息
<msg>
<type>chat</type>
<sender>发送者名字</sender><!--发送者名字-->
<reciver>接收者名字</reciver><!--接收者名字,如果为空,表示对所有用户发-->
<content>文本消息内容</content><发送的消息内容
</msg>
6.在线用户表消息
<msg>
<type>budyList</type>
<users>用户1,用户2,...</users><!--在线用户名字,多个以,分开-->
</msg>
7.上线消息:
<msg>
<type>onLine</type>
<user>用户2</user><!--上线者名字-->
</msg>
8.下线消息
<msg>
<type>offLine</type>
<user>用户2</user><!--上线者名字-->
</msg>
在本次操作中我们定下的协议时:
1.登陆
// 1.登陆请求:发送用户名,密码
this.setMsg2Server("<login><name>" + name + "</name><pwd>" + pwd+"</pwd></login><end>");
2.登陆回复
if (!loginState) {
// 不存在这个用户帐号则重新输入
setMessage("<loginResp>No Name</loginResp><end>");
} else if (isLogin) {
// 已登陆,则
setMessage("<loginResp>Logining</loginResp><end>");
} else {
setMessage("<loginResp>OK</loginResp><end>");
}
}
3.消息
sth.setMessage("<msg><sender>" + uName + "</sender><body>" + msg+ "</body></msg><end>");