10月19日闲的蛋疼,做了个
仿微信的聊天工具
git地址: https://github.com/killinux/mysocket/tree/master/websocket/project/Test
ui参考的网上例子http://ios.9tech.cn/news/2013/1111/38520.html
服务端用 tomcat7的websocket
客户端
1.可以用浏览器
2.sockroket的ios客户端,ios8,开发工具xcode6
ios客户端代码如下代码:
ViewController.m
// // ViewController.m // BubbleDemo // // Created by xiao7 on 14/10/19. // Copyright (c) 2014年 killinux. All rights reserved. // #import "ViewController.h" @interface ViewController () @end @implementation ViewController NSString *websocket_url = @"ws://192.168.0.102:8080/webs/websocket/test"; NSString *myName = @"haoning"; NSString *toName = @"all"; - (void)viewDidLoad { [super viewDidLoad]; //----init data---begin---- // NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"weixin",@"name",@"这是一个测试",@"content", nil]; // NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:@"haoning",@"name",@"hello",@"content", nil]; // NSDictionary *dict7 = [NSDictionary dictionaryWithObjectsAndKeys:@"weixin",@"name",@",长数据测试。",@"content", nil]; // _resultArray = [NSMutableArray arrayWithObjects:dict,dict1, nil]; // [_resultArray addObject:dict7]; _resultArray = [[NSMutableArray alloc] init]; //----init data---end---- //websocket---begin--- _mywebSocket.delegate = nil; [_mywebSocket close]; _mywebSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:websocket_url]]]; _mywebSocket.delegate = self; [_mywebSocket open]; NSLog(@"open success!"); //websocket---end---- } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } //pragma websocket - (void)webSocketDidOpen:(SRWebSocket *)webSocket; { NSLog(@"Websocket Connected"); self.title = @"Connected!"; } - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error; { NSLog(@":( Websocket Failed With Error %@", error); _mywebSocket = nil; } - (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message; { NSLog(@"websocket Received \"%@\"", message); NSArray *messageArray = [message componentsSeparatedByString:@","]; if(messageArray.count<3){ NSLog(@"error parameter not right:%@",message); // NSDictionary *dict8 = [NSDictionary dictionaryWithObjectsAndKeys:@"haoning",@"name",message,@"content", nil]; // [_resultArray addObject:dict8]; }else{ NSDictionary *dict8 = [NSDictionary dictionaryWithObjectsAndKeys:messageArray[0],@"name",messageArray[2],@"content", nil]; [_resultArray addObject:dict8]; } [tableViewList reloadData]; } - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean; { NSLog(@"WebSocket closed"); self.title = @"Connection Closed! (see logs)"; _mywebSocket = nil; } //发送消息 - (IBAction)sendBubbleMessage:(id)sender { NSString *thistext = messageTxt.text; NSLog(@"sendBubbleMessage:%@",thistext); // NSDictionary *dict8 = [NSDictionary dictionaryWithObjectsAndKeys:@"haoning",@"name",thistext,@"content", nil]; // [_resultArray addObject:dict8]; // [tableViewList reloadData]; NSString *sendMessage =[myName stringByAppendingFormat:@",%@,%@",toName,thistext]; [_mywebSocket send:sendMessage]; messageTxt.text=nil; } //泡泡文本 - (UIView *)bubbleView:(NSString *)text from:(BOOL)fromSelf withPosition:(int)position{ //计算大小 UIFont *font = [UIFont systemFontOfSize:14]; CGSize size = [text sizeWithFont:font constrainedToSize:CGSizeMake(180.0f, 20000.0f) lineBreakMode:NSLineBreakByWordWrapping]; // build single chat bubble cell with given text UIView *returnView = [[UIView alloc] initWithFrame:CGRectZero]; returnView.backgroundColor = [UIColor clearColor]; //背影图片 UIImage *bubble = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:fromSelf?@"SenderAppNodeBkg_HL":@"ReceiverTextNodeBkg" ofType:@"png"]]; UIImageView *bubbleImageView = [[UIImageView alloc] initWithImage:[bubble stretchableImageWithLeftCapWidth:floorf(bubble.size.width/2) topCapHeight:floorf(bubble.size.height/2)]]; //NSLog(@"%f,%f",size.width,size.height); UILabel *bubbleText = [[UILabel alloc] initWithFrame:CGRectMake(fromSelf?15.0f:22.0f, 20.0f, size.width+10, size.height+10)]; bubbleText.backgroundColor = [UIColor clearColor]; bubbleText.font = font; bubbleText.numberOfLines = 0; bubbleText.lineBreakMode = NSLineBreakByWordWrapping; bubbleText.text = text; bubbleImageView.frame = CGRectMake(0.0f, 14.0f, bubbleText.frame.size.width+30.0f, bubbleText.frame.size.height+20.0f); if(fromSelf) returnView.frame = CGRectMake(320-position-(bubbleText.frame.size.width+30.0f), 0.0f, bubbleText.frame.size.width+30.0f, bubbleText.frame.size.height+30.0f); else returnView.frame = CGRectMake(position, 0.0f, bubbleText.frame.size.width+30.0f, bubbleText.frame.size.height+30.0f); [returnView addSubview:bubbleImageView]; [returnView addSubview:bubbleText]; return returnView; } //泡泡语音 - (UIView *)yuyinView:(NSInteger)logntime from:(BOOL)fromSelf withIndexRow:(NSInteger)indexRow withPosition:(int)position{ //根据语音长度 int yuyinwidth = 66+fromSelf; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.tag = indexRow; if(fromSelf) button.frame =CGRectMake(320-position-yuyinwidth, 10, yuyinwidth, 54); else button.frame =CGRectMake(position, 10, yuyinwidth, 54); //image偏移量 UIEdgeInsets imageInsert; imageInsert.top = -10; imageInsert.left = fromSelf?button.frame.size.width/3:-button.frame.size.width/3; button.imageEdgeInsets = imageInsert; [button setImage:[UIImage imageNamed:fromSelf?@"SenderVoiceNodePlaying":@"ReceiverVoiceNodePlaying"] forState:UIControlStateNormal]; UIImage *backgroundImage = [UIImage imageNamed:fromSelf?@"SenderVoiceNodeDownloading":@"ReceiverVoiceNodeDownloading"]; backgroundImage = [backgroundImage stretchableImageWithLeftCapWidth:20 topCapHeight:0]; [button setBackgroundImage:backgroundImage forState:UIControlStateNormal]; UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(fromSelf?-30:button.frame.size.width, 0, 30, button.frame.size.height)]; label.text = [NSString stringWithFormat:@"%d''",logntime]; label.textColor = [UIColor grayColor]; label.font = [UIFont systemFontOfSize:13]; label.textAlignment = NSTextAlignmentCenter; label.backgroundColor = [UIColor clearColor]; [button addSubview:label]; return button; } #pragma UITableView - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _resultArray.count; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *dict = [_resultArray objectAtIndex:indexPath.row]; UIFont *font = [UIFont systemFontOfSize:14]; CGSize size = [[dict objectForKey:@"content"] sizeWithFont:font constrainedToSize:CGSizeMake(180.0f, 20000.0f) lineBreakMode:NSLineBreakByWordWrapping]; return size.height+44; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; cell.selectionStyle = UITableViewCellSelectionStyleNone; }else{ for (UIView *cellView in cell.subviews){ [cellView removeFromSuperview]; } } NSDictionary *dict = [_resultArray objectAtIndex:indexPath.row]; //创建头像 UIImageView *photo ; if ([[dict objectForKey:@"name"]isEqualToString:@"haoning"]) { photo = [[UIImageView alloc]initWithFrame:CGRectMake(320-60, 10, 50, 50)]; [cell addSubview:photo]; photo.image = [UIImage imageNamed:@"photo1"]; if ([[dict objectForKey:@"content"] isEqualToString:@"0"]) { [cell addSubview:[self yuyinView:1 from:YES withIndexRow:indexPath.row withPosition:65]]; }else{ [cell addSubview:[self bubbleView:[dict objectForKey:@"content"] from:YES withPosition:65]]; } }else{ photo = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 50, 50)]; [cell addSubview:photo]; photo.image = [UIImage imageNamed:@"photo"]; if ([[dict objectForKey:@"content"] isEqualToString:@"0"]) { [cell addSubview:[self yuyinView:1 from:NO withIndexRow:indexPath.row withPosition:65]]; }else{ [cell addSubview:[self bubbleView:[dict objectForKey:@"content"] from:NO withPosition:65]]; } } return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { } @end
ViewController.h
// // ViewController.h // BubbleDemo // // Created by xiao7 on 14/10/19. // Copyright (c) 2014年 killinux. All rights reserved. // #import <UIKit/UIKit.h> #import "SocketRocket/SRWebSocket.h" @interface ViewController : UIViewController<SRWebSocketDelegate> { IBOutlet UITableView *tableViewList; IBOutlet UITextField *messageTxt; } @property (nonatomic, strong) NSMutableArray *resultArray; @property (nonatomic, strong) SRWebSocket *mywebSocket; @end
演示
chrome上
ios8系统上
html的客户端
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Insert title here</title> </head> <script type="text/javascript"> String.prototype.startWith=function(s){ if(s==null||s==""||this.length==0||s.length>this.length) return false; if(this.substr(0,s.length)==s) return true; else return false; return true; } Date.prototype.format = function(format) { var o = { "M+" : this.getMonth() + 1, //month "d+" : this.getDate(), //day "h+" : this.getHours(), //hour "m+" : this.getMinutes(), //minute "s+" : this.getSeconds(), //second "q+" : Math.floor((this.getMonth() + 3) / 3), //quarter "S" : this.getMilliseconds() } if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (this.getFullYear() + "") .substr(4 - RegExp.$1.length)); for ( var k in o) if (new RegExp("(" + k + ")").test(format)) format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)); return format; } var ws = null; function log(text) { /* document.getElementById("log").innerHTML = (new Date).getTime() + ": " + text + "<br>" + document.getElementById("log").innerHTML; */ document.getElementById("log").innerHTML = new Date().format('yyyy-MM-dd hh:mm:ss') + "," + text + "<br>" + document.getElementById("log").innerHTML; } function enterSend(){ if(event.keyCode == 13){ document.getElementById("sendbtn").click(); } } function startServer() { var url = document.getElementById("serverip").value;// "ws://192.168.0.102:8887"; if ('WebSocket' in window) { ws = new WebSocket(url); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(url); } else { log('浏览器不支持'); return; } ws.onopen = function() { log('唷嘻,连上了'); }; // 收到服务器发送的文本消息, event.data表示文本内容 ws.onmessage = function(event) { var thisdata = event.data; if(thisdata.startWith("open")){ //alert(thisdata); document.getElementById("username").value=thisdata.split(" ")[1]; }else{ //log(event.data); var showData=event.data.split(","); log(showData[0]+"说:"+showData[2]); } }; ws.onclose = function() { log('Closed! 刷新页面尝试连接.'); } //document.getElementById("conbtn").disabled = "true"; //document.getElementById("stopbtn").removeAttribute('disabled'); } function sendMessage() { var textMessage = document.getElementById("textMessage").value; var username = document.getElementById("username").value; var toUser = ""; if (ws != null && textMessage != "") { ws.send(username+","+toUser+","+textMessage); } document.getElementById("textMessage").value=""; } function stopconn() { ws.close(); //document.getElementById("conbtn").removeAttribute('disabled'); //document.getElementById("stopbtn").disabled = "true"; } </script> <body onload="startServer()"> <input id="serverip" type="text" size="20" value="ws://192.168.0.102:8080/webs/websocket/test" /> <!-- ws://192.168.0.102:8887 182.254.155.153 --> <!-- <input id="conbtn" type="button" onclick="startServer()" value="open" /> <input id="stopbtn" type="button" onclick="stopconn()" value="stop" disabled="disabled"/> --> </br> 您的名字:<input id="username" type="text" /></br></br> <input id="textMessage" type="text" size="20" onkeydown="enterSend()" style="border:1;width:400px" /> <input id="sendbtn" type="button" onclick="sendMessage()" value="Send"> <div id="log"></div> </body> </html>
java的tomcat7的后台:
package com.hao; import java.io.DataInputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Random; import javax.servlet.http.HttpServletRequest; import org.apache.catalina.websocket.MessageInbound; import org.apache.catalina.websocket.StreamInbound; import org.apache.catalina.websocket.WebSocketServlet; import org.apache.catalina.websocket.WsOutbound; public class HelloWorldWebSocketServlet extends WebSocketServlet { public static Map<String,MyMessageInbound> mmiList = new HashMap<String,MyMessageInbound>(); protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest arg1) { return new MyMessageInbound(); } public int getUserCount(){ return mmiList.size(); } private class MyMessageInbound extends MessageInbound { WsOutbound myoutbound; String mykey; @Override public void onOpen(WsOutbound outbound) { try { System.out.println("Open Client."); this.myoutbound = outbound; mykey ="open "+System.currentTimeMillis();; mmiList.put(mykey, this); System.out.println("mmiList size:"+mmiList.size()); outbound.writeTextMessage(CharBuffer.wrap(mykey)); } catch (IOException e) { e.printStackTrace(); } } @Override public void onClose(int status) { System.out.println("Close Client."); //mmiList.remove(this); mmiList.remove(mykey); } @Override protected void onBinaryMessage(ByteBuffer arg0) throws IOException { } @Override protected void onTextMessage(CharBuffer message) throws IOException { // TODO Auto-generated method stub System.out.println("onText--->" + message.toString()); // for (int i=0;i< mmiList.size();i++ ) { // MyMessageInbound mmib = (MyMessageInbound) mmiList.get(i); // CharBuffer buffer = CharBuffer.wrap(message); // mmib.myoutbound.writeTextMessage(buffer); // mmib.myoutbound.flush(); // } for (Map.Entry<String, MyMessageInbound> entry : mmiList.entrySet()) { //System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); MyMessageInbound mmib = (MyMessageInbound) entry.getValue(); // String str = entry.getKey()+message.toString() CharBuffer buffer = CharBuffer.wrap(message); mmib.myoutbound.writeTextMessage(buffer); mmib.myoutbound.flush(); } /*Socket socket; String msg = ""; try { // 向服务器利用Socket发送信息 socket = new Socket("192.168.0.102", 5000); // socket = new Socket("127.0.0.1",5000); PrintWriter output = new PrintWriter(socket.getOutputStream()); output.write(message.toString()); output.flush(); // 这里是接收到Server的信息 DataInputStream input = new DataInputStream( socket.getInputStream()); byte[] b = new byte[1024]; input.read(b); // Server返回的信息 msg = new String(b).trim(); output.close(); input.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 往浏览器发送信息 CharBuffer cb = CharBuffer.wrap(new StringBuilder(msg)); getWsOutbound().writeTextMessage(cb);*/ } } public static void main(String[] args) { Socket socket; String message = "haoning"; String msg = ""; try { // 向服务器利用Socket发送信息 socket = new Socket("192.168.0.102", 5000); // socket = new Socket("127.0.0.1",5000); PrintWriter output = new PrintWriter(socket.getOutputStream()); output.write(message.toString()); output.flush(); // 这里是接收到Server的信息 DataInputStream input = new DataInputStream(socket.getInputStream()); byte[] b = new byte[1024]; input.read(b); // Server返回的信息 msg = new String(b).trim(); output.close(); input.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
web.xml
<servlet> <servlet-name>wsSnake</servlet-name> <servlet-class>com.hao.HelloWorldWebSocketServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>wsSnake</servlet-name> <url-pattern>/websocket/test</url-pattern> </servlet-mapping>