1在网络中,两台机器为了进行通信。首先需要建立一个网络通路,然后它们之间才能够进行通信。
2 Tcp和Udp的区别
udp:将数据及源和目的封装成数据包中,不需要建立连接
每个数据包的大小限制在64K内
因无连接,是不可靠的协议
不需要建立连接,速度快
Tcp
建立连接,形成传输数据的通道
在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低
3使用UDP实现的一个能够聊天的小程序
package com.Socket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 使用UDP的方式进行聊天
* <p/>
* “收消息”和“发消息”的动作要能够同时进行,所以我们需要使用“多线程”的技术
* 起一个单独的线程负责“收消息”,另一个线程负责“发送消息”
* User: OF895
* Date: 2014/12/3
* Time: 23:59
*/
public class ChatByUdp {
public static void main(String[] args) throws Exception {
//这里我们首先为下面的线程提供一个“socket"套接字,当然我们也可以写死在下面的收发消息的类中
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket = new DatagramSocket(10002);
//开启两个线程,执行动作
new Thread(new SendMessage(sendSocket)).start();
new Thread(new ReciveMessage(receiveSocket)).start();
}
}
class SendMessage implements Runnable {
//使用“构造函数”,传入一个upd的socket套接字,用于进行udp编程
private DatagramSocket datagramSocket;
public SendMessage(DatagramSocket datagramSocket) {
this.datagramSocket = datagramSocket;
}
@Override
public void run() {
try {
//1读取键盘录入的信息
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while ((line = br.readLine()) != null) {
if("over".equals(line)){
break;
}
byte[] bytes = line.getBytes();
//2创建UDP数据包
DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("127.0.0.1"), 10002);
//因为udp是不需要建立连接,就可以发送数据的,那么我们直接发送数据
datagramSocket.send(dp);
System.out.println(Thread.currentThread().getName());
}
} catch (Exception e) {
throw new RuntimeException("发送消息发生异常!");
}
}
}
class ReciveMessage implements Runnable {
private DatagramSocket datagramSocket;
public ReciveMessage(DatagramSocket datagramSocket) {
this.datagramSocket = datagramSocket;
}
@Override
public void run() {
try {
//这边使用一个“循环”不停的去接受发送端,发过来的数据
while (true) {
byte[] buf = new byte[1024];
//创建一个数据包,带有缓冲字节数组,用于接受从“发送端”发送过来的数据
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//使用datagramSocket“接受”到向这个端口发来的消息,并且通过DatagramPacket存放到buf这个字节数组中
datagramSocket.receive(dp);
String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(), 0, dp.getLength());
System.out.println(Thread.currentThread().getName() + ",ip = " +ip + ":" + data);
}
} catch (Exception e) {
throw new RuntimeException("接收消息发生异常!");
}
}
}
运行结果:
hello
Thread-0
Thread-1,ip = 127.0.0.1:hello
nihao
Thread-0
Thread-1,ip = 127.0.0.1:nihao
hehe
Thread-0
Thread-1,ip = 127.0.0.1:hehe
4使用Tcp进行实现的一个小例子
使用TCP/IP进行“客户机”的数据(mp3,文本,图像)传输到“服务器”上。未使用线程,一对一。这个用的是字节传输数据。没有使用缓冲。注意与下面程序的区别。
客户端程序:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TextClient
{
/**
* 客户端。这里讲一个“文件”,传输到“服务器”端。
* @param args
*/
public static void main(String[] args)
{
FileInputStream fis = null;
Socket s = null;
OutputStream os = null;
//1启动socket服务
try
{
s = new Socket("192.168.0.131",10005);
fis = new FileInputStream("c:\\1.mp3");
os = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = fis.read(buf)) != -1)
{
os.write(buf, 0, len);
os.flush();
}
s.shutdownOutput();
InputStream is = s.getInputStream();
byte[] bufIn = new byte[1024];
is.read(bufIn);//从输入流中读取一定数量的字节,并将其存储在缓冲区数组 bufIn中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾或者抛出异常前,此方法一直阻塞。
//也就是从socket流中读取“服务器”端发过来的“歌曲上传成功”这么一句话。缓存到字节数组bufIn中。
String str = new String(bufIn,0,bufIn.length);//将字节数组转换为“字符串”。
System.out.println(str);
}
catch (UnknownHostException e)
{
System.out.println("IP地址有误");
e.printStackTrace();
}
catch (IOException e)
{
System.out.println("输出异常");
e.printStackTrace();
}
try
{
//这里关闭流资源的时候,需要注意一下。
fis.close();
os.close();
s.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
服务器端程序:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TextServer
{
/**
* “客户端”和“服务器”端进行“数据”传输(包括,图片,mp3,文本等文件)
* 这个没有使用线程,是一对一的。
* @param args
*/
public static void main(String[] args)
{
Socket s = null;
ServerSocket ss = null;
FileOutputStream fos = null;
try
{
ss = new ServerSocket(10005);//这边的“客户机”连接“服务器”,我们需要单独try catch下。如果没连上下面的代码就不需要执行了。
s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
System.out.println("ip:"+ ip + "...........has connected!");
}
catch (IOException e)
{
System.out.println("客户机没有连接上服务器");
e.printStackTrace();
}
try
{
fos = new FileOutputStream("c:\\server.mp3");
InputStream is = s.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
while((len = is.read(buf)) != -1)
{
fos.write(buf, 0, len);
fos.flush();
}
OutputStream os = s.getOutputStream();
String str = "歌曲上传成功";
byte[] bufOs = str.getBytes();//这里讲str这句话转换成字节数组,存入字节数组中。不要写成byte[] bufos = new byte[1024];
os.write(bufOs);//这一句等同于os.write(bufos,0,bufos.length);效果是相同的,翻看api文档。作用是将 bufos.length 个字节从指定的 byte数组写入此(这里指socket的outputstream)输出流.
}
catch (IOException e)
{
System.out.println("输入输出有误!");
e.printStackTrace();
}
try
{
fos.close();
s.close();
ss.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
5使用Tcp的小例子2:
利用“线程”和“死循环”实现多个“客户端”向“服务器”端发送消息。基于TCP/IP协议传输。这个使用了缓冲技术。
客户端代码:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class TcpClient
{
/**
* “客户端”向“服务器”端发送“字符”。在“服务器”端显示到控制面板上。
*
* @param args
*/
public static void main(String[] args)
{
Socket s = null;
BufferedReader br = null;
BufferedWriter bw = null;
try
{
s = new Socket("192.168.0.131",10007);
br = new BufferedReader(new InputStreamReader(System.in));
bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String len = null;
while((len = br.readLine()) != null)
{
bw.write(len);
bw.newLine();//这个用于换行
bw.flush();
}
s.shutdownOutput();
}
catch (UnknownHostException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
try
{
s.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
服务器端代码:
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServer
{
/**这个是服务器端,接受从“客户端”发送过来的信息。
* 这里讲ss.accept()方法是用了一个“死循环”,这样有一个“客户机”来连接
* “服务器”就连接一次。这样就实现了多个客户机与“服务器”通讯。(单向的,从客户机到服务器)
* 这里有个注意点需要主意一下,我们需要将“客户机”发送过来的信息,在服务器端显示在控制台上。
* 这个动作我们需要封装到一个单独的线程中。
*
* * @param args
*/
public static void main(String[] args)
{
ServerSocket ss = null;
Socket s = null;
BufferedReader br = null;
BufferedWriter bw = null;
try
{
ss = new ServerSocket(10007);//在实际的操作中,我们通常会将ServerSocket ss = new ServerSocket(10007);的异常单独try catch一下。
while(true)
{
s = ss.accept();
new Thread(new ClientAccept(s,br,bw)).start();
}
}
catch (IOException e)
{
System.out.println("客户端没有连上“服务器”端");
e.printStackTrace();
}
try
{
s.close();
ss.close();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//单独封装一个线程,用来处理每个客户机,与服务器连接后,进行一些操作。
class ClientAccept implements Runnable
{
Socket s = null;//需要将Socekt从TcpServer中带过来。
BufferedReader br = null;
BufferedWriter bw = null;
public ClientAccept(Socket s,BufferedReader br,BufferedWriter bw)
{
this.s = s;
this.br = br;
this.bw = bw;
}
@Override
public void run()
{
try{
String ip = s.getInetAddress().getHostAddress();
System.out.println("ip:"+ ip + "..............has connected!");
InputStream is = s.getInputStream();
br = new BufferedReader(new InputStreamReader(is));//这里用到了“转换流”和“缓冲技术”。
bw = new BufferedWriter(new OutputStreamWriter(System.out));//把它输出到“控制台”上
String len = null;
while((len = br.readLine()) != null)
{
bw.write(len);
bw.newLine();
bw.flush();
}
}catch(Exception e)
{
e.printStackTrace();
}
}
}