最近研究了一下Java中的socket通信机制,由于英语水平不好,只能习惯性的百度、谷歌上找相关类文章,总是有一些收获的,也遇到了不少小困难(其实是对这些机制不了解),现在写下来。
顺便说一下,网上的那些源代码都是复制来复制去,照样写上去总是不能正常运行,是他们的错的还是我写的是错的?
下面是一些最基本的东西,网上总结的。
端口 :端口是将应用程序与IP网络相关联,是应用进程的地址标识。分为两种公用端口(0~1023)和临时端口(1024~65535),我们用的就是临时端口。
套接字:区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。Socket原意是“插座”。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
流:Java中的流分为两种,一种是字节流(8位),另一种是字符流(16),分别由四个抽象类来表示:InputStream,OutputStream,Reader,Writer。
InputStream/OutputStream:
是所有的输入和输入类的基类。
面向字节形式的I/O操作(8位字节流)。
Reader/Writer:
面向字符的I/O操作(16位的Unicode字符,在JAVA语言中,byte类型是8位的,char类型是16位的,所以在处理中文的时候需要用Reader和Writer)
InputStreamReader:可以将InputStream转换为Reader
OutputStreamWriter:可以将OutputStream转换为Writer
关于TCP的三次握手可以看这个:http://www.cnblogs.com/rootq/articles/1377355.html
基于TCP的聊天室
下面是效果图:
客户端将消息发往服务端用的是字节流,服务器端发往客户端用的是字符流。
Client客户端代码:
开启一个ServerSocket监听一个端口
/**
* 服务器端 *
*/
public class Server {
private ServerSocket sock ;
private String str ;
private List<Socket> sockLi = new ArrayList<Socket>();
private String conte = null;//当前接收到的信息
public Server()
{
try {
//* 创建ServerSocket负责接收客户连接请求
sock = new ServerSocket(5171,3);
} catch (IOException e) {
e.printStackTrace();
}
}
public void beginServer()
{
while(true)
{
try {
Socket soc = sock.accept() ;
sockLi.add(soc);
//开启线程
new Thread(new Serv(soc)).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private class Serv implements Runnable{
private Socket soc ;
public Serv(Socket so)
{
this.soc = so;
}
@Override
public void run() {
try {
InputStream instr = soc.getInputStream();
while(!soc.isClosed()){
//从流中读取下一个字节
int len = instr.read();
//从字节流中读取信息
byte[] bt = new byte[len];
instr.read(bt, 0, len);
str = new String(bt);
if(str.equals("end"))
{
Thread.sleep(200);
soc.close();
sockLi.remove(soc);
break ;
}
else if(str.trim().equals(""))
{
continue ;
}
else
{
conte = str;
sendMessage();
}
}
} catch (IOException e) {
//连接断开
sockLi.remove(soc);
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void sendMessage()
{
PrintWriter pr;
for(Socket so:sockLi)
{
try {
pr = new PrintWriter(so.getOutputStream(),true);
pr.print(conte+"\n");
pr.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
Server se = new Server();
se.beginServer();
}
}
提一下 readline和read都会阻塞进程,readline遇到\n会返回,所以在客户端发送消息后要发送一个\n表示已经发送完。
public class SocketSend {
public static void main(String[] args)
{
}
private Handler hd ;
private BufferedReader buf ;
static Socket sock ;
private Bundle bun;
private String name ;
public SocketSend(Handler hd,String name)
{
this.name = name ;
this.hd = hd ;
try {
sock = new Socket();
bun = new Bundle();
//android中的本地地址为10.0.2.2
InetSocketAddress isa =
new InetSocketAddress("10.0.2.2", 5171);
sock.connect(isa, 300);//如果300ms没有连接到就会失败
buf = new BufferedReader(
new InputStreamReader(sock.getInputStream()));
new Thread(new messtake(buf)).start();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private class messtake implements Runnable{
private BufferedReader bu= null ;
public messtake(BufferedReader buf)
{
bu = buf ;
}
@Override
public void run() {
String msg ="";
try {
while ((msg = bu.readLine()) != null) {
if(msg.trim().equals(""))
{//这里会接收到一个空白字符串,还不知道为什么 !
continue;
}
//先把字符串中原本的/n替换掉,客户端接收再替换过来
//一下是android部分通知ui更新
bun.putString("contentMess",
msg.replace("Sun948040237", "\n"));
bun.putInt("type", 1);
Message message = new Message();
message.setData(bun);
hd.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//向服务器发送消息
public void send(String content)
{
try {
content = name+"&#&" +content ;
OutputStream ou = sock.getOutputStream();
ou.write(content.getBytes().length);
ou.write(content.getBytes());
ou.flush();
} catch (IOException e) {
bun.putString("contentMess", "发送失败:"+content);
bun.putInt("type", 0);
Message message = new Message();
message.setData(bun);
hd.sendMessage(message);
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
客户端调用的时候
//NAME是昵称
String con = "苹果";
new SocketSend(myHandler,NAME).send(con.replace("\n", "Sun948040237")+"\n");
下面是下载示例的链接