什么是套接字(Socket)
套接字位于应用层与传输层之间,是应用程序与网络之间进行数据传输的门户,每一个套接字都与一个进程相关联。
那要如何区分不同的套接字呢,这里就要提到端口号的概念。
端口号
端口号是一个16比特的数,其大小在0-65535之间。0-1023范围的端口号称为周知端口号,是受限制的,这是指它们保留给诸如HTTP(80)和FTP(21)之类的周知应用层协议来使用。
UDP套接字
一个UDP套接字由一个二元组来标识,该二元组包含一个目的地址和一个目的端口号。
如果两个UDP报文段有不同的源IP地址或不同的源端口号,但具有相同的目的IP地址和目的端口号,那么这两个报文段将被定向到相同的目的套接字。
TCP套接字
一个TCP套接字是由一个四元组来标识,包括源IP地址、源端口号、目的IP地址、目的端口号。
当一个TCP报文段从网络到达一台主机时,该主机使用全部四个值来将报文段定向到相应的TCP套接字。
与UDP套接字不同,具有不同源IP地址和端口号的TCP报文段将被定向到不同的套接字。
Java实现简单的服务器与客户端
MultiThreadEchoServer.java
package 并发;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MultiThreadEchoServer {
class HandleMsg implements Runnable{
Socket clientSocket;
public HandleMsg(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void run() {
BufferedReader is = null;
PrintWriter os = null;
try {
is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
os = new PrintWriter(clientSocket.getOutputStream(), true);
//从inputStream中读取客户端发送的数据
String inputLine = null;
long b = System.currentTimeMillis();
while ((inputLine = is.readLine()) != null) {
os.println(inputLine);
}
long e = System.currentTimeMillis();
//输出处理一次客户端请求所花费的时间(包括读数据和回写数据的时间)
System.out.println("spend:"+(e-b)+"ms");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭输入输出流以及套接字
try {
//确保is不为null,is不等于null说明输入流还没关,如果在等于null的情况下去close就会抛异常
if(is != null)
is.close();
if(os != null)
os.close();
clientSocket.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
//欢迎套接字,所有要与服务器通信的客户的起始接触点,三次握手在这个套接字里完成
ServerSocket echoServer = null;
//连接套接字,随后为了与每个客户端通信而生成的套接字
Socket clientSocket = null;
try {
echoServer = new ServerSocket(8000);
}catch(IOException e) {
System.out.println(e);
}
while(true) {
try {
clientSocket = echoServer.accept();
System.out.println(clientSocket.getRemoteSocketAddress()+" Connect!");
//非静态内部类的实例化需要通过外部类的对象
pool.execute(new MultiThreadEchoServer().new HandleMsg(clientSocket));
}catch(IOException e) {
System.out.println(e);
}
}
}
}
Client1.java
package 并发;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
public class Client1 {
public static void main(String[] args) {
Socket client = null;
PrintWriter writer = null;
BufferedReader reader = null;
try {
client = new Socket();
client.connect(new InetSocketAddress("localhost", 8000));
writer = new PrintWriter(client.getOutputStream(), true);
writer.println("Hello!");
//表示强制将缓冲区中的数据发送出去,不必等到缓冲区满.
writer.flush();
reader = new BufferedReader(new InputStreamReader(client.getInputStream()));
System.out.println("from server:"+reader.readLine());
}catch (IOException e) {
e.printStackTrace();
} finally {
//关闭输入输出流以及套接字
try {
if(writer != null)
writer.close();
if(reader != null)
reader.close();
if(client != null)
client.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
欢迎套接字与连接套接字
服务器为了能够处理客户端的请求,需要先后建立两个套接字
- 欢迎套接字(
ServerSocket
):所有来自客户端连接请求的起始接触点,三次握手在这个套接字里完成 - 连接套接字:随后进行通信用的套接字
参考
《计算机网络自顶向下方法》
碎碎念
周日,天气晴,成都冬日里每个出太阳的日子都是节日。周末的沙河俨然成为成华区附属公园,穿着花花绿绿的大妈争相在银杏树下拍照,一对对情侣,嬉戏的孩童。望着那些快秃头的银杏,是不是也该来些霸王。一个人听着歌穿过人群,我也不知道我的脚步匆匆着什么。