概念:
开始进行通讯,需要客户端和服务端
1.TCP客户端:
客户端向服务器发起连接请求后,就被动地等待服务器的响应。典型的TCP客户端要经过下面三步:
1.创建一个Socket实例:构造器向指定的远程主机和端口建立一个TCP连接。
2. 通过套接字的输入输出流(I/O streams)进行通信:一个Socket连接实例包括一个InputStream和一个OutputStream,它们的用法同于其他Java输入输出流。
3. 使用Socket类的close()方法关闭连接。
package com.tcp.ip.chapter2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
/**
* TCP客户端
* @author Administrator
*
*/
public class TCPEchoClient {
public static void main(String[] args) throws IOException{
if((args.length < 2) || (args.length>3)) { // Test for correct # of args
throw new IllegalArgumentException("Parameter(s): <Server> <Word> [<Port>]");
}
//服务名称和ip地址
String server = args[0];
//将内容转换为byte数组
byte[] data = args[1].getBytes();
//获取服务端口
int servPort = (args.length == 3) ?
Integer.parseInt(args[2]) : 7;
//创建连接特定的服务器
Socket socket = new Socket(server, servPort);
System.out.println("Connected to server ... sending echo string");
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write(data);
//接受服务器传递的数据,记录当前装入字符串的位置
int totalBytesRcvd = 0;
int bytesRcvd;
//表示向服务器发送数据
while (totalBytesRcvd < data.length) {
if((bytesRcvd = in.read(data, totalBytesRcvd, data.length-totalBytesRcvd)) == -1){
throw new SocketException("Connection closed prematurely");
}
totalBytesRcvd += bytesRcvd;
}
System.out.println("Received:" + new String (data));
socket.close();
}
}
了解常用的API
Socket 创建
Socket(InetAddress remoteAddr, int remotePort)
Socket(String remoteHost, int remotePort)
Socket (InetAddress remoteAddr, int remotePort, InetAddress localAddr, int localPort)
Socket(String remoteHost, int remotePort, InetAddress localAddr, int localPort);
Socket();
前四个构造函数在创建了一个TCP套接字后,先连接到(connect)指定的远程地址和端口号,再将其返回给程序。前两个构造函数没有指定本地地址和端口号,因此将采用默认地址和可用的端口号。在有多个接口的主机上指定本地地址是有用的。指定的目的地址字符串参数可以使用与InetAddress构造函数的参数相同的型式。最后一个构造函数创建一个没有连接的套接字,在使用它进行通信之前,必须进行显式连接(通过connect()方法,见下文)。
Socket: 操作
void connect(SocketAddress destination)
void connect(SocketAddress destination, int timeout) 连接超时
InputStream getInputStream();
OutputStream getOutputStream();
void close();
void shutdownInput(); //未读取的数据都会放弃掉
void shutdownOutput();
Socket:获取/检测属性
InetAddress getInetAddress();
int getPort();
InetAddress getLocalAddress();
int getLocalPort();
SocketAddress getRemoteSocketAddress();
SocketAddress getLocalSocketAddress();
InetSocketAddress:创建访问
InetSocketAddress(InetAddress addr, int port)
InetSocketAddress(int port)
InetSocketAddress(String hostname, int port)
static InetSocketAddress createUnresolved(String host,int port)
boolean isUnresolved();
InetAddress getAddress()
int getPort();
String getHostName();
String toString();
InetSocketAddress类为主机地址和端口号提供了一个不可变的组合。只接收端口号作为参数的构造函数将使用特殊的"任何"地址来创建实例,这点对于服务器端非常有用。接收字符串主机名的构造函数会尝试将其解析成相应的IP地址,而createUnresolved()静态方法允许在不对主机名进行解析情况下创建实例,。如果在创建InetSocketAddress实例时没有对主机名进行解析,或解析失败,isUnresolved()方法将返回true。get...()系列方法提供了对指定属性的访问,getHostName()方法将返回InetSocketAddress内部InetAddress所关联的主机名。toString()方法重写了Object类的toString()方法,返回一个包含了主机名、数字型地址(如果已知)和端口号的字符串。其中,主机名与地址之间由'/'(斜线)隔开,地址和端口号之间由':'(冒号)隔开。如果InetSocketAddress的主机名没有解析,则冒号前只有创建实例时的主机名字符串。
2.TCP服务器端
1. 创建一个ServerSocket实例并指定本地端口。此套接字的功能是侦听该指定端口收到的连接。
2. 重复执行:
a. 调用ServerSocket的accept()方法以获取下一个客户端连接。基于新建立的客户端连接,创建一个Socket实例,并由accept()方法返回。
b. 使用所返回的Socket实例的InputStream和OutputStream与客户端进行通信。
c. 通信完成后,使用Socket类的close()方法关闭该客户端套接字连接。
package com.tcp.ip.chapter2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
/**
* 服务器端
* @author Administrator
*
*/
public class TCPEchoServer {
//缓存大小为32
private static final int BUFSIZE = 32;
public static void main(String[] args) throws IOException {
if(args.length != 1) {
throw new IllegalArgumentException("Parameter(s) :<Port>");
}
int servPort = Integer.parseInt(args[0]);
//创建服务器的套接字
ServerSocket servSock = new ServerSocket(servPort);
int recvMsgSize;
byte[] receveBuf = new byte[BUFSIZE];
while (true) {
//监听客户端socket
Socket clntSock = servSock.accept();
SocketAddress clientAddress = clntSock.getRemoteSocketAddress();
//打印获取的地址
System.out.println("正在处理的客户端的地址: " + clientAddress);
InputStream in = clntSock.getInputStream();
OutputStream out = clntSock.getOutputStream();
StringBuffer sb = new StringBuffer();
//读取传过来的数据
while ((recvMsgSize = in.read(receveBuf)) != -1) {
String str = new String(receveBuf);
System.out.println("11111111111111111111");
out.write(receveBuf,0,recvMsgSize);
sb.append(str);
}
System.out.println(sb.toString());
clntSock.close();
}
}
}
有疑问:为啥out.write(receveBuf,0,recvMsgSize) 没有问题,但是如果是out.write("dddddddd".getBytes())就会阻塞?后续将继续思考这个问题,如果客户端传过来的数据比较短,服务具体out.write("dddddddd".getBytes())是没有问题的。