HTTP与Socket分析与编程
一、TCP/IP的介绍
首先先介绍一下TCP/IP,TCP是传输层协议,IP是网络层协议。手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上。
建立起一个TCP连接需要经过“三次握手”:
第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)
二、HTTP连接
HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用,是应用层协议。
HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。
2)在HTTP 1.1中则可以在一次连接中处理多个请求,并且多个请求可以重叠进行,
不需要等待一个请求结束后再发送下一个请求。
由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即使不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。
(一)HTTP 8种请求方法:
1)GET:请求获取Request-URI 所标识的资源。
2)POST:在Request-URI所标识的资源后附加新的数据。
3)HEAD:请求获取Request-URI所标识的资源的响应消息头。
4)OPTIONS:请求查询服务器性能,或查询与资源相关的选项和需求。
5)PUT:请求服务器存储一个资源,并用Request-URI作为其标识。
6)DELETE:请求服务器删除由Request-URI所标识的资源。
7)TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断。
8)CONNECT:HTTP1.1协议中预留给能够将连接改为管道方式的代理服务器。
详解GET和POST
1)GET:
GET方法是默认的HTTP请求方法,我们日常用GET方法来提交表单数据,然而用GET提交的数据只经过简单的编码,同时它将作为URL的一部分向web服务器发
送,因此使用GET方法来提交数据存在着安全隐患,例如:
Http://127.0.0.1/login.jsp?Name=zhangshi&Age=30&Submit=%cc%E+%BD%BB
从上面的内容,可以很容易的辨认出提交表单的内容(?之后的内容),另外由GET 方法提交的数据是作为URL请求的一部分,所以提交的数据量不能太大。
2)POST:
POST方法是GET方法的一个替代方法,它主要是向Web服务器提交表单数据,尤其是大批量的数据。POST方法克服了GET方法的一些缺点。通过POST方法提交表单数据时,数据不是作为URL请求的一部分而是作为标准数据传送给Web服务器,这就克服了GET方法中的信息无法保密和数据量太小的缺点。因此,出于安全的考虑以及对用户隐私的尊重,通常表单提交时采用POST方法。
(二) Java之HTTP通信
1. 通过URLConnection的通信
通信流程:
第一:创建URL以及HttpURLConnection对象。
第二:连接参数设置。
第三:连接到服务器。
第四:向服务器写数据。
最后一步:从服务器读取数据。
下面是一些接口的介绍:
HttpURLConnection是java的标准类,继承自URLConnection类,URLConnection与HttpURLConnection都是抽象类,无法直接实例化对象。其对象主要通过URL的openConnection方法获得,创建一个httpURLConnection连接的代码如下所示:
URL url = new UR(“https://www.doczj.com/doc/1911476027.html,”);
HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
openConnection方法只创建URLConnection或者HttpURLConnection实例,但是并不进行真正的连接操作。并且,每次openConnection都将创建一个新的实例。因此,在连接之前我们可以对其一些属性进行设置,比如超时时间等。
下面对HttpURLConnetcion实例的属性设置:
//设置输入/输出流
connection.setDoOutput(true);
connection.setDoInput(true);
//设置请求的方式为Get或者Post
connection.setRequestMethod(“GET”);
connection.setRequestMethod(“POST”);
//在设置POST方式时要注意,POST请求方式不能够使用缓存
connection.setUseCaches(false);
在完成HttpURLConnection实例的初始化以后,我们可以分别使用Get和POST 方式来完成一个实例。
下面两个例子是分别使用GET和POST请求方式的方法例子:
1) GET请求
package com.dingo.sendurl;
import https://www.doczj.com/doc/1911476027.html,.HttpURLConnection;
import https://www.doczj.com/doc/1911476027.html,.URL;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class send_url_get {
private String urlStr;
private URL url;
private HttpURLConnection url_con;
private String contentStr;
public void setUrlStr(String urlStr) {
this.urlStr = urlStr;
}
public String getContentStr() {
return contentStr;
}
private void setContentStr(String contentStr) {
this.contentStr = contentStr;
}
public void send_url(){
try{
StringBuilder temp = new StringBuilder();
url = new url(/urlStr);
url_con= (HttpURLConnection)url.openConnection();
url_con.setDoOutput(true);
url_con.setRequestMethod("GET");
url_con.getOutputStream().flush();
url_con.getOutputStream().close();
InputStream in =url_con.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(in)); while(rd.read()!=-1){
temp.append(rd.readLine());
}
setContentStr(new String (temp));
} catch (Exception e){
e.printStackTrace();
} finally{
if(url_con!=null){
url_con.disconnect();
}
}
}
}
2) POST 请求
//post
package com.dingo.sendurl;
import https://www.doczj.com/doc/1911476027.html,.*;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class send_url_post {
private String urlStr;
private URL url;
private HttpURLConnection url_con;
private String response_content;
public void setUrlStr(String urlStr) {
this.urlStr = urlStr;
}
public String getResponse_content() {
return response_content;
}
private void setResponse_content(String response_content) {
this.response_content = response_content;
}
public void send_url(String mobile_number){
try{
url = new url(/urlStr);
url_con=(HttpURLConnection)url.openConnection();
url_con.setRequestMethod("POST");
url_con.setDoOutput(true);
String param="action=mobile&mobile="+mobile_number;
url_con.getOutputStream().write(param.getBytes());
url_con.getOutputStream().flush();
url_con.getOutputStream().close();
InputStream in= url_con.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(in)); StringBuilder tempStr=new StringBuilder();
while(rd.read()!=-1){
tempStr.append(rd.readLine());
}
setResponse_content(new String(tempStr));
} catch(Exception e){
e.printStackTrace();
}
finally{
if(url_con!=null)
url_con.disconnect();
}
}
}
2. 通过HttpClient进行通信
HTTP 协议可能是现在Internet 上使用得最多、最重要的协议了,越来越多的Java 应用程序需要直接通过HTTP 协议来访问网络资源。虽然在JDK 的java net包中已经提供了访问HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。HttpClient 是Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持HTTP 协议的客户端编程工具包,并且它支持HTTP 协议最新的版本和建议。HttpClient 已经应用在很多的项目中,比如Apache Jakarta 上很著名的另外两个开源项目Cactus 和HTMLUnit都使用了HttpClient。现在HttpClient最新版本为HttpClient 4.2 (GA)。
HttpClient的主要功能:
1) 实现所有HTTP的方法
2) 支持自动转向
3) 支持HTTPS协议
4) 支持代理服务器等
使用HttpClient的步骤:
1) 创建HttpClient实例。
2) 创建某种连接方法的实例,如果是get方法就使用GetMethod,如果是post方法,就
使用PostMethod,然后再方法的构造函数中传入待连接的地址。
3) 调用第一步中创建好的实例的execute方法来执行第二部创建好的method实例。
4) 读response
5) 释放连接,无论执行方法是否成功,都要释放连接。
6) 对得到后的内容进行处理。
下面是一些例子用来演示HttpClient的使用。
1) 读取网页(http/https)的内容
package http.demo;
import java.io.IOException;
import https://www.doczj.com/doc/1911476027.html,mons.httpclient.*;
import https://www.doczj.com/doc/1911476027.html,mons.httpclient.methods.*;
/**
*最简单的HTTP客户端,用来演示通过GET或者POST方式访问某个页面
*@author Liudong
*/
public class SimpleClient{
public static void main(String[]args)throws IOException
{
HttpClient client=new HttpClient();
//设置代理服务器地址和端口
//client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port);
//使用GET方法,如果服务器需要通过HTTPS连接,那只需要将下面URL中的http换成https HttpMethod method=new GetMethod("https://www.doczj.com/doc/1911476027.html,");
//使用POST方法
//HttpMethod method = new PostMethod("https://www.doczj.com/doc/1911476027.html,");
client.executeMethod(method);
//打印服务器返回的状态
System.out.println(method.getStatusLine());
//打印返回的信息
System.out.println(method.getResponseBodyAsString());
//释放连接
method.releaseConnection();
}
}
2) 以GET或POST方式向网页提交参数
我们知道如果是GET的请求方式,那么所有参数都直接放到页面的URL后面用问号与页面地址隔开,每个参数用&隔开,例如:
https://www.doczj.com/doc/1911476027.html,/?name=liudong&mobile=123456,但是当使用POST
方法时就会稍微有一点点麻烦。本小节的例子演示向如何查询手机号码所在的城市,代码如下:
package http.demo;
import java.io.IOException;
import https://www.doczj.com/doc/1911476027.html,mons.httpclient.*;
import https://www.doczj.com/doc/1911476027.html,mons.httpclient.methods.*;
/**
*提交参数演示
*该程序连接到一个用于查询手机号码所属地的页面
*以便查询号码段1330227所在的省份以及城市
*@author Liudong
*/
public class SimpleHttpClient{
public static void main(String[]args)throws IOException{
HttpClient client=new HttpClient();
client.getHostConfiguration().setHost("https://www.doczj.com/doc/1911476027.html,",80,"http");
method=getPostMethod(); //使用POST方式提交数据
client.executeMethod(method); //打印服务器返回的状态
System.out.println(method.getStatusLine()); //打印结果页面
Stringresponse=new String(method.getResponseBodyAsString().getBytes("8859_1"));
//打印返回的信息
System.out.println(response);
method.releaseConnection();
}
/**
*使用GET方式提交数据
*@return
*/
private static HttpMethod getGetMethod(){
return new GetMethod("/simcard.php?simcard=1330227");
}
/**
*使用POST方式提交数据
*@return
*/
private static HttpMethod getPostMethod(){
PostMethod post=new PostMethod("/simcard.php");
NameValuePair simcard=new NameValuePair("simcard","1330227");
post.setRequestBody(new NameValuePair[]{simcard});
return post;
}
}
在上面的例子中页面https://www.doczj.com/doc/1911476027.html,/simcard.php需要一个参数是simcard,这个参数值为手机号码段,即手机号码的前七位,服务器会返回提交的手机号码对应的省份、城市以及其他详细信息。GET的提交方法只需要在URL后加入参数信息,而POST则需要通过NameValuePair类来设置参数名称和它所对应的值。
3) 处理页面重定向
在JSP/Servlet编程中response.sendRedirect方法就是使用HTTP协议中的重定向机制。它与JSP中的
HttpMethod.getStatusCode()方法判断返回值是否为下表中的某个值来判断是否需要跳转。如果已经确认需要进行页面跳转了,那么可以通过读取HTTP头中的location 属性来获取新的地址。
下面的代码片段演示如何处理页面的重定向
client.executeMethod(post);
System.out.println(post.getStatusLine().toString());
post.releaseConnection();
//检查是否重定向
int statuscode = post.getStatusCode();
if((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) || (statuscode == HttpStatus.SC_MO VED_PERMANENTLY) || (statuscode ==HttpStatus.SC_SEE_OTHER) || (statuscode == HttpStatu s.SC_TEMPORARY_REDIRECT)) {
//读取新的URL地址
Headerheader=post.getResponseHeader("location");
if (header!=null){
Stringnewuri=header.getValue();
if((newuri==null)||(newuri.equals("")))
newuri="/";
GetMethodredirect=new GetMethod(newuri);
client.executeMethod(redirect);
System.out.println("Redirect:"+redirect.getStatusLine().toString());
redirect.releaseConnection();
}else
System.out.println("Invalid redirect");
}
我们可以自行编写两个JSP页面,其中一个页面用response.sendRedirect方法
重定向到另外一个页面用来测试上面的例子。
4)模拟输入用户名和口令进行测试
本小节应该说是HTTP客户端编程中最常碰见的问题,很多网站的内容都只是对注册用户可见的,这种情况下就必须要求使用正确的用户名和口令登录成功后,方可浏览到想要的页面。因为HTTP协议是无状态的,也就是连接的有效期只限于当前请求,请求内容结束后连接就关闭了。在这种情况下为了保存用户的登录信息必须使用到Cookie机制。以
JSP/Servlet为例,当浏览器请求一个JSP或者是Servlet的页面时,应用服务器会返回一个参数,名为jsessionid(因不同应用服务器而异),值是一个较长的唯一字符串
的Cookie,这个字符串值也就是当前访问该站点的会话标识。浏览器在每访问该站点的其他页面时候都要带上jsessionid这样的Cookie信息,应用服务器根据读取这个会话标识来获取对应的会话信息。
对于需要用户登录的网站,一般在用户登录成功后会将用户资料保存在服务器的会话中,这样当访问到其他的页面时候,应用服务器根据浏览器送上的Cookie中读取当前请求对
应的会话标识以获得对应的会话信息,然后就可以判断用户资料是否存在于会话信息中,如果存在则允许访问页面,否则跳转到登录页面中要求用户输入帐号和口令进行登录。这就是一般使用JSP开发网站在处理用户登录的比较通用的方法。
这样一来,对于HTTP的客户端来讲,如果要访问一个受保护的页面时就必须模拟浏览器所做的工作,首先就是请求登录页面,然后读取Cookie值;再次请求登录页面并加入
登录页所需的每个参数;最后就是请求最终所需的页面。当然在除第一次请求外其他的请求都需要附带上Cookie信息以便服务器能判断当前请求是否已经通过验证。说了这么多,
可是如果你使用httpclient的话,你甚至连一行代码都无需增加,你只需要先传递登录
信息执行登录过程,然后直接访问想要的页面,跟访问一个普通的页面没有任何区别,因为类HttpClient已经帮你做了所有该做的事情了,下面的例子实现了这样一个访问的过程。
package http.demo;
import https://www.doczj.com/doc/1911476027.html,mons.httpclient.*;
import https://www.doczj.com/doc/1911476027.html,mons.httpclient.cookie.*;
import https://www.doczj.com/doc/1911476027.html,mons.httpclient.methods.*;
/**
*用来演示登录表单的示例
*@author Liudong
*/
public class FormLoginDemo{
static final String LOGON_SITE="localhost";
static final int LOGON_PORT=8080;
public static void main(String[]args)throws Exception{
HttpClient client=new HttpClient();
client.getHostConfiguration().setHost(LOGON_SITE,LOGON_PORT);
//模拟登录页面login.jsp->main.jsp
PostMethod post=new PostMethod("/main.jsp");
NameValuePair name=new NameValuePair("name","ld");
NameValuePair pass=new NameValuePair("password","ld");
post.setRequestBody(new NameValuePair[]{name,pass});
int status=client.executeMethod(post);
System.out.println(post.getResponseBodyAsString());
post.releaseConnection();
//查看cookie信息
CookieSpec cookiespec=CookiePolicy.getDefaultSpec();
Cookie[]cookies=cookiespec.match(LOGON_SITE,LOGON_PORT,"/",false,client .getState().getCookies());
if(cookies.length==0){
System.out.println("None");
}else{
for(int i=0;i System.out.println(cookies[i].toString()); } } //访问所需的页面main2.jsp GetMethodget=new GetMethod("/main2.jsp"); client.executeMethod(get); System.out.println(get.getResponseBodyAsString()); get.releaseConnection(); } } 5)提交XML格式的参数 提交XML格式的参数很简单,仅仅是一个提交时候的ContentType问题,下面的例子演示从文件文件中读取XML信息并提交给服务器的过程,该过程可以用来测试Web服务。 import java.io.File; import java.io.FileInputStream; import https://www.doczj.com/doc/1911476027.html,mons.httpclient.HttpClient; import https://www.doczj.com/doc/1911476027.html,mons.httpclient.methods.EntityEnclosingMethod; import https://www.doczj.com/doc/1911476027.html,mons.httpclient.methods.PostMethod; /** *用来演示提交XML格式数据的例子 */ public class PostXMLClient{ public static void main(String[]args)throws Exception{ File input=new File(“test.xml”); PostMethod post=new PostMethod(“http://localhost:8080/httpclient/xml.jsp”); // 设置请求的内容直接从文件中读取 post.setRequestBody(new FileInputStream(input)); if(input.length() post.setRequestContentLength(input.length()); else post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED); // 指定请求内容的类型 post.setRequestHeader("Content-type","text/xml; charset=GBK"); HttpClient httpclient=new HttpClient(); int result=httpclient.executeMethod(post); System.out.println("Response status code: "+result); System.out.println("Response body: "); System.out.println(post.getResponseBodyAsString()); post.releaseConnection(); } } 6)通过http上传文件 httpclient使用了单独的一个HttpMethod子类来处理文件的上传,这个类就是MultipartPostMethod,该类已经封装了文件上传的细节,我们要做的仅仅是告诉它我们要上传文件的全路径即可,下面的代码片段演示如何使用这个类。 MultipartPostMethod filePost = new MultipartPostMethod(targetURL); filePost.addParameter("fileName",targetFilePath); HttpClient client = new HttpClient(); //由于要上传的文件可能比较大,因此在此设置最大的连接超时时间 client.getHttpConnectionManager().getParams().setConnectionTimeout(5000); int status=client.executeMethod(filePost); 上面代码中,targetFilePath即为要上传的文件所在的路径。 7)访问启用认证的页面 我们经常会碰到这样的页面,当访问它的时候会弹出一个浏览器的对话框要求输入用户名和密码后方可,这种用户认证的方式不同于我们在前面介绍的基于表单的用户身份验证。这是HTTP的认证策略,httpclient支持三种认证方式包括:基本、摘要以及NTLM认证。其中基本认证最简单、通用但也最不安全;摘要认证是在HTTP 1.1中加入的认证方式,而NTLM则是微软公司定义的而不是通用的规范,最新版本的NTLM是比摘要认证还要安全的一种方式。 下面例子是从httpclient的CVS服务器中下载的,它简单演示如何访问一个认证保护的页面: import https://www.doczj.com/doc/1911476027.html,mons.httpclient.HttpClient; import https://www.doczj.com/doc/1911476027.html,ernamePasswordCredentials; import https://www.doczj.com/doc/1911476027.html,mons.httpclient.methods.GetMethod; public class BasicAuthenticationExample{ public BasicAuthenticationExample(){ } public static void main(String[]args)throws Exception{ HttpClient client=new HttpClient(); client.getState().setCredentials("https://www.doczj.com/doc/1911476027.html,","realm",new UsernamePas swordCredentials("username","password")); GetMethod get=new GetMethod("https://https://www.doczj.com/doc/1911476027.html,/products/index.html"); get.setDoAuthentication(true); int status=client.executeMethod(get); System.out.println(status+"\n"+get.getResponseBodyAsString()); get.releaseConnection(); } } 8)多线程模式下使用httpclient 多线程同时访问httpclient,例如同时从一个站点上下载多个文件。对于同一个HttpConnection同一个时间只能有一个线程访问,为了保证多线程工作环境下不产生冲突,httpclient使用了一个多线程连接管理器的类:MultiThreadedHttpConnectionManager,要使用这个类很简单,只需要在构造HttpClient实例的时候传入即可,代码如下: MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnection Manager(); HttpClient client = new HttpClient(connectionManager); 以后尽管访问client实例即可。 三、Socket 套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址, 远地进程的协议端口。 应用层通过传输层进行数据通信时,TCP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个TCP 协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为 应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层 通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输 的并发服务。 (一)Socket连接于TCP连接的关系 创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP或UDP),当使用TCP协议进行连接时,该Socket连接就是一 个TCP连接。 (二)Socket与HTTP连接 由于通常情况下Socket连接就是TCP连接,因此Socket连接一旦建立,通信双方即可开始相互发送数据内容,直到双方连接断开。但在实际网络应用中, 客户端到服务器之间的通信往往需要穿越多个中间节点,例如路由器、网关、防火 墙等,大部分防火墙默认会关闭长时间处于非活跃状态的连接而导致Socket 连 接断连,因此需要通过轮询告诉网络,该连接处于活跃状态。 而HTTP连接使用的是“请求—响应”的方式,不仅在请求时需要先建立连接,而且需要客户端向服务器发出请求后,服务器端才能回复数据。 很多情况下,需要服务器端主动向客户端推送数据,保持客户端与服务器数据的实时与同步。此时若双方建立的是Socket连接,服务器就可以直接将数据传送 给客户端;若双方建立的是HTTP连接,则服务器需要等到客户端发送一次请求 后才能将数据传回给客户端,因此,客户端定时向服务器端发送连接请求,不仅可 以保持在线,同时也是在“询问”服务器是否有新的数据,如果有就将数据传给客户 端。 (三)Java socket通信 1.1V1通信 服务端: 1.package com.dnion.socket; 2. 3.import java.io.BufferedReader; 4.import java.io.BufferedWriter; 5.import java.io.InputStreamReader; 6.import java.io.OutputStreamWriter; 7.import java.io.PrintWriter; 8.import https://www.doczj.com/doc/1911476027.html,.ServerSocket; 9.import https://www.doczj.com/doc/1911476027.html,.Socket; 10. 11.public class JabberServer { 12. 13.public static int PORT = 8080; 14.public static void main(String[] agrs) { 15.ServerSocket s = null; 16.Socket socket = null; 17.BufferedReader br = null; 18.PrintWriter pw = null; 19.try { 20.//设定服务端的端口号 21.s = new ServerSocket(PORT); 22.System.out.println("ServerSocket Start:"+s); 23.//等待请求,此方法会一直阻塞,直到获得请求才往下走 24.socket = s.accept(); 25.System.out.println("Connection accept socket:"+socket); 26.//用于接收客户端发来的请求 27.br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 28.//用于发送返回信息,可以不需要装饰这么多io流使用缓冲流时发送数据要注意调用.flush()方法 29.pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true); 30.while(true){ 31.String str = br.readLine(); 32.if(str.equals("END")){ 33.break; 34.} 35.System.out.println("Client Socket Message:"+str); 36.Thread.sleep(1000); 37.pw.println("Message Received"); 38.pw.flush(); 39.} 40. 41.} catch (Exception e) { 42.// TODO Auto-generated catch block 43. e.printStackTrace(); 44.}finally{ 45.System.out.println("Close....."); 46.try { 47.br.close(); 48.pw.close(); 49.socket.close(); 50.s.close(); 51.} catch (Exception e2) { 52. 53.} 54.} 55.} 56.} 客户端: 1.package com.dnion.socket; 2. 3.import java.io.BufferedReader; 4.import java.io.BufferedWriter; 5.import java.io.IOException; 6.import java.io.InputStreamReader; 7.import java.io.OutputStreamWriter; 8.import java.io.PrintWriter; 9.import https://www.doczj.com/doc/1911476027.html,.Socket; 10. 11.public class JabberClient { 12. 13.public static void main(String[] args) { 14.Socket socket = null; 15.BufferedReader br = null; 16.PrintWriter pw = null; 17.try { 18.//客户端socket指定服务器的地址和端口号 19.socket = new Socket("127.0.0.1", JabberServer.PORT); 20.System.out.println("Socket=" + socket); 21.//同服务器原理一样 22.br = new BufferedReader(new InputStreamReader( 23.socket.getInputStream())); 24.pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter( 25.socket.getOutputStream()))); 26.for (int i = 0; i < 10; i++) { 27.pw.println("howdy " + i); 28.pw.flush(); 29.String str = br.readLine(); 30.System.out.println(str); 31.} 32.pw.println("END"); 33.pw.flush(); 34.} catch (Exception e) { 35.e.printStackTrace(); 36.} finally { 37.try { 38.System.out.println("close......"); 39.br.close(); 40.pw.close(); 41.socket.close(); 42.} catch (IOException e) { 43.// TODO Auto-generated catch block 44.e.printStackTrace(); 45.} 46.} 47.} 48. 49.} 2.一对多通信,使用多线程实现 服务端: 服务类: 1.package com.dnion.socket; 2. 3.import java.io.IOException; 4.import https://www.doczj.com/doc/1911476027.html,.ServerSocket; 5.import https://www.doczj.com/doc/1911476027.html,.Socket; 6. 7.public class MultiJabberServer { 8. 9. 10.public static void main(String[] args) { 11.ServerSocket s = null; 12.Socket socket = null; 13.try { 14.s = new ServerSocket(8080); 15.//等待新请求、否则一直阻塞 16.while(true){ 17.socket = s.accept(); 18.System.out.println("socket:"+socket); 19.new ServeOneJabbr(socket); 20. 21.} 22.} catch (Exception e) { 23.try { 24.socket.close(); 25.} catch (IOException e1) { 26.// TODO Auto-generated catch block 27.e1.printStackTrace(); 28.} 29.}finally{ 30.try { 31.s.close(); 32.} catch (IOException e) { 33.// TODO Auto-generated catch block 34. e.printStackTrace(); 35.} 36.} 37. 38. 39.} 40. 41.} 服务端响应请求的类: 1.package com.dnion.socket; 2. 3.import java.io.BufferedReader; 4.import java.io.BufferedWriter; 5.import java.io.IOException; 6.import java.io.InputStreamReader; 7.import java.io.OutputStreamWriter; 8.import java.io.PrintWriter; 9.import https://www.doczj.com/doc/1911476027.html,.Socket; 10. 11.public class ServeOneJabbr extends Thread{ 12. 13.private Socket socket = null; 14.private BufferedReader br = null; 15.private PrintWriter pw = null; 16. 17.public ServeOneJabbr(Socket s){ 18.socket = s; 19.try { 20.br = new BufferedReader(new InputStreamReader(socket.getInputStream())); 21.pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);