参考资料:

http://haohaoxuexi.iteye.com/blog/1979837
http://zhidao.baidu.com/link?url=OeOSa0YbOzSbMVPa8sgPXcwtyyHsWB1lPkh1XopETtNK_lVtbd9lL7NH3qlFxjC-4kNUmCkIXgcfRW7KJq9_FK
http://www.cnblogs.com/rond/p/3565113.html
http://www.cnblogs.com/mengdd/archive/2013/03/10/2952616.html

TCP/IP客户套接字

Java中有两种类型的TCP套接字,一种用于服务器,一种用于客户端。
ServerSocket类型设计成“监听器”,等待客户端连接的到来。因此,ServerSocket用于服务器。
Socket类用于客户端,它被设计成连接服务器套接字并且初始化协议的交换。

图片来自:http://www.cnblogs.com/rond/p/3565113.html

Socket

Socket可以使客户程序与服务器程序通信,使用Socket连接服务器的过程包含以下4个基本的步骤

(1)创建Socket
(2)打开连接到Socket的输入/输出流
(3)按照一定协议对Socket执行读写操作
(4)关闭Socket

 
构造函数

Socket()

Socket(InetAddress address, int port)throws UnknownHostException, IOException
Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException
Socket(String host, int port)throws UnknownHostException, IOException
Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException
 
除去第一种不带参数的之外,其它构造函数会尝试建立与服务器的连接。如果失败会抛出IOException错误。如果成功,则返回Socket对象。
InetAddress是一个用于记录主机的类,其静态getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。Socket(String host, int port, InetAddress localAddress, int localPort)构造函数的参数分别为目标IP、目标端口、绑定本地IP、绑定本地端口。
 
Socket方法
getInetAddress();      远程服务端的IP地址
getPort();          远程服务端的端口
getLocalAddress()      本地客户端的IP地址
getLocalPort()        本地客户端的端口
getInputStream();     返回与调用的套接字相关联的输入流
getOutStream();      返回与调用的套接字相关联的输出流
值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。
 
Socket状态
isClosed();            //连接是否已关闭,若关闭,返回true;否则返回false
isConnect();      //如果曾经连接过,返回true;否则返回false
isBound();            //如果Socket已经与本地一个端口绑定,返回true;否则返回false
如果要确认Socket的状态是否处于连接中,下面语句是很好的判断方式。
boolean isConnection=socket.isConnected() && !socket.isClosed();   //判断当前是否处于连接

半关闭Socket

很多时候,我们并不知道在获得的输入流里面到底读多长才结束。下面是一些比较普遍的方法:
  • 自定义标识符(譬如下面的例子,当受到“bye”字符串的时候,关闭Socket)
  • 告知读取长度(有些自定义协议的,固定前几个字节表示读取的长度的)
  • 读完所有数据
  • 当Socket调用close的时候关闭的时候,关闭其输入输出流

例:

public class TestPort {
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
String hostname = "www.baidu.com";
InetAddress addr = InetAddress.getByName(hostname);
Socket so = new Socket(addr,80);
System.out.println("远程连接地址:"+so.getInetAddress());
System.out.println("远程连接端口:"+so.getPort());
System.out.println("本地连接地址:"+so.getLocalAddress());
System.out.println("本地连接端口:"+so.getLocalPort());
InputStream is = so.getInputStream();
/*
* 这部分为客户端操作,需要一台远程服务器
int c;
while((c=is.read())!=-1){
System.out.println("gas");
}*/
is.close();
so.close();
}
}

运行结果:

远程连接地址:www.baidu.com/119.75.218.70
远程连接端口:80
本地连接地址:/192.168.1.160
本地连接端口:4338
 ServerSocket
在客户端/服务器端的通信模式中,服务器端用来监听特定端口上客户端的连接。并且可以发送信息,通过ServerSocket类实现,而客户端通过上面的Socket类实现。
 
构造函数
ServerSocket()throws IOException
ServerSocket(int port)throws IOException
ServerSocket(int port, int backlog)throws IOException
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException
 
注意点:
1. port服务端要监听的端口;backlog客户端连接请求的队列长度;bindAddr服务端绑定IP
2. 如果端口被占用或者没有权限使用某些端口会抛出BindException错误。譬如1~1023的端口需要管理员才拥有权限绑定。
3. 如果设置端口为0,则系统会自动为其分配一个端口;
4. bindAddr用于绑定服务器IP,为什么会有这样的设置呢,譬如有些机器有多个网卡。
5. ServerSocket一旦绑定了监听端口,就无法更改。ServerSocket()可以实现在绑定端口前设置其他的参数。
 
ServerSocket类可以通过getInetAddress方法返回此服务器套接字的本地地址,该方法要绑定一个本地的InetAddress,可以在构造方法 
ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException 中通过参数bindAddr传递。
getLocalPort方法用来返回此套接字在其上侦听的接口。
 
:返回服务器端信息
public class GetServerIP {
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
InetAddress addr =InetAddress.getByName("localhost");
ServerSocket se = new ServerSocket(10000,10,addr);
System.out.println(se.getInetAddress());
System.out.println(se.getLocalPort());
}
}

运行结果

localhost/127.0.0.1
10000

附:完整例子

发送端 socket

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket; public class SocketSender {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1", 27401);
// 向服务端发送信息
OutputStream outer = socket.getOutputStream();
byte[] b = "客户端:向服务端发送文字,\"这是一行测试....\"".getBytes();
outer.write(b);
outer.flush();
System.out.println("发送完毕!"); // 接收服务端的返回值
InputStream inner = socket.getInputStream();
int count = 0;
while (count == 0) {
count = inner.available();
}
byte[] recv = new byte[count];
inner.read(recv); String str_recv = new String(recv);
System.out.println("客户端:接收到服务端的文字:" + str_recv);
} catch (IOException e) {
System.out.println("发送端出现异常");
} finally {
if (socket != null)
try {
socket.close();
} catch (IOException e) {
System.out.println("发送端 finally 出现异常");
}
}
}
}

接收端:ServerSocket

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; public class SocketReceiver {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(27401);
while (true) {
Socket socket = serverSocket.accept(); // 接收客户端的信息
InputStream in = socket.getInputStream();
int count = 0;
while (count == 0) {
count = in.available();
}
byte[] b = new byte[count];
in.read(b);
String str = new String(b);
System.out.println(str); // 向客户端发送确认消息
OutputStream outer = socket.getOutputStream();
byte[] b_out = "已经收到,返回消息码200".getBytes();
outer.write(b_out);
outer.flush(); // 关闭socket
socket.close();
}
} catch (IOException e) {
System.out.println("接收端出现异常");
} finally {
if (serverSocket != null)
try {
serverSocket.close();
} catch (IOException e) {
System.out.println("接收端 finally 出现异常");
}
}
}
}

接收端:方法2,利用多线程,每一个发送端对应接收端的一个线程

public class SocketReceiverWithThread {
public static void main(String[] args) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(27401);
while (true) {
Socket socket = serverSocket.accept();
new Thread(new Handler(socket)).start();
//socket.close(); 注意 关闭socket不能在这里,而应该写在线程内,否则可能线程没结束就关闭了socket
} } catch (IOException e) {
System.out.println("接收端出现异常");
} finally {
if (serverSocket != null)
try {
serverSocket.close();
} catch (IOException e) {
System.out.println("接收端 finally 出现异常");
}
}
}
} class Handler implements Runnable {
private Socket socket;
public Handler(Socket socket) {
this.socket = socket;
}
public void run() {
try {
// 接收客户端的信息
InputStream in = socket.getInputStream();
int count = 0;
while (count == 0) {
count = in.available();
}
byte[] b = new byte[count];
in.read(b);
String str = new String(b);
System.out.println(str); // 向客户端发送确认消息
OutputStream outer = socket.getOutputStream();
byte[] b_out = "已经收到,返回消息码200".getBytes();
outer.write(b_out);
outer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭socket
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

附2:使用socket进行文件传送

客户端 ClienPart.java

package com.client;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket; public class ClientPart {
private Socket socket=null;
private String ip ;// 设置成服务器IP
private int port ; public ClientPart(String ip,int port) {
this.ip=ip;
this.port=port; }
private boolean createConnection() {
try {
socket = new Socket(ip, port);
System.out.print("连接服务器成功!" + "\n");
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
//向服务器发送消息
private void sentMessage(String msg){
if (socket == null)
return;
Writer writer;
try {
writer = new OutputStreamWriter(socket.getOutputStream());
writer.write(msg);
writer.flush(); //写完后要记得flush
writer.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void getFile() {
if (socket == null)
return;
DataInputStream inputStream = null;
try {
inputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
} catch (Exception e) {
System.out.print("接收消息缓存错误\n");
return;
}
try {
//本地保存路径,文件名会自动从服务器端继承而来。
String savePath = "F:\\client\\";
int bufferSize = 8192;
byte[] buf = new byte[bufferSize];
long len=0; savePath += inputStream.readUTF(); //读取文件名
DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
len = inputStream.readLong(); //读取文件长度 System.out.println("文件的长度为:" + len);
System.out.println("开始接收文件!"); while (true) {
int read = 0;
if (inputStream != null) {
read = inputStream.read(buf);
}
if (read == -1) {
break;
}
//客户端读出文件
fileOut.write(buf, 0, read);
} System.out.println("接收完成,文件存为" + savePath + "\n");
fileOut.close();
socket.close();
} catch (Exception e) {
System.out.println("接收消息错误" + "\n");
return;
}
} private void putFile(String filename){
String filePath = filename;
File client_file = new File(filePath); //要传输的文件路径
long filelen = client_file.length();
System.out.println("要上传的文件长度:" + (int)filelen);
try{
DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
DataOutputStream out_stream = new DataOutputStream(socket.getOutputStream());
out_stream.writeUTF(client_file.getName());
out_stream.flush();
out_stream.writeLong((long) client_file.length());
out_stream.flush(); int bufferSize = 8192;
byte[] buf = new byte[bufferSize]; while (true) {
int read = 0;
if (in_stream != null) {
read = in_stream.read(buf);
}
if (read == -1) {
break;
}
out_stream.write(buf, 0, read);
}
out_stream.flush();
// 注意关闭socket链接,不然客户端会等待server的数据过来,
// 直到socket超时,会导致数据不完整。
in_stream.close();
socket.close();
System.out.println("文件传输完成");
}catch(Exception e){
e.printStackTrace();
} } public static void main(String arg[]) {
ClientPart client = new ClientPart("127.0.0.1",7005);
client.createConnection();
client.sentMessage("read");//向服务器发送信息,说明需要读取文件
client.createConnection();
client.getFile(); //获取服务器发送的文件 client.createConnection();
client.sentMessage("write");//向服务器发送信息,说明需要上传文件
client.createConnection();
client.putFile("F:\\client\\1.txt");//向服务器发送文件 }
}

服务端 ServerPart.java

package com.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.ServerSocket;
import java.net.Socket; public class ServerPart {
int port = 7005;
public void serverStart() {
try {
ServerSocket ssocket = new ServerSocket(port);
System.out.println("服务器准备完毕!");
while (true) {
String msg = checkIO(ssocket); //获取来自客户端的请求是读/写
if(msg.compareTo("read")==0){
sendFile(ssocket); //向客户端发送文件
}
else if(msg.compareTo("write")==0){
saveFile(ssocket); //存储来自客户端的文件
}
}
} catch (Exception e) {
e.printStackTrace();
}
} //服务器检查来自客户端的请求是读/写
public String checkIO(ServerSocket ssocket) throws IOException{
Socket socket=ssocket.accept();
Reader reader = new InputStreamReader(socket.getInputStream());
char chars[] = new char[10];
int len;
StringBuilder sb = new StringBuilder();
while ((len=reader.read(chars)) != -1) {
sb.append(new String(chars, 0, len));
}
reader.close();
socket.close();
return sb.toString();
} public void sendFile(ServerSocket ssocket) throws IOException{
Socket soc = null;
// 选择进行传输的文件
String filePath = "F:\\server\\info.bin";
File myfile = new File(filePath);
System.out.println("服务器文件长度:" + (int) myfile.length());
soc = ssocket.accept();
System.out.println("建立socket链接"); DataInputStream in_stream = new DataInputStream(new BufferedInputStream(new FileInputStream(filePath)));
DataOutputStream out_stream = new DataOutputStream(soc.getOutputStream());
//将文件名及长度传给客户端。这里要真正适用所有平台,例如中文名的处理,还需要加工,具体可以参见Think In Java 4th里有现成的代码。
out_stream.writeUTF(myfile.getName());
out_stream.flush();
out_stream.writeLong((long) myfile.length());
out_stream.flush(); int bufferSize = 8192;
byte[] buf = new byte[bufferSize]; while (true) {
int read = 0;
if (in_stream != null) {
read = in_stream.read(buf);
}
if (read == -1) {
break;
}
out_stream.write(buf, 0, read);
}
out_stream.flush();
// 注意关闭socket链接哦,不然客户端会等待server的数据过来,
// 直到socket超时,导致数据不完整。
out_stream.close();
in_stream.close();
soc.close();
System.out.println("服务器文件传输完成");
} //服务器保存文件的方法
public void saveFile(ServerSocket ssocket) throws IOException{
Socket soc = ssocket.accept();
if (soc == null)
return;
DataInputStream inputStream = null;
try {
inputStream = new DataInputStream(new BufferedInputStream(soc.getInputStream()));
} catch (Exception e) {
System.out.print("服务器接收消息缓存错误\n");
return;
} try {
//服务器文件的保存路径
String savePath = "f:\\server\\";
int bufferSize = 8192;
byte[] buf = new byte[bufferSize];
long len=0; savePath += inputStream.readUTF(); //读取文件名
DataOutputStream fileOut = new DataOutputStream(new BufferedOutputStream(new BufferedOutputStream(new FileOutputStream(savePath))));
len = inputStream.readLong(); //读取文件长度
//控制台输出
System.out.println("文件的长度为:" + len);
System.out.println("开始接收文件!"); while (true) {
int read = 0;
if (inputStream != null) {
read = inputStream.read(buf);
}
if (read == -1) {
break;
}
//客户端读出文件
fileOut.write(buf, 0, read);
}
System.out.println("接收完成,文件存为" + savePath + "\n");
fileOut.close();
soc.close();
} catch (Exception e) {
System.out.println("接收消息错误" + "\n");
e.printStackTrace();
return;
}
} public static void main(String arg[]) {
//启动服务器
new ServerPart().serverStart();
}
}
 

Java学习笔记--Socket和ServerSocket的更多相关文章

  1. Java学习笔记——Socket实现文件传输

    我越是逃离,却越是靠近你. 我越是背过脸,却越是看见你. 我从你开始, 我在你结束. 需求:实现局域网下socket传输文件. 客户端步骤: 1.建立与服务器的连接 2.创建client输出流 3.创 ...

  2. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  3. 20155234 2016-2017-2第十周《Java学习笔记》学习总结

    20155234第十周<Java学习笔记>学习总结 教材学习内容总结 网络编程 在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的位置,或者接收到指定 ...

  4. 20145330第十周《Java学习笔记》

    20145330第十周<Java学习笔记> 网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就 ...

  5. 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁

    什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...

  6. 0035 Java学习笔记-注解

    什么是注解 注解可以看作类的第6大要素(成员变量.构造器.方法.代码块.内部类) 注解有点像修饰符,可以修饰一些程序要素:类.接口.变量.方法.局部变量等等 注解要和对应的配套工具(APT:Annot ...

  7. Java学习笔记(04)

    Java学习笔记(04) 如有不对或不足的地方,请给出建议,谢谢! 一.对象 面向对象的核心:找合适的对象做合适的事情 面向对象的编程思想:尽可能的用计算机语言来描述现实生活中的事物 面向对象:侧重于 ...

  8. 0032 Java学习笔记-类加载机制-初步

    JVM虚拟机 Java虚拟机有自己完善的硬件架构(处理器.堆栈.寄存器等)和指令系统 Java虚拟机是一种能运行Java bytecode的虚拟机 JVM并非专属于Java语言,只要生成的编译文件能匹 ...

  9. 0030 Java学习笔记-面向对象-垃圾回收、(强、软、弱、虚)引用

    垃圾回收特点 垃圾:程序运行过程中,会为对象.数组等分配内存,运行过程中或结束后,这些对象可能就没用了,没有变量再指向它们,这时候,它们就成了垃圾,等着垃圾回收程序的回收再利用 Java的垃圾回收机制 ...

随机推荐

  1. 指定Action、Category调用系统Activity

    1.Intent对象详解 Android的应用程序包含三种重要组件:Activity.Service.BroadcastReceiver,应用程序采用一致的方式来启动它们----都是依靠Intent来 ...

  2. uva 10020 Minimal coverage

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...

  3. activity5 流程 入门

    http://blog.csdn.net/yangyi22/article/details/9225849 谢谢原文作者提供!

  4. HASH JOIN算法

    哈希连接(HASH JOIN) 前文提到,嵌套循环只适合输出少量结果集.如果要返回大量结果集(比如返回100W数据),根据嵌套循环算法,被驱动表会扫描100W次,显然这是不对的.看到这里你应该明白为 ...

  5. AES - Rijndael 算法(三)

    四.Rijndael算法实现,java版本 public class Rijndael_Algorithms {  private byte[] key;  /**------------------ ...

  6. Objective-C实现变参函数

    原文:http://www.tanhao.me/pieces/1104.html   NSLog(NSString *format, ...)   + (id)arrayWithObjects:(id ...

  7. ios NSString 去除空格和回车

    去除两端空格 NSString *temp = [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCh ...

  8. C++ lambda 表达式传递的变量默认不可变

    我遇到例如以下问题: int count=0; listener->onTouchMoved=[count](Touch* t,Event* e){ count++; log("onT ...

  9. Keil IDE指南.

    Keil IDE指南(转载) 熟悉Keil C 51的朋友对于Keil MDK上手应该比较容易,毕竟界面是很像的.但ARM内核毕竟不同于51内核,因此无论在设置上还是在编程思想上,都需要下番功夫研究的 ...

  10. 判断Http请求由手机端发起,还是有电脑端发起

    某些情形,我们需要判断Http请求是来自手机端还是电脑端,关键是取得User-Agent的信息,进行筛选判断即可. 核心类如下: public static boolean isMobileDevic ...