Java Socket基础
较基础的Java Socket通信样例!
一、长连接通信
客户端和服务端建立通道后,Socket一直开着。两端通过read()等待消息。
1)简易聊天
服务端可主动对各个客户端发送消息,而各客户端只能单独发送给服务端。当然,只要你愿意,在服务端改下,即可转发了。
附件解压工程ChatSocket,运行TestServer、TestClient即可。src下还有两批处理,详细看TestServer文件内注释了。
1.1)ChatServer.java
- /**
- *长连接、1对n主动发消息的服务器
- *
- *@authorJoin
- */
- publicclassChatServerextendsThread{
- /**服务端口*/
- privateintport;
- /**服务套接字*/
- privateServerSocketmServerSocket;
- /**线程池*/
- privateExecutorServicepool;
- /**客户端套接字集合*/
- privateArrayList<Socket>mClientList;
- publicChatServer(intport){
- this.port=port;
- pool=Executors.newCachedThreadPool();//缓存线程池
- mClientList=newArrayList<Socket>();
- }
- @Override
- publicvoidrun(){
- try{
- mServerSocket=newServerSocket(port);//创建本地特定端口服务器套接字
- Socketclient=null;
- while(true){
- client=mServerSocket.accept();//接收连接的套接字
- mClientList.add(client);//加入客户端列表
- System.out.println(client.getInetAddress()+"建立了连接");
- pool.execute(newThreadServer(client));//新线程执行任务
- }
- }catch(BindExceptione){//端口使用中
- System.out.println("端口使用中,请关闭后重开!");
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- /**各个连接客户端的服务线程*/
- privateclassThreadServerextendsThread{
- privateSocketclient;
- publicThreadServer(Socketclient){
- this.client=client;
- }
- @Override
- publicvoidrun(){
- DataInputStreamin=null;
- DataOutputStreamout=null;
- try{
- in=newDataInputStream(client.getInputStream());
- out=newDataOutputStream(client.getOutputStream());
- BufferedReaderwt=newBufferedReader(newInputStreamReader(
- System.in));//控制台输入流
- while(true){
- if(in.available()>0){
- /*有数据时接收*/
- Stringstr=in.readUTF();
- System.out.println(str);
- /*接收end或空时,退出*/
- if(null==str||"end".equals(str)){
- System.out
- .println(client.getInetAddress()+"正常退出");
- break;
- }
- /*回复或转发已接收数据*/
- }else{
- if(wt.ready()){
- /*控制台有数据时发送*/
- Stringstr=wt.readLine();
- sendMessage(str);//群发消息
- }else{
- try{
- /*发送紧急数据,判断连接*/
- client.sendUrgentData(0xFF);
- Thread.sleep(100);
- }catch(Exceptione){
- System.out.println(client.getInetAddress()
- +"断开连接");
- break;
- }
- }
- }
- }
- }catch(SocketExceptione){//Connectionreset
- System.out.println(client.getInetAddress()+"异常退出");
- }catch(IOExceptione){
- e.printStackTrace();
- }finally{
- mClientList.remove(client);//移除当前客户端
- try{
- if(null!=in){
- in.close();
- }
- if(null!=out){
- out.close();
- }
- if(null!=client){
- client.close();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- }
- /**
- *群发信息
- *@throwsIOException
- */
- publicvoidsendMessage(Stringmsg)throwsIOException{
- DataOutputStreamout=null;
- for(Socketclient:mClientList){
- out=newDataOutputStream(client.getOutputStream());
- out.writeUTF(msg);
- out.flush();
- }
- }
- }
1.2)ChatClient.java
- /**
- *接收和发送消息的客户端
- *
- *@authorJoin
- */
- publicclassChatClientextendsThread{
- /**套接字地址*/
- privateSocketAddressaddress;
- /**超时时间*/
- privateinttimeout;
- publicChatClient(Stringhost,intport,inttimeout){
- this.address=newInetSocketAddress(host,port);
- this.timeout=timeout;
- }
- @Override
- publicvoidrun(){
- Socketsocket=newSocket();//创建未连接套接字
- try{
- //socket.setOOBInline(false);//默认即是false,表示不处理紧急数据
- socket.connect(address,timeout);//连接到服务器,并指定超时时间
- handleSocket(socket);//数据接收发送处理
- }catch(ConnectExceptione){//拒绝连接
- System.out.println("连接不上服务器");
- }catch(SocketTimeoutExceptione){//连接超时
- System.out.println("连接服务器超时");
- }catch(IOExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=socket){
- socket.close();//关闭Socket
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- privatevoidhandleSocket(Socketsocket){
- DataInputStreamin=null;
- DataOutputStreamout=null;
- try{
- in=newDataInputStream(socket.getInputStream());
- out=newDataOutputStream(socket.getOutputStream());
- BufferedReaderwt=newBufferedReader(newInputStreamReader(
- System.in));//控制台输入流
- while(true){
- if(wt.ready()){
- /*控制台有数据时发送*/
- Stringstr=wt.readLine();
- out.writeUTF(str);
- out.flush();
- /*发送end时,自身也退出*/
- if(str.equals("end")){
- break;
- }
- }else{
- try{
- /*发送紧急数据,判断连接*/
- socket.sendUrgentData(0xFF);
- Thread.sleep(100);
- }catch(Exceptione){
- System.out.println("服务器断开连接");
- break;
- }
- }
- /*有数据时接收并输出*/
- if(in.available()>0)
- System.out.println(in.readUTF());
- }
- }catch(SocketExceptione){//Connectionreset
- System.out.println("服务器异常");
- }catch(IOExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=in){
- in.close();
- }
- if(null!=out){
- out.close();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- }
2)对象消息
发送对象消息。服务端有LinkServer和LinkServer2两个,分别支持单客户端与多客户端,方式也有些不一样。
附件解压工程LinkedSocketB,.test包内Test开头的即是测试类了。服务端接收到客户端连接时,直接一个循环发送n个对象过去,没什么大问题^^。
2.1)LinkClient.java
- /**
- *只建立一个Socket,长连接Server接收数据。
- *
- *@authorJoin
- */
- publicclassLinkClientextendsThread{
- /**套接字地址*/
- privateSocketAddressaddress;
- /**超时时间*/
- privateinttimeout;
- /**客户端监听接口*/
- privateOnLinkClientListenerlistener;
- /**
- *构造客户端
- *
- *@paramhost服务器名称
- *@paramport服务器端口
- *@paramtimeout连接超时时间
- */
- publicLinkClient(Stringhost,intport,inttimeout){
- this.address=newInetSocketAddress(host,port);
- this.timeout=timeout;
- }
- @Override
- publicvoidrun(){
- Socketsocket=newSocket();//创建未连接套接字
- try{
- socket.connect(address,timeout);//连接到服务器,并指定超时时间
- if(null!=listener){
- listener.onConnected();
- }
- receiveObj(socket);//接收服务器数据
- }catch(ConnectExceptione){//拒绝连接
- if(null!=listener){
- listener.onConnectException();
- }
- }catch(SocketTimeoutExceptione){//连接超时
- if(null!=listener){
- listener.onTimeoutException();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=socket){
- socket.close();//关闭Socket
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- /**接收服务器发送的对象*/
- privatevoidreceiveObj(Socketsocket){
- //socket.shutdownOutput();//半关闭输出流
- ObjectInputStreamis=null;
- try{
- is=newObjectInputStream(newBufferedInputStream(
- socket.getInputStream()));
- /**循环接收对象*/
- while(true){
- Objectobj=is.readObject();
- if(null==obj){
- break;
- }
- if(null!=listener){
- listener.onReceive(obj);
- }
- }
- if(null!=listener){
- listener.onExited();
- }
- }catch(SocketExceptione){//Connectionreset
- if(null!=listener){
- listener.onSocketException();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }catch(ClassNotFoundExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=is){
- is.close();
- }
- if(null!=socket){
- socket.close();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- /**设置客户端监听接口*/
- publicvoidsetOnLinkClientListener(OnLinkClientListenerlistener){
- this.listener=listener;
- }
- }
2.2)LinkServer.java
- /**
- *开启服务器,长连接一个Socket,主动发送数据
- *
- *@authorJoin
- */
- publicclassLinkServerextendsThread{
- /**服务端口*/
- privateintport;
- /**服务套接字*/
- privateServerSocketmServerSocket;
- /**线程池*/
- privateExecutorServicepool;
- /**服务器监听接口*/
- privateOnLinkServerListenerlistener;
- /**同步对象*/
- privateObjectlockObj=newObject();
- /**是否等待*/
- privatebooleanisWaiting=false;
- /**发送对象集合*/
- privateArrayList<Object>sendObjList;
- publicLinkServer(intport){
- this.port=port;
- pool=Executors.newCachedThreadPool();//缓存线程池
- sendObjList=newArrayList<Object>();
- }
- @Override
- publicvoidrun(){
- try{
- mServerSocket=newServerSocket(port);//创建本地特定端口服务器套接字
- Socketclient=null;
- client=mServerSocket.accept();//接收连接的套接字
- if(null!=listener){
- listener.onClientConnected(client.getInetAddress());
- }
- pool.execute(newThreadServer(client));//新线程执行任务
- }catch(BindExceptione){//端口使用中
- if(null!=listener){
- listener.onBindException();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- /**各个连接客户端的服务线程*/
- privateclassThreadServerextendsThread{
- privateSocketclient;
- publicThreadServer(Socketclient){
- this.client=client;
- }
- @Override
- publicvoidrun(){
- ObjectOutputStreamos=null;
- try{
- os=newObjectOutputStream(client.getOutputStream());
- ObjectsendObj;
- synchronized(lockObj){
- while(true){
- if(sendObjList.size()<=0){
- isWaiting=true;
- lockObj.wait();
- }
- sendObj=sendObjList.get(0);
- os.writeObject(sendObj);
- os.flush();
- /*发送null时,表示退出了*/
- if(null==sendObj){
- if(null!=listener){
- listener.onExited(client.getInetAddress());
- }
- break;
- }
- sendObjList.remove(0);
- }
- }
- }catch(SocketExceptione){//Connectionreset
- if(null!=listener){
- listener.onSocketException(client.getInetAddress());
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }catch(InterruptedExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=os){
- os.close();
- }
- if(null!=client){
- client.close();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- }
- /**发送序列化对象*/
- publicvoidsendObj(Objectobj){
- /*这个判断非必需的,记得就好*/
- if(null!=obj&&!isSerializable(obj))
- thrownewIllegalArgumentException(
- "Objectneedstoimplementjava.io.Serializable!");
- sendObjList.add(obj);
- if(isWaiting){
- synchronized(lockObj){
- lockObj.notifyAll();
- }
- isWaiting=false;
- }
- }
- /**判断是否序列化*/
- privatebooleanisSerializable(Objectobj){
- Class<?>[]cls=obj.getClass().getInterfaces();
- for(Class<?>clazz:cls){
- if(clazz.getName().equals(Serializable.class.getName()))
- returntrue;
- }
- returnfalse;
- }
- /**设置服务器监听接口*/
- publicvoidsetOnLinkServerListener(OnLinkServerListenerlistener){
- this.listener=listener;
- }
- }
2.3)LinkServer2.java
- /**
- *开启服务器,长连接各个Socket,主动发送数据
- *
- *@authorJoin
- */
- publicclassLinkServer2extendsThread{
- /**服务端口*/
- privateintport;
- /**服务套接字*/
- privateServerSocketmServerSocket;
- /**服务器监听接口*/
- privateOnLinkServerListenerlistener;
- /**客户端套接字集合*/
- privateArrayList<Socket>clientList;
- /**客户端的输出流集合*/
- privateArrayList<ObjectOutputStream>osList;
- publicLinkServer2(intport){
- this.port=port;
- clientList=newArrayList<Socket>();
- osList=newArrayList<ObjectOutputStream>();
- }
- @Override
- publicvoidrun(){
- try{
- mServerSocket=newServerSocket(port);//创建本地特定端口服务器套接字
- Socketclient=null;
- while(true){
- client=mServerSocket.accept();//接收连接的套接字
- if(null!=listener){
- listener.onClientConnected(client.getInetAddress());
- }
- clientList.add(client);
- osList.add(newObjectOutputStream(client.getOutputStream()));//增加连接的输出流
- }
- }catch(BindExceptione){//端口使用中
- if(null!=listener){
- listener.onBindException();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- /**发送序列化对象*/
- publicvoidsendObj(Objectobj){
- /*这个判断非必需的,记得就好*/
- if(null!=obj&&!isSerializable(obj))
- thrownewIllegalArgumentException(
- "Objectneedstoimplementjava.io.Serializable!");
- ObjectOutputStreamout=null;
- for(inti=0;i<osList.size();i++){
- try{
- out=osList.get(i);
- out.writeObject(obj);
- out.flush();
- /*发送null时,表示退出了*/
- if(null==obj){
- if(null!=listener){
- listener.onExited(clientList.get(i).getInetAddress());
- }
- closeSocket(i);//关闭当前Socket
- i--;//少了个记得减
- }
- }catch(SocketExceptione){//Connectionreset
- if(null!=listener){
- listener.onSocketException(clientList.get(i)
- .getInetAddress());
- }
- closeSocket(i);//关闭当前Socket
- i--;//少了个记得减
- }catch(IOExceptione){
- e.printStackTrace();
- closeSocket(i);//关闭当前Socket
- i--;//少了个记得减
- }
- }
- }
- /**关闭某个Socket*/
- privatevoidcloseSocket(intindex){
- try{
- osList.get(index).close();
- osList.remove(index);
- clientList.get(index).close();
- clientList.remove(index);
- }catch(IOExceptione1){
- e1.printStackTrace();
- }
- }
- /**判断是否序列化*/
- privatebooleanisSerializable(Objectobj){
- Class<?>[]cls=obj.getClass().getInterfaces();
- for(Class<?>clazz:cls){
- if(clazz.getName().equals(Serializable.class.getName()))
- returntrue;
- }
- returnfalse;
- }
- /**设置服务器监听接口*/
- publicvoidsetOnLinkServerListener(OnLinkServerListenerlistener){
- this.listener=listener;
- }
- }
二、短连接通信
客户端请求一次,建立一次Socket,完成后即可关闭。服务端通过accept()等待。
1)对象消息
客户端发送对象,服务端接受对象,很简单的例子。
1.1)EasyClient.java
- /**
- *每次连接服务器发送一个消息
- *
- *@authorJoin
- */
- publicclassEasyClient{
- /**套接字地址*/
- privateSocketAddressaddress;
- /**超时时间*/
- privateinttimeout;
- /**客户端监听接口*/
- privateOnClientListenerlistener;
- publicEasyClient(Stringhost,intport,inttimeout){
- this.address=newInetSocketAddress(host,port);
- this.timeout=timeout;
- }
- /**
- *发送一个消息
- *
- *@paramobj对象消息
- */
- publicbooleansendMessage(Objectobj){
- booleanresult=false;
- Socketsocket=null;
- try{
- socket=newSocket();
- socket.connect(address,timeout);
- if(null!=listener){
- listener.onConnected();
- }
- result=sendMessage(socket,obj);
- }catch(ConnectExceptione){//拒绝连接
- if(null!=listener){
- listener.onConnectException();
- }
- }catch(SocketTimeoutExceptione){//连接超时
- if(null!=listener){
- listener.onTimeoutException();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=socket){
- socket.close();//关闭Socket
- socket=null;
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- returnresult;
- }
- /**通过Socket发送obj消息*/
- privatebooleansendMessage(Socketsocket,Objectobj){
- booleanresult=false;
- ObjectOutputStreamos=null;
- try{
- os=newObjectOutputStream(socket.getOutputStream());
- os.writeObject(obj);
- os.flush();
- if(null!=listener){
- listener.onMessageSent(obj);
- }
- result=true;
- }catch(SocketExceptione){//Connectionreset
- if(null!=listener){
- listener.onSocketException();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=os){
- os.close();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- returnresult;
- }
- /**设置客户端监听接口*/
- publicvoidsetOnClientListener(OnClientListenerlistener){
- this.listener=listener;
- }
- }
1.2)EasyServer.java
- publicclassEasyServerextendsThread{
- /**服务端口*/
- privateintport;
- /**服务套接字*/
- privateServerSocketmServerSocket;
- /**服务器监听接口*/
- privateOnServerListenerlistener;
- publicEasyServer(intport){
- this.port=port;
- }
- @Override
- publicvoidrun(){
- try{
- mServerSocket=newServerSocket(port);//创建本地特定端口服务器套接字
- Socketsocket=null;
- while(true){
- socket=mServerSocket.accept();//接收连接的套接字
- receiveMessage(socket);//接收socket处理消息
- }
- }catch(BindExceptione){//端口使用中
- if(null!=listener){
- listener.onBindException();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- /**接收处理每个Socket信息*/
- privatevoidreceiveMessage(Socketsocket){
- ObjectInputStreamis=null;
- try{
- is=newObjectInputStream(newBufferedInputStream(
- socket.getInputStream()));
- Objectobj=is.readObject();
- if(null!=listener){
- listener.onReceive(socket.getInetAddress(),obj);
- }
- }catch(SocketExceptione){//Connectionreset
- if(null!=listener){
- listener.onSocketException(socket.getInetAddress());
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }catch(ClassNotFoundExceptione){
- e.printStackTrace();
- }finally{
- try{
- if(null!=is){
- is.close();
- }
- if(null!=socket){
- socket.close();
- }
- }catch(IOExceptione){
- e.printStackTrace();
- }
- }
- }
- /**设置服务器监听接口*/
- publicvoidsetOnServerListener(OnServerListenerlistener){
- this.listener=listener;
- }
- }
三、后记
恩,哦,socket还有个比较常用的是setSoTimeout(),在read()阻塞超时会报SocketTimeoutException,catch住处理即可。
更多的如SSLSocket、NIO的简单例子,可以先
Click here!这个整理好的样例工程就不提供了,自己动手^^!
ps:老遇到8W冗余字符,拆着拆着就三个了==!