这篇文章主要围绕java网络编程-TCP-多人群聊究极版和java多人聊天展开,旨在为您提供一份详细的参考资料。我们将全面介绍java网络编程-TCP-多人群聊究极版的优缺点,解答java多人聊天的相
这篇文章主要围绕java 网络编程-TCP-多人群聊究极版和java 多人聊天展开,旨在为您提供一份详细的参考资料。我们将全面介绍java 网络编程-TCP-多人群聊究极版的优缺点,解答java 多人聊天的相关问题,同时也会为您带来(OK) mim-tcp-mdr - (使用 原始 quagga 和 tcp) - android-x86-7.1-rc2 - kernel-4.9.54 (开启 ROUTE_MULTIPATH)、ACE 网络编程:IPC SAP、ACE_SOCKET 和 TCP/IP 通信实例、android – 什么’adb forward tcp:8080 tcp:8080’命令呢?、Android 网络编程 TCP 与 UDP的实用方法。
本文目录一览:- java 网络编程-TCP-多人群聊究极版(java 多人聊天)
- (OK) mim-tcp-mdr - (使用 原始 quagga 和 tcp) - android-x86-7.1-rc2 - kernel-4.9.54 (开启 ROUTE_MULTIPATH)
- ACE 网络编程:IPC SAP、ACE_SOCKET 和 TCP/IP 通信实例
- android – 什么’adb forward tcp:8080 tcp:8080’命令呢?
- Android 网络编程 TCP 与 UDP
java 网络编程-TCP-多人群聊究极版(java 多人聊天)
多个客户可以正常收发信息,因为可以同时发送和接受信息,不是发送完信息后等待
返回信息,所以要加入多线程
public class Client { public static void main(String[]args) throws UnkNownHostException,IOException { System.out.println("客户端启动中..."); BufferedReader br =new BufferedReader(new InputStreamReader(system.in)); System.out.println("请输入用户名:"); String name=br.readLine(); Socket client =new Socket("localhost",9999); new Thread(new Send(client,name)).start();//在线程还没启动之前,通过构造方法发送名称,先于信息的发送, //因为接收信息时,名称已经发送并被读取到变量中 new Thread(new Receive(client)).start(); } }
作为释放资源工具类
public class utils { public static void close(Closeable... target ) { for(Closeable it:target) { try { if(null!=it) { it.close(); } } catch (IOException e) { e.printstacktrace(); } } } }
客户端的发送端封装了:
发送消息
从键盘读取消息
run()
释放资源
public class Send implements Runnable{ private BufferedReader br; private DataOutputStream dos; private Socket client; private boolean flag=true; private String name; public Send(Socket client,String name) { this.client=client; this.name=name; br=new BufferedReader(new InputStreamReader(system.in)); try { dos=new DataOutputStream(client.getoutputStream()); //发送名称 send(name); } catch (IOException e) { this.release(); } } public void run() { while(flag) { String msg=getstr(); send(msg); } } private void release() { this.flag=false; utils.close(dos,client); } //从控制台获取消息 private String getstr() { try { String msg=br.readLine(); return msg; } catch (IOException e) { e.printstacktrace(); } return null; } private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { release(); } } }
客户端的接收端封装了:
接收消息
run()
释放资源
public class Receive implements Runnable { private DataInputStream dis; private Socket client; private boolean flag=true; public Receive(Socket client) { this.client=client; try { dis=new DataInputStream(client.getInputStream()); } catch (IOException e) { release(); } } private String receive() { String msg; try { msg = dis.readUTF(); return msg; } catch (IOException e) { release(); } return null; } public void run() { while(flag) { String msg=receive(); System.out.println(msg); } } private void release() { this.flag=false; utils.close(dis,client); } }
在线聊天室
服务器:
public class Chat { private static copyOnWriteArrayList<channel> list=new copyOnWriteArrayList<>();//使用并发容器,适合多线程 public static void main(String[]args) throws IOException { System.out.println("服务器启动中..."); ServerSocket server=new ServerSocket(9999); while(true) { Socket client =server.accept(); System.out.println("一个客户端建立了连接"); channel c=new channel(client); list.add(c); //容器管理所有成员 new Thread(c).start(); } } static class channel implements Runnable{ private DataInputStream dis; private DataOutputStream dos; private BufferedReader br; private Socket client; private boolean flag; private String name; public channel(Socket client) { this.client=client; try { dis=new DataInputStream(client.getInputStream()); dos=new DataOutputStream(client.getoutputStream()); flag=true; //获取名称 this.name=receive(); //欢迎到来 this.send("欢迎来到聊天室"); this.sendothers(this.name+"来到了聊天室",true); } catch (IOException e) { release(); } } //接收消息 private String receive() { try { String msg=dis.readUTF(); return msg; } catch (IOException e) { release(); } return null; } //发送给消息 private void send(String msg) { try { dos.writeUTF(msg); dos.flush(); } catch (IOException e) { release(); } } //群聊:获取自己的消息,发给其他人和私聊功能 private void sendothers(String msg,boolean issys) { boolean issecrete =msg.startsWith("@"); if(issecrete) { int index=msg.indexOf(":"); String target=msg.substring(1,index); msg=msg.substring(index+1); for(channel it:list) { if(it.name.equals(target)) //查找目标 { it.send(this.name+"悄悄地对你说:"+msg); //私聊消息 break; } } }else { for(channel it:list) { if(it==this) { continue; } if(!issys) { it.send(this.name+":"+msg); }else { it.send(msg); } } } } private void release() { this.flag=false; utils.close(dis,dos,client); //写一个工具类,使用Closeable...可变参数 //退出时,从容器里面移掉自身 list.remove(this); sendothers("离开了聊天室",false); } public void run() { while(flag) { String msg=receive(); sendothers(msg,false); } release(); } } }
(OK) mim-tcp-mdr - (使用 原始 quagga 和 tcp) - android-x86-7.1-rc2 - kernel-4.9.54 (开启 ROUTE_MULTIPATH)
# 如果不开启 kernel 的 ROUTE_MULTIPATH 多路径,
# 则,ip route show (can not show multiple hops to one destination)
---------------------------------------------------------
vim out/target/product/x86_64/obj/kernel/.config
--------------------------------------------------------- look
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
# CONFIG_IP_FIB_TRIE_STATS is not set
CONFIG_IP_MULTIPLE_TABLES=y
# CONFIG_IP_ROUTE_MULTIPATH is not set
# CONFIG_IP_ROUTE_VERBOSE is not set
CONFIG_IP_PNP=y
CONFIG_IP_PNP_DHCP=y
---------------------------------------------------------
---------------------------------------------------------
vim kernel/arch/x86/configs/android-x86_64_defconfig
--------------------------------------------------------- use
# CONFIG_IP_ROUTE_MULTIPATH is not set
# CONFIG_IP_ROUTE_VERBOSE is not set
修改为
CONFIG_IP_ROUTE_MULTIPATH=y
CONFIG_IP_ROUTE_VERBOSE=y
---------------------------------------------------------
ACE 网络编程:IPC SAP、ACE_SOCKET 和 TCP/IP 通信实例
socket、TLI、STREAM 管道和 FIFO 为访问局部和全局 IPC 机制提供广泛的接口。但是,有许多问题与这些不统一的接口有关联。比如类型安全的缺乏和多维度的复杂性会导致成问题的和易错的编程。ACE 的 IPC SAP 类属提供了统一的层次类属,对那些麻烦而易错的接口进行封装。在保持高性能的同时,IPC SAP 被设计用于改善通信软件的正确性、易学性、可移植性和可复用性。
IPC SAP 类属
根据底层使用的不同 IPC 接口,IPC SAP 类被划分为四种主要的类属,图 2-1 描述了这一划分。ACE_IPC_SAP 类提供的一些函数是所有 IPC 接口公有的。有四个不同的类由此类派生而出,每个类各自代表 ACE 包含的一种 IPC SAP 包装类属。这些类封装适用于特定 IPC 接口的功能。例如,ACE_SOCK 类包含的功能适用于 BSD socket 编程接口,而 ACE_TLI 包装 TLI 编程接口。
在这四个类的每一个类下面都有一整层次的包装类,它们完整地包装底层接口,并提供高度可复用、模块化、安全和易用的包装类。下面重点介绍一下最常用的 socket 类属。
socket 类属(ACE_SOCK)
该类属中的类都位于 ACE_SOCK 之下;它提供使用 BSD socket 编程接口的 Internet 域和 UNIX 域协议族的接口。这个类属中的类被进一步划分为:
Dgram 类和 Stream 类:Dgram 类基于 UDP 数据报协议,提供不可靠的无连接消息传递功能。另一方面,Stream 类基于 TCP 协议,提供面向连接的消息传递。
Acceptor、Connector 类和 Stream 类:Acceptor 和 Connector 类分别用于被动和主动地建立连接。Acceptor 类封装 BSD accept () 调用,而 Connector 封装 BSD connect () 调用。Stream 类用于在连接建立之后提供双向的数据流,并包含有发送和接收方法。
下表详细描述了该类属中的类以及它们的职责:
类名 |
职责 |
ACE_SOCK_Acceptor |
用于被动的连接建立,基于 BSD accept () 和 listen () 调用。 |
ACE_SOCK_Connector |
用于主动的连接建立,基于 BSD connect () 调用。 |
ACE_SOCK_Dgram |
用于提供基于 UDP(用户数据报协议)的无连接消息传递服务。封装了 sendto () 和 receivefrom () 等调用,并提供了简单的 send () 和 recv () 接口。 |
ACE_SOCK_IO |
用于提供面向连接的消息传递服务。封装了 send ()、recv() 和 write () 等调用。该类是 ACE_SOCK_Stream 和 ACE_SOCK_CODgram 类的基类。 |
ACE_SOCK_Stream |
用于提供基于 TCP(传输控制协议)的面向连接的消息传递服务。派生自 ACE_SOCK_IO,并提供了更多的包装方法。 |
ACE_SOCK_CODgram |
用于提供有连接数据报(connected datagram)抽象。派生自 ACE_SOCK_IO;它包含的 open () 方法使用 bind () 来绑定到指定的本地地址,并使用 UDP 连接到远地地址。 |
ACE_SOCK_Dgram_Mcast |
用于提供基于数据报的多点传送 (multicast) 抽象。包括预订多点传送组,以及发送和接收消息的方法 |
ACE_SOCK_Dgram_Bcast |
用于提供基于数据报的广播 (broadcast) 抽象。包括在子网中向所有接口广播数据报消息的方法
|
在下面的部分,我们将要演示怎样将 IPC_SAP 包装类直接用于处理进程间通信。
使用 ACE 的流
ACE 中的流包装提供面向连接的通信。流数据传输包装类包括 ACE_SOCK_Stream 和 ACE_LSOCK_Stream,它们分别包装 TCP/IP 和 UNIX 域 socket 协议数据传输功能。连接建立类包括针对 TCP/IP 的 ACE_SOCK_Connector 和 ACE_SOCK_Acceptor,以及针对 UNIX 域 socket 的 ACE_LSOCK_Connector 和 ACE_LSOCK_Acceptor。
Acceptor 类用于被动地接受连接(使用 BSD accept () 调用),而 Connector 类用于主动地建立连接(使用 BSD connect () 调用)。
下面的例子演示接收器和连接器是怎样用于建立连接的。该连接随后将用于使用流数据传输类来传输数据。
#include "ace/SOCK_Acceptor.h"
#include "ace/SOCK_Stream.h"
#include "ace/Log_Msg.h"
#define SIZE_DATA 18
#define SIZE_BUF 1024
#define NO_ITERATIONS 5
class Server
{
public:
///初始化
Server(int port): server_addr_(port),peer_acceptor_(server_addr_)
{
data_buf_= new char[SIZE_BUF];
}
///建立监听
int accept_connections()
{
if (peer_acceptor_.get_local_addr (server_addr_) == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"%p\n","Error in get_local_addr"),1);
ACE_DEBUG ((LM_DEBUG,"Starting server at port %d\n",
server_addr_.get_port_number ()));
}
while(1)
{
ACE_Time_Value timeout (ACE_DEFAULT_TIMEOUT);
if (peer_acceptor_.accept (new_stream_, &client_addr_, &timeout)== -1)
{
ACE_ERROR ((LM_ERROR, "%p\n", "accept"));
continue;
}
else
{
ACE_DEBUG((LM_DEBUG,
"Connection established with remote %s:%d\n",
client_addr_.get_host_name(),client_addr_.get_port_number()));
handle_read();
}
}
}
///读取流内容
int handle_read()
{
for(int i=0;i<NO_ITERATIONS;i++)
{
int byte_count=0;
if( (byte_count=new_stream_.recv_n (data_buf_, SIZE_DATA, 0))==-1)
{
ACE_ERROR ((LM_ERROR, "%p\n", "Error in recv"));
}
else
{
data_buf_[byte_count]=0;
ACE_DEBUG((LM_DEBUG,"Server received %s \n",data_buf_));
}
}
if (new_stream_.close () == -1)
{
ACE_ERROR ((LM_ERROR, "%p\n", "close"));
}
return 0;
}
private:
char *data_buf_;
ACE_INET_Addr server_addr_;//地址
ACE_INET_Addr client_addr_;
ACE_SOCK_Acceptor peer_acceptor_;
ACE_SOCK_Stream new_stream_;//流
};
int ACE_TMAIN (int, ACE_TCHAR *[])
{
Server server(8002);
server.accept_connections();
return 0;
};
上面的例子创建了一个被动服务器,侦听到来的客户连接。在连接建立后,服务器接收来自客户的数据,然后关闭连接。Server 类表示该服务器。
Server 类包含的 accept_connections () 方法使用接受器(也就是 ACE_SOCK_Acceptor)来将连接接受 “进” ACE_SOCK_Stream new_stream_。该操作是这样来完成的:调用接受器上的 accept (),并将流作为参数传入其中;我们想要接受器将连接接受进这个流。一旦连接已建立进流中,流的包装方法 send () 和 recv () 就可以用来在新建立的链路上发送和接收数据。还有一个空的 ACE_INET_Addr client_addr_也被传入接受器的 accept () 方法,并在其中被设定为发起连接的远地机器的地址。
在连接建立后,服务器调用 handle_read () 方法,它开始从客户那里读取一个预先知道的单词,然后将流关闭。对于要处理多个客户的服务器来说,这也许并不是很实际的情况。在现实世界的情况中可能发生的是,连接在单独的线程或进程中被处理。在后续章节中将反复演示怎样完成这样的多线程和多进程类型的处理。
连接关闭通过调用流上的 close () 方法来完成,该方法会释放所有的 socket 资源并终止连接。
下面的例子演示怎样与前面例子中演示的接受器协同使用连接器。
#include "ace/SOCK_Connector.h"
#include "ace/INET_Addr.h"
#include "ace/Log_Msg.h"
#define SIZE_BUF 128
#define NO_ITERATIONS 5
class Client
{
public:
Client(char *hostname, int port):remote_addr_(port,hostname)
{
data_buf_="Hello from Client";
}
int connect_to_server()
{
ACE_DEBUG ((LM_DEBUG, "(%P|%t) Starting connect to %s:%d\n",
remote_addr_.get_host_name(),remote_addr_.get_port_number()));
if (connector_.connect (client_stream_, remote_addr_) == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","connection failed"),-1);
}
else
{
ACE_DEBUG ((LM_DEBUG,"(%P|%t) connected to %s\n",
remote_addr_.get_host_name ()));
}
return 0;
}
int send_to_server()
{
for(int i=0;i<NO_ITERATIONS; i++)
{
if (client_stream_.send_n (data_buf_,
ACE_OS::strlen(data_buf_)+1, 0) == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","send_n"),0);
break;
}
}
close();
}
int close()
{
if (client_stream_.close () == -1)
{
ACE_ERROR_RETURN ((LM_ERROR,"(%P|%t) %p\n","close"),-1);
}
else
{
return 0;
}
}
private:
ACE_SOCK_Stream client_stream_;
ACE_INET_Addr remote_addr_;
ACE_SOCK_Connector connector_;
char *data_buf_;
};
int ACE_TMAIN (int, ACE_TCHAR *[])
{
Client client("127.0.0.1", 8002);
client.connect_to_server();
client.send_to_server();
return 0;
};
上面的例子演示的客户主动连接到服务器。在建立连接后,客户将单个字符串发送若干次到服务器,并关闭连接。
客户由单个 Client 类表示。Client 含有 connect_to_server () 和 send_to_server () 方法。
Connect_to_server () 方法使用类型为 ACE_SOCK_Connector 的连接器(connector_)来主动地建立连接。连接的设置通过调用连接器 connector_上的 connect () 方法来完成:传入的参数为我们想要连接的机器的地址 remote_addr_,以及用于在其中建立连接的空 ACE_SOCK_Stream client_stream_。远地机器在例子的运行时参数中指定。一旦 connect () 方法成功返回,通过使用 ACE_SOCK_Stream 封装类中的 send () 和 recv () 方法族,流就可以用于在新建立的链路上发送和接收数据了。
在此例中,一旦连接建立好,send_to_server () 方法就会被调用,以将一个字符串发送 NO_ITERATIONS 次到服务器。如前面所提到的,这是通过使用流包装类的 send () 方法来完成的。
android – 什么’adb forward tcp:8080 tcp:8080’命令呢?
解决方法
第一个端口是主机,第二个端口是设备端口.
从docs
forwarding of requests on a specific host port to a different port on an emulator/device instance
Android 网络编程 TCP 与 UDP
TCP
TCP(Transmission Control Protocol,传输控制协议) 即传输控制协议,是一种传输层通信协议
特点:面向连接、面向字节流、全双工通信、可靠
面向连接:指的是要使用TCP传输数据,必须先建立TCP连接,传输完成后释放连接,就像打电话一样必须先拨号建立一条连接,打完后挂机释放连接。
全双工通信:即一旦建立了TCP连接,通信双方可以在任何时候都能发送数据。
可靠的:指的是通过TCP连接传送的数据,无差错,不丢失,不重复,并且按序到达。
面向字节流:流,指的是流入到进程或从进程流出的字符序列。简单来说,虽然有时候要传输的数据流太大,TCP报文长度有限制,不能一次传输完,要把它分为好几个数据块,但是由于可靠性保证,接收方可以按顺序接收数据块然后重新组成分块之前的数据流,所以TCP看起来就像直接互相传输字节流一样,面向字节流
TCP建立连接:必须经过三次握手
第一次握手:建立连接。客户端发送连接请求报文段
第二次握手:服务器收到客户端的报文段,需要对报文段进行确认,然后返回确认信息
第三次握手:客户端收到服务器的报文段,并向服务器发送报文段。即收到确认信息后再次向服务端返回确认连接信息
为什么TCP建立连接需要三次握手?
防止服务器端因为接收了早已失效的连接请求报文从而一直等待客户端请求,从而浪费资源。
TCP释放连接四次握手
现在假设A主动释放连接:(数据传输结束后,通信的双方都可释放连接),其释放TCP连接的过程如下:
第一次握手: A发送释放信息到B;(发出去之后,A->B发送数据这条路径就断了)
第二次握手: B收到A的释放信息之后,回复确认释放的信息:我同意你的释放连接请求
第三次握手: B发送“请求释放连接“信息给A
第四次握手: A收到B发送的信息后向B发送确认释放信息:我同意你的释放连接请求
为什么TCP释放连接要四次握手?
为了保证双方都能通知对方“需要释放连接”,即在释放连接后都无法接收或发送消息给对方
UDP
(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去!
特点:无连接的、不可靠的、面向报文、没有拥塞控制
无连接的:和TCP要建立连接不同,UDP传输数据不需要建立连接,就像写信,在信封写上收信人名称、地址就可以交给邮局发送了,至于能不能送到,就要看邮局的送信能力和送信过程的困难程度了。
不可靠的:因为UDP发出去的数据包发出去就不管了,不管它会不会到达,所以很可能会出现丢包现象,使传输的数据出错。
面向报文:数据报文,就相当于一个数据包,应用层交给UDP多大的数据包,UDP就照样发送,不会像TCP那样拆分。
没有拥塞控制:拥塞,是指到达通信子网中某一部分的分组数量过多,使得该部分网络来不及处理,以致引起这部分乃至整个网络性能下降的现象,严重时甚至会导致网络通信业务陷入停顿,即出现死锁现象,就像交通堵塞一样。TCP建立连接后如果发送的数据因为信道质量的原因不能到达目的地,它会不断重发,有可能导致越来越塞,所以需要一个复杂的原理来控制拥塞。而UDP就没有这个烦恼,发出去就不管了。
TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
示例分析:分别使用UDP与TCP实现聊天功能
TCP:常用API:
ServerSocket 服务端
new ServerSocket(int port) —— 创建监听端口号为port的ServerSocket
getLocalPort() —— 获取本地端口,即Socket监听的端口
setSoTimeout(int timeout) —— 设置accept()的连接超时时间
accept() —— 等待连接。返回客户端的Socket实例对象。若设置了超时,连接超时将抛异常
isClosed() —— 连接是否关闭
close() —— 关闭连接
Socket 客户端
setSoTimeout(int timeout) —— 设置read()读取流的超时时间
getInetAddress().getHostAddress() —— 获取客户端的主机IP地址
getPort() —— 获取客户端与本服务器连接端口
getLocalPort() —— 获取本地端口,与serverSocket.getLocalPort()获取的端口一致
getInputStream() —— 获取输入流,用于接收客户端发送过来的信息。一般使用read(byte[] data)来读取,此方法也属于阻塞式,但若设置了读取流的超时时间,超时将抛异常SocketTimeoutException
getOutputStream() —— 获取输出流,用户给客户端发送信息
package com.hejun.socket.TCP;
import com.hejun.socket.UDP.MsgPool;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPService {
public void start(){
try {
//创建ServerSocket服务器套接字,并设置端口号
//注意 端口号应该大于等于1024,因为0~1023内都被系统内部占用了
ServerSocket serverSocket = new ServerSocket(10010);
// 设置连接超时时间,不设置,则是一直阻塞等待
// serverSocket.setSoTimeout(8000);
MsgPool.getsInstance().start();
while (true){
//等待客户度连接,超时则抛出异常
Socket accept = serverSocket.accept();
System.out.println("IP:" + accept.getInetAddress().getHostAddress() + "PORT:" + accept.getPort() + "IS ONLINE" );
CLientTask cLientTask = new CLientTask(accept);
MsgPool.getsInstance().addMsgComingListener(cLientTask);
cLientTask.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String arg[]){
new TCPService().start();
}
}
package com.hejun.socket.UDP;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
public class MsgPool {
private static MsgPool sInstance = new MsgPool();
//使用队列存放信息
private LinkedBlockingQueue<String> mQueue = new LinkedBlockingQueue<>();
public static MsgPool getsInstance(){
return sInstance;
}
private MsgPool() {
}
//向队列中中放入数据
public void sendMsg(String msg){
try {
mQueue.put(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//从队列中拿取数据
public void start(){
new Thread(){
@Override
public void run() {
try {
String msg = mQueue.take();
notifyComingMsg(msg);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
private void notifyComingMsg(String msg) {
for (MsgComingListener listener: comingListeners){
listener.onMsgComing(msg);
}
}
public interface MsgComingListener{
void onMsgComing(String msg);
}
private List<MsgComingListener> comingListeners = new ArrayList<>();
public void addMsgComingListener(MsgComingListener comingListener){
comingListeners.add(comingListener);
}
}
package com.hejun.socket.TCP;
import com.hejun.socket.UDP.MsgPool;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
public class CLientTask extends Thread implements MsgPool.MsgComingListener{
private Socket mSocket;
private InputStream mIs;
private OutputStream mOs;
public CLientTask(Socket socket){
this.mSocket = socket;
try {
mIs = socket.getInputStream();
mOs = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
//接收信息,读取数据
@Override
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(mIs));
String line = null;
try{
while ((line = br.readLine()) != null){
System.out.println(mSocket.getPort() + ":" + line);
MsgPool.getsInstance().sendMsg(line);
}
}catch (IOException e){
e.printStackTrace();
}
}
//发送信息,写入数据
@Override
public void onMsgComing(String msg) {
try {
mOs.write(msg.getBytes());
// mOs.write("\n" .getBytes());
mOs.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
UDP:
DatagramSocket:
new DatagramSocket(int port) —— 创建监听端口为port的套接字
setSoTimeout(int timeout) —— 设置接收信息的超时时间。不设置,则一直阻塞
receive(DatagramPacket packet) —— 用数据报packet接收数据,阻塞式。未设置超时时间,一直阻塞,设置了没接收到数据会抛SocketTimeoutException
close() —— 关闭
DatagramPacket:
new DatagramPacket(byte[] data, int length) —— 创建一个data为数据缓冲区,数据最大长度(≤data.length)为length的数据报。有效数据缓冲区应该足够大来装下对方发送过来的全部数据,否则超过缓冲区的数据将丢失。
getLength() —— 获取接收到数据的有效长度
getData() —— 获取数据报中的数据,就是上面的data
getAddress().getHostAddress() —— 获取数据报中的主机IP地址。发送和接收获取的,都是对方IP
getPort() —— 获取数据报中的端口。发送和接收获取的,都是对方IP
package com.hejun.socket.UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;
public class UDPService {
private InetAddress mInetAddress;
private int port = 8888;
private DatagramSocket mSocket;
private Scanner scanner;
public UDPService() {
try {
mInetAddress = InetAddress.getLocalHost();
//创建套接字
mSocket = new DatagramSocket(port,mInetAddress);
} catch (Exception e) {
e.printStackTrace();
}
}
public void start(){
while(true){
try {
//创建数据报
byte[] bytes = new byte[1024];
DatagramPacket received = new DatagramPacket(bytes,bytes.length);
mSocket.receive(received);
//接收信息
InetAddress address = received.getAddress();
int recevidPort = received.getPort();
String clietContent = new String(received.getData(),0,received.getLength());
System.out.print("address="+address + " port = " + recevidPort + "data="+ clietContent );
//发送信息
scanner = new Scanner(System.in);
// scanner.useDelimiter("\n");
String sc = scanner.next();
byte[] bytes1 = sc.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes1,bytes1.length,address,recevidPort);
mSocket.send(datagramPacket);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
new UDPService().start();
}
}
package com.hejun.socket.UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Scanner;
public class UDPClient {
private String secviceIP = "192.168.141.2";
private InetAddress mInetAddress;
private int port = 8888;
private DatagramSocket mDatagramSocket;
private Scanner scanner;
public UDPClient() {
try {
mInetAddress = InetAddress.getByName(secviceIP);
//创建套接字
this.mDatagramSocket = new DatagramSocket();
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public void start(){
while (true){
try {
//发送信息
scanner = new Scanner(System.in);
// scanner.useDelimiter("\n");
String sc = scanner.next();
byte[] bytes = sc.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,mInetAddress,port);
mDatagramSocket.send(datagramPacket);
//接收信息
byte[] bytes1 = new byte[1024];
DatagramPacket received = new DatagramPacket(bytes1,bytes1.length);
mDatagramSocket.receive(received);
String clietContent = new String(received.getData(),0,received.getLength());
System.out.print(clietContent);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args){
new UDPClient().start();
}
public void start(String text){
try {
byte[] bytes = text.getBytes();
DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,mInetAddress,port);
mDatagramSocket.send(datagramPacket);
} catch (IOException e) {
e.printStackTrace();
}
}
public String get(){
byte[] bytes1 = new byte[1024];
try {
DatagramPacket received = new DatagramPacket(bytes1,bytes1.length);
mDatagramSocket.receive(received);
String clietContent = new String(received.getData(),0,received.getLength());
return clietContent;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
DatagramPacket中的数据data最大是65507,超过则会在发送的时候报错: Exception:sendto failed: EMSGSIZE (Message too long)
今天的关于java 网络编程-TCP-多人群聊究极版和java 多人聊天的分享已经结束,谢谢您的关注,如果想了解更多关于(OK) mim-tcp-mdr - (使用 原始 quagga 和 tcp) - android-x86-7.1-rc2 - kernel-4.9.54 (开启 ROUTE_MULTIPATH)、ACE 网络编程:IPC SAP、ACE_SOCKET 和 TCP/IP 通信实例、android – 什么’adb forward tcp:8080 tcp:8080’命令呢?、Android 网络编程 TCP 与 UDP的相关知识,请在本站进行查询。
本文标签: