2.1-Socket
Socket
An abstraction of a network interface
The Socket Application Programming Interface (API) allows you to:
– Send data (bytes)
– Receive data (bytes)
Java network programming
import java.net.*;
先导包
Similar to reading and writing files:
– But the “file” exists on a remote machine;
– Receive/send data.
What is I/O?
– Input: to bring in information (read)
– Output: to send out information (write)
- Information for Input/Output can be:
– anywhere: memory, disk, in a file, over the network, in another program …
– of any type (any object): Text, Image, Audio, Video
Streams
- Java input/output makes use of streams:
A stream is a connection to a source of data or to a destination for data (sometimes both)
Streams can represent any data, so a stream is a sequence of bytes that flow from a source to a destination.
In a program, we read information from an input stream and write information to an output stream.
在使用结束后记得关闭
Always close your streams
∵A stream is an expensive resource, and there is a limit on the number of streams that you can have open at one time.
Some streams can be used only for input, others only for output, others for both
Java has two broad categories of streams(socket 用字节流):
在java.io包中操作文件内容的主要有两大类:字节流、字符流,两类都分为输入和输出操作。在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成
- byte streams, for machine-formatted data 字节流
• InputStream
• OutputStream
• Writing and reading are very efficient.
网络编程中socket使用字节流
- character streams (textual), for human-readable data 字符流
• Reader
• Writer
• Require translation.
The java.io package contains a relatively large number of classes to deal with streams
Writing to a Socket is (in terms of code) similar to writing to a file
socket中使用的IO方法
BufferedReader/BufferedWriter InputStreamReader/OutputStreamWriter
Input:
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Output:
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream)), true);
SEND OVER A NETWORK
Uniquely identifying machines on a network:
– Domain names or
– Internet: IP (Internet Protocol) address
IP addressing
Every computer in the world has a different IP address
The familiar DNS (Domain Name System)
use the IP address directly (in the “dotted quad” format)
• 127.0.0.1 is for local machine
The IP address is represented internally as a 32-bit number.(IPv4)
- usually presented as **dotted quad **点分十进制, e.g. 10.0.0.1
127.0.0.1 is for local machine.
A special Java object exists to represent this number from either of the forms:
static InetAddress.getByName()
在 java.net package.
在给定主机名的情况下确定主机的IP地址
如果参数为null,获得的是本机的IP地址
如:InetAddress.getByName(“www.163.com")
import java.net.*;
public class IPFinder {
public static void main(String[] args) throws Exception
{
String domainName = “www.qmul.ac.uk”;
InetAddress a = InetAddress.getByName(domainName);
System.out.println(a);
}
}
domain name and DNS
Domain Name is simply a name that maps to an IP address
The Domain Name System (DNS) performs this mapping
由于IP地址具有不方便记忆并且不能显示地址组织的名称和性质等缺点,人们设计出了域名,并通过网域名称系统(DNS,Domain Name System)来将域名和IP地址相互映射,使人更方便地访问互联网,而不用去记住能够被机器直接读取的IP地址数串。
using nslookup 查看domain name mapping的IP
You can send DNS queries to a DNS server
It will return the IP address that maps to a domain name
Need to map DNS name (www.example.com) to IP address (192.0.32.10) before you can send packets
客户端⾸先会发出⼀个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。
域名服务器收到客户端的请求后,返回 IP 地址
Port
– A unique identifier for a particular service running on a machine.
– E.g. A web server “listens” on Port 80
- When setting up a client or a server:
– Must choose a port(必须选择server的port,也就是选择对应的服务).
– Both client and server agree to connect.
The port is not a physical location in a machine, but a software abstraction
System services reserve the use of ports 0 through 1023.
不要使用小于1023的端口号,都有对应的服务,如
80 | web servers (HTTP) |
---|---|
443 | encrypted web servers (HTTPS) |
22 | secure shell (SSH) |
20 and 21 | File Transfer Protocol (FTP) |
25 | Simple Mail Transfer Protocol (SMTP) |
Usually choice for web proxy is port 8080:
Usually represented as ==IP address : port==.
• 127.0.0.1:8080
• localhost:8080
CLIENT-SERVER MODEL
Server and Clients
Network: allows two machines to connect and talk to each other.
server: stay and listen
client:makes requests
The client is trying to connect to the server. Once connected, there is a two way (双向) communication.
Server just listens. If no requests received: server does nothing
在本地运行:本地主机充当服务器和客户端
Run both client and server on one machine (localhost)
• Producing a localhost:
InetAddress addr = InetAddress.getByName(null);
or
InetAddress.getByName("localhost");
or
InetAddress.getByName("127.0.0.1");
Sockets
Sockets
An abstraction of a network interface
The Socket object presents a stream to you (the programmer)
socket允许程序员将网络连接看成可以读写字节的流。socket对程序员掩盖了网络底层细节(计网那些)
Java’s built-in multithreading(内置多线程):
– Handles multiple connections at once
When a client wants a service, it attempts the connection with the server by supplying the port number associated with the service
对于客户端来说,需要提供服务端的端口号来建立连接
There are likely to be multiple clients waiting for the same service at the same time (e.g. web browsers wanting a web page)
每有一个client建立连接,creates a socket at its end of the communication link
这些socket需要相同的服务,所以连接到相同的server socket(port)上
server 通过client端的sockets来区分他们 并且 keeping their communication separate.
The server, upon receiving the client’s initial request (on a particular port number), creates a new socket at its end, dedicated to the communication with that specific client. 服务器端在接收到客户端的socket端口后,新建一个 socket对象 (这个对象只是对客户端socket的引用,所以可以和客户端socket共享一个stream)与该用户进行通信
Socket实质上提供了进程通信的端点。进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。正如打电话之前,双方必须各自拥有一台电话机一样。
想象成一根电缆,当两头都插入插座后就可以建立IO流,而不用管底层的控制
Java中的socket
如何在Java 里实现socket:
服务器端
使用ServerSocket:相当于开启一个服务,并等待客户端的连接( listen for incoming connections)。
– Only need to give the port number.构造方法只需要端口
public ServerSocket(int port)
使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。
ServerSocket server = new ServerSocket(6666);
– Only need to give the port number.
这个port num是指定服务器要绑定的端口(服务器要监听的,与服务挂钩的端口)(用于给client看)
accept()
为了区分不同client,serversocket有一个成员方法accept()
ServerSocket returns a corresponding Socket – via the accept() method – through which communications will take place on the server side. 返回client的socket对象
Socket socket = s.accept();
客户端
public Socket(String host, int port)
– Give both the IP address and port number.
String host
:服务器主机的名称/服务器的IP地址
int port
: 服务器的端口号
Socket client = new Socket("127.0.0.1", 6666);
成员方法:
To produce the corresponding InputStream and OutputStream objects from each Socket, use the methods:
OutputStream getOutputStream()
:返回此套接字的输出流InputStream getInputStream()
:返回此套接字的输入流void close()
:关闭此套接字
下图为建立socket连接的整个流程
区分socket和serversocket
server socket是用来监听,等待建立连接:ServerSocket returns a corresponding Socket – via the accept() method – through which communications will take place on the server side.
socket: a client is used to initiate a connection.
Example
A simple server and client
Server description:
Create a ServerSocket object.
Put the server in a waiting state.
Set up input and output streams.
Send and receive data.
Close the connection.
服务器的实现步骤:(in/out是针对于客户端、服务器端来说)
创建服务器ServerSocket对象和系统要指定的端口号
使用ServerSocket对象中的方法accept,获取到网络字节输入流InputStream对象创建网络字节输入流InputStream对象(参数为accept的socket的成员方法socket.getInputStream)
再用BufferedReader套住这个InputStream,用其中的成员方法readLine读client发来的字符
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
创建网络字节输出流OutputStream对象(参数为accept的socket的socket.getOutputStream方法)
再用BufferedWirter套住这个OutputStream
创建 PrintWriter对象(参数为OutputStream)用其中的println方法向流中写入信息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( socket.getOutputStream())), true);//true自动刷行
释放资源(先Socket 后ServerSocket)
import java.io.*;
import java.net.*;
public class MyServer {
public static final int PORT = 8080;//定义监听端口
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(PORT);
System.out.println(“Started: ” + s);
try {
Socket socket = s.accept();
try {
System.out.println(“Connection accepted: ” + socket);
BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(new
BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);//true自动刷行
while (true) {
String str = in.readLine(); //从流中读
if (str.equals(“END”)) break;
System.out.println(“Echoing: ” + str);
out.println(str); //向流中写刚刚读出来的
}
finally {
System.out.println(“closing...”);
socket.close();
}
}
finally { s.close(); }
}
}
注意抛出异常,或catch异常
记得finally里关闭socket
public class ServerMain
{
public static void main(String[] args)
{
System.out.println("[Server Main] Starting server listening...");
ServerSocket serverSocket = null;
try
{
// Start a ServerSocket that lists on port
serverSocket = new ServerSocket(5051);
while(true) //Loop forever listening for connections
{
// Wait for a client to connect
Socket socket = serverSocket.accept();
System.out.println("[Server Main] Received new connection");
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
PrintWriter printWriter = new PrintWriter(writer, true);
// Sends a message via the IO stream to the socket
printWriter.println("Hello!");
socket.close();
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally //A finally block *always* gets executed. It will close the listening socket.
{
try
{
serverSocket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
假多线程:server加while一直listen,并且每个结束记得close
Client description:
Establish the connection with the server.
Set up input and output streams.
Send and receive data.
Close the connection
实现步骤:
创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号创建网络字节输入流InputStream对象(参数为accept的socket的成员方法socket.getInputStream)
再用BufferedReader套住这个InputStream,用其中的成员方法readLine读sever回写到流的数据
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
创建网络字节输出流OutputStream对象(参数为accept的socket的socket.getOutputStream方法)
再用BufferedWirter套住这个OutputStream
创建 PrintWriter对象(参数为OutputStream)用其中的println向流中写入信息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter( socket.getOutputStream())), true);//true自动刷行
释放资源(Socket)
import java.net.*;
import java.io.*;
public class MyClient {
public static void main(String[] args) throws IOException {
InetAddress addr = InetAddress.getByName(null);
System.out.println(“addr = ” + addr);
Socket socket = new Socket(addr, MyServer.PORT);
try {
System.out.println("socket = " + socket);
BufferedReader in = new BufferedReader(new
InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(new
BufferedWriter(new OutputStreamWriter(
socket.getOutputStream())), true);
for (int i=0; i < 10; i++) {
out.println(“howdy ” + i);
String str = in.readLine();
System.out.println(str);
}
out.println(“END”);
}
finally {
System.out.println(“closing...”);
socket.close();
}
}
}
Serving multiple clients
Procedure:
– Make a single ServerSocket in the server.
– Call accept() to wait for a new connection.
– When accept() returns, take the resulting Socket and use it to create a new thread whose job is to serve that particular client.
– Then call accept() again to wait for a new client.
MultiServer
import java.io.*;
import java.net.*;
class ServeOne extends Thread {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public ServeOne(Socket s) throws IOException {
socket = s;
in = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())), true);
start();
}
public void run() {
try {
while (true) {
String str = in.readLine();
if (str.equals(“END”)) break;
System.out.println(“Echoing: ” + str);
out.println(str);
}
System.out.println(“closing...”);
}
catch (IOException e) { System.err.println(“IO Exception”); }
finally {
try { socket.close(); }
catch (IOException e) {
System.err.println(“Socket not closed”);
}
}
}
}
public class MultiServer {
static final int PORT = 8080;
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(PORT);
System.out.println(“Server Started”);
try {
while (true) {
Socket socket = s.accept();
try { new ServeOne(socket); }
catch (IOException e) { socket.close(); }
}
}
finally { s.close(); }
}
}
MultiClient
import java.net.*;
import java.io.*;
class ClientThread extends Thread {
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private static int counter = 0;
private int id = counter++;
private static int threadcount = 0;
public static int threadCount() { return threadcount; }
public ClientThread(InetAddress addr) {
System.out.println(“Making client ” + id);
threadcount++;
try { socket = new Socket(addr, MultiServer.PORT); }
catch (IOException e) { System.err.println(“Socket failed”); }
try {
in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(
socket.getOutputStream())), true);
start();
}
catch (IOException e) {
try { socket.close(); }
catch (IOException e2) {
System.err.println(“Socket not closed”);
}
}
}
public void run() {
try {
for (int i = 0; i < 5; i++) {
out.println(“Client ” + id + “: ” + i);
String str = in.readLine();
System.out.println(str);
}
out.println(“END”);
}
catch (IOException e) { System.err.println(“IO Exception”); }
finally {
try { socket.close(); }
catch (IOException e) {
System.err.println(“Socket not closed”);
}
threadcount--;
}
}
}
public class MultiClient {
static final int MAX_THREADS = 40;
public static void main(String[] args) throws
IOException,InterruptedException {
InetAddress addr = InetAddress.getByName(null);
while (true) {
if (ClientThread.threadCount() < MAX_THREADS)
new ClientThread(addr);
Thread.currentThread().sleep(100);
}
}
}