一:什么是Socket ?来自百度百科的解释:
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务
但是在这之前你一定了解TCP/IP协议,以及四个抽象层(图片来自网络):
接下来说说Socket ------》套接字,图片来自于网络:
可以看出来Socket 就是是在应用层和传输层直接进行通信的操作,在我自己理解看来,Socket 将原本复杂的操作简化成为几个接口提供个应用层进行调用完成操作。
Socket = IP 地址+端口号
Socket 是网络运行的程序之间双向通信链路的终结点 。
二:TCP
对于TCP的工作流程可分为三步:一、连接的建立; 二、传输数据; 三、断开连接
TCP 是通过三次握手
对于三次握手还是需要好好理解滴~
TCP采用的是客户机/服务器方式建立的连接。所以说对于一方就是被动的被打开,处于“听”的状态。看书才知道,两个主机各自执行主动打开和被动打开的命令来完成初始状态的准备。
以下客户机用A 服务器用B。其实此过程在我看来就和加QQ好友一样。
第一次握手:第一次握手也就是请求!你加一个QQ好友,你要发送一个加友请求给的对方,这个请求就是SYN。A给B发送一个封装在一个IP数据报文中的SYN 报文。此时SYN=1 seq=X。 (seq,序号字段对数据字节计数。 SYN,用于连接建立和拆除,用于指示本TCP报文段在连接中的作用。)
第二次握手:请求发送了,对方同意了,以时不时会收到一个消息:对方同意啥啥啥你们现在可以咋咋咋。别说你没看到过。这就是服务器允许了连接,返回的消息是什么呢?其实想想你加好友发过去的是你自己的信息,对方的信息你不是很了解就一个QQ号或者其他,而你的请求得到同意之后,你就会获取到对方的信息。所以说在第二次握手中,返回的信息,首部除了SYN=1,还有ACK=1,而seq中多了服务器的起始号,此时seq=y(服务器端)。此时确认号字段ack=x+1。咦~貌似顺序变了呢!其实也很好理解!
可以把seq比划为之间联系的信息过程。第一次握手中消息记录只有客户端也就是请求方发送的数据所以seq=X,第二次握手,对方同意了,对方给你发送了一个同意的消息此时seq=y。ack可以理解为标志谁在回复!允许连接也就是同意加好友那么给请求放的回复+1,请求方为x,所以ack=x+1。这个消息专业的讲叫做SYNACK报文段。
第三次握手:好了建立连接了 好友加上了,你不确认一下?于是乎你会发个确认消息。艾玛,那岂不是消息记录中X多了一条,回复消息Y多了一条和回复的标志又要变!没错你现在又变成客户机给服务器了所以seq=x+1,ack=y+1!
这样三次握手就完了
三:代码实现TCP 通信
服务器端:
package SocektTP;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
/**
*
* 服务器端
*
* @throws IOException
*
*/
public static void main(String[] args) throws IOException {
// 创建服务器端Socket 并且指定绑定的端口并且进行监听
int count = 0;
ServerSocket serverSocket = new ServerSocket(8888);
// 开始监听,等待客户端的连接
System.out.println("等待客户端连接*******");
// 多个客户端进行监听:
Socket socket = null;
while (true) {
// 开始监听
socket = serverSocket.accept();
// 建立线程
SocketThread st = new SocketThread(socket);
// 启动线程
st.start();
count++;
System.out.println("目前客户端的数量是:" + count);
}
// 死循环无法到达
// serverSocket.close();
}
}
服务器线程:
package SocektTP;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.io.IOException;
;
/**
* 服务器线程处理类
*
* @author ge.zhang
*
*/
public class SocketThread extends Thread {
// 和线程有关的socket
Socket socket = new Socket();
public SocketThread(Socket socket) {
this.socket = socket;
}
// 重写run 方法,实现服务器端的socket 相关通信的操作
public void run() {
// 读取客户端信息
InputStream is = null;
InputStreamReader isr = null;
BufferedReader bf = null;
// 向客户端发送消息
OutputStream os = null;
PrintWriter pw = null;
try {
is = socket.getInputStream();
isr = new InputStreamReader(is);
bf = new BufferedReader(isr);
String data = null;
System.out.println("服务器:");
while ((data = bf.readLine()) != null) {
System.out.println("客户端:" + data);
}
socket.shutdownInput();// 关闭输入流
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write("欢迎你");
pw.flush();// 刷新缓存
} catch (IOException e) {
e.printStackTrace();
}
// 保证一定会运行放在finally中
finally {
try {
bf.close();
isr.close();
is.close();
os.close();
pw.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
客户端:
package SocektTP;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class client {
/**
* 客户端
*
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException, IOException {
// 建立客户端socket 指定服务器地址和端口
Socket socket = new Socket("localhost", 8888);
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("用户名:zg 密码:123");
pw.flush();
socket.shutdownOutput();// 关闭输出流
//接受服务器端发送响应的消息
InputStream is=socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);// 字符流
BufferedReader bf = new BufferedReader(isr);// 添加缓冲
String data = null;
System.out.println("客户端:");
while ((data = bf.readLine()) != null) {
System.out.println("服务器:" + data);
}
// 关闭资源
pw.close();
os.close();
is.close();
isr.close();
bf.close();
socket.close();
}
}