Silverlight使用JavaSocket连接jabber服务器
一、开发环境
Vs2010,Sl4,jdk6,MyEclipse8.5
二、Silverlight socket 使用 注意事项
1、Silverlight Socket 数据交换端口必须在4502-4534范围
2、必须创建一个Socket监听943端口(该端口是固定的,客户端策略请求固定发送到该端口)
三、Silverlight Socket 访问介绍流程图
四、服务器java端介绍以及代码
1、去官网下载 smack jar包 http://www.igniterealtime.org/downloads/source.jsp
2、引入jar包,如下:
3、编写socket 的服务器端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.XMPPConnection;
/**
* @author geolo
*
* @使用须知
* 1. 每一个Socket客服端连接成功后会驻留在后台,因此在Jabber退出时一定要关闭自己的Socket,为保持客户端正常连接,所以不能采用垃圾收集器 <br />
* 2. 连接Jabber的格式是 "userName,password".比如-->geolo,364200<-- 注意用“逗号”做分隔符 <br />
* 3. 关闭Socket的格式是"Socket_Exit," 比如-->Socket_Exit,<--注意用“逗号”做尾符 <br />
* 4. 如果Jabber登录成功会收到“LOGIN_TURE”提示 <br />
* 5. 如果Jabber登录失败会收到“LOGIN_FALSE”提示 <br />
* 6. 服务器Socket只有在重启的时候才会被关闭,否则一直保持等待客服端Socket登录状态。 <br />
* 7. 服务器的IP视本机地址而定。端口号位4502. <br />
* 8. 感谢使用.
*/
public class Server{
//服务器端口
private static final int SERVERPORT = 4502;
//客户端连接
private static List<Socket> mClientList = new ArrayList<Socket>();
//线程池
private ExecutorService mExecutorService;
//ServerSocket对象
private ServerSocket mServerSocket;
//Jabber连接对象
XMPPConnection xmppConnection;
//开启服务器
public static void main(String[] args){
new Server();
}
public Server(){
try{
//设置服务器端口
mServerSocket = new ServerSocket(SERVERPORT);
//创建一个线程池
mExecutorService = Executors.newCachedThreadPool();
System.out.println("start...");
try {
//用来临时保存客户端连接的Socket对象
Socket client = null;
while (true){
//接收客户连接并添加到list中
client = mServerSocket.accept();
mClientList.add(client);
//设置Jabber端口
xmppConnection = new XMPPConnection(new ConnectionConfiguration("wyu.0101.com.cn", 5222));
xmppConnection.connect();//连接Jabber
System.out.println("连接成功");
//开启一个客户端线程
mExecutorService.execute(new ThreadServer(xmppConnection,client));
}
} catch (Exception e) {
System.out.println("连接失败"); e.printStackTrace();
}
}
catch (IOException e){
System.out.println("登录失败3");
e.printStackTrace();
}
}
//每个客户端单独开启一个线程
static class ThreadServer implements Runnable{
private SocketmSocket;
private BufferedReadermBufferedReader;
private PrintWritermPrintWriter;
private StringmStrMSG;
XMPPConnection connection;
String isLogined;//登录是否成功
public ThreadServer(XMPPConnection xmppConnection,Socket socket) throws IOException{
if(this.mSocket != socket && this.connection != xmppConnection){
this.mSocket = socket;
this.connection = xmppConnection;
}
mBufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
sendMessage(mSocket,"Socket: "+this.mSocket.getInetAddress());
}
public void run(){
try{
while ((mStrMSG = mBufferedReader.readLine()) != null){
isLogined = "LOGIN_FALSE";
if(mStrMSG.indexOf(",") < 0){
sendMessage(mSocket,isLogined);
}else{
String strArray[] = mStrMSG.split(",");
if (strArray[0].trim().equalsIgnoreCase("Socket_Exit")){
sendMessage(mSocket,"Socket closed");
//当一个客户端退出时
mClientList.remove(mSocket);
mBufferedReader.close();
mPrintWriter.close();
mSocket.close();
connection.disconnect();
break;
}else{
try {
System.out.println("name: "+strArray[0]+" password: "+strArray[1]);
connection.login(strArray[0], strArray[1]);
isLogined = "LOGIN_TURE";
} catch (Exception e) {
isLogined = "LOGIN_FALSE";
System.out.println("错误2");
}
}
}
sendMessage(mSocket,isLogined);
}
}catch (Exception e){
System.out.println("错误3");
}
}
//发送消息给所对应的客户端
private void sendMessage(Socket client,String message) throws IOException{
mPrintWriter = new PrintWriter(client.getOutputStream(), true);
mPrintWriter.println(message);
}
}
}
4、运行 java Appcation
五、客服端 silverlight socket 介绍以及源码
1、界面设计如下:
2、界面代码
<UserControl x:Class="SlToJavaSocket.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid Name ="LayoutRoot" Background ="White" ShowGridLines ="True">
<Grid.RowDefinitions >
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions >
<TextBox x:Name="txtToSend" Grid.Row ="0"/>
<Button Grid.Row ="1" Click ="OnSend" Content ="Send" Margin ="20" />
</Grid >
</UserControl>
3、后台代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net.Sockets;
using System.Threading;
using System.Text;
namespace SlToJavaSocket
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
// 定义一个可在全局使用的Socket
System.Net.Sockets.Socket socket;
// 定义一个同步上下文类,用来将子线程的操作调度到主线程上以可控制UI 属性。
SynchronizationContext syn;
// 发送信息按钮的单击事件
void OnSend(object sender, RoutedEventArgs args)
{
// 定义一个字节数组,并将文本框的的类容转换为字节数组后存入
byte[] bytes = Encoding.UTF8.GetBytes(txtToSend.Text);
// 显示信息,可不要。
txtToSend.Text += "\r\nDnsSafeHost:" + Application.Current.Host.Source.DnsSafeHost;
// 将同步上下文设置在当前上下文(线程,主线程,可控制UI 的)
syn = SynchronizationContext.Current;
// 为socket 创建示例,并设置相关属性。
socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 定义并实例一个Socket 参数
SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs();
socketArgs.SocketClientAccessPolicyProtocol = SocketClientAccessPolicyProtocol.Tcp;
// 设置到远程终节点属性(4502 端口,为什么是4502 ,MS 的SL 通信安全上有)
//socketArgs.RemoteEndPoint = new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4510);
socketArgs.RemoteEndPoint = new DnsEndPoint(Application.Current.Host.Source.DnsSafeHost, 4502);
// 设置好当Socket 任何一个动作完成时的回调函数。
socketArgs.Completed += new EventHandler<SocketAsyncEventArgs>(socketArgs_Completed);
//Socket 参数的用户标识,实际上就是一个可以传递的OBJECT 参数。
socketArgs.UserToken = bytes;
// 执行连接。
MessageBox.Show( (socket.ConnectAsync(socketArgs)).ToString());
}
void socketArgs_Completed(object sender, SocketAsyncEventArgs e)
{
// 当任何一个Socket 动作完成,都回调该函数,然后对LastOperation 进行判断后继续执行相应的部分
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
ProcessConnect(e);
break;
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
}
}
// 将数据放入buffer 并进行异步发送
void ProcessConnect(SocketAsyncEventArgs e)
{
// 当连接成功后,获取Socket 参数 e 传递过来的用户标识(也就是本示例中用户输入的字符串转换的Byte 字节数组)
byte[] bytes = (byte[])e.UserToken;
// 设置Socket 参数的缓冲区参数,将我们的字节数组设置为Socket 的缓冲区。
e.SetBuffer(bytes, 0, bytes.Length);
// 同步一下上下文,显示一下当前的状态信息。
syn.Post(GetText, "States:" + e.SocketError.ToString() + "," + e.LastOperation.ToString());
// 发送数据
socket.SendAsync(e);
}
// 发送完成后,执行等待接收服务器发回的数据
void ProcessSend(SocketAsyncEventArgs e)
{
// 定义个空的字节数组,设置好其大小
byte[] bytes = new byte[1024];
// 将前面定义字节数组设置成缓冲区
e.SetBuffer(bytes, 0, bytes.Length);
// 执行异步接收
socket.ReceiveAsync(e);
}
// 当接收完成后
void ProcessReceive(SocketAsyncEventArgs e)
{
// 在执行好接收后,本地SOCKET 的缓冲区就会被服务器发送的数据填充。
// 显示下信息,当然也是用同步上下文的方式,在显示信息的时候,就直接将缓冲区的字节数组转换成字符串。
syn.Post(GetText, Encoding.UTF8.GetString(e.Buffer, 0, e.Buffer.Length) + " and Received");
// 关闭Socket 连接
socket.Close();
// 最后显示下,Socket 关闭。
syn.Post(GetText, "Socket Closed");
}
// 同步上下文调用的方法。
void GetText(object str)
{
txtToSend.Text += "\r\n" + str.ToString();
}
}
}
六、运行效果
1、Silverlight端运行效果:
2、java端运行效果
3、恭喜,连上 服务器(lxf,lxf 是指我在服务器上注册的用户名和密码)
七、注意事项
1、把 策略文件放到 服务器 943 端口 (类似于开个socket , 发送 策略文件)
2、Silverlight 对外端口 4502-4534 (设置超过或小于后果自负)
3、如果是win7 系统的一定要注意 IIS中 用户的权限(我就在这悲剧了半天~)