天天看点

java TCP/IP Socket编程-----基本套接字-----TCP套接字通讯-----笔记5

概念:

开始进行通讯,需要客户端和服务端

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())是没有问题的。