socket详解及简易聊天室编写
来源:互联网 发布:模拟炒股软件手机版 编辑:程序博客网 时间:2024/05/20 01:09
转载请注明出处:http://blog.csdn.net/u012975705/article/details/48752377
app源码下载地址:https://github.com/noyo/ChatRoom/tree/master
服务器端代码下载地址:http://download.csdn.net/detail/u012975705/9141251
Socket介绍
网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。
Server端Listen(监听)某个端口是否有连接请求,Client端向Server 端发出Connect(连接)请求,Server端向Client端发回Accept(接受)消息。一个连接就建立起来了。Server端和Client 端都可以通过Send,Write等方法与对方通信。
对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:1、创建Socket;2、 打开连接到Socket的输入/出流;3、 按照一定的协议对Socket进行读/写操作;4、 关闭Socket.
java在包java.net中提供了两个类Socket和ServerSocket,分别用来表示双向连接的客户端和服务端。这是两个封装得非常好的类,使用很方便。
Socket client = new Socket(“127.0.0.1”, 80);//第一个参数为主机ip,第二个参数为端口号
ServerSocket server = new ServerSocket(80);
注意,在选择端口时,必须小心。每一个端口提供一种特定的服务,只有给出正确的端口,才 能获得相应的服务。0~1023的端口号为系统所保留,例如http服务的端口号为80,telnet服务的端口号为21,ftp服务的端口号为23, 所以我们在选择端口号时,最好选择一个大于1023的数以防止发生冲突。
在创建socket时如果发生错误,将产生IOException,在程序中必须对之作出处理。所以在创建Socket或ServerSocket是必须捕获或抛出例外。
聊天室编写
就直接上代码了,代码中大部分地方都有注释,要有什么不懂得地方可以在评论的时候询问。
1、客户端代码
ChatClient.java:package com.practice.noyet.chatroom.socket;import android.util.Log;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.io.PrintWriter;import java.net.Socket;/** * Created by noyet on 2015/9/26. */public class ChatClient { public Socket socket; /** 数据写入服务器端 */ public PrintWriter writer; /** 从服务器读取数据 */ public BufferedReader reader; public ChatClient() { createSocket(); } /** * 获取ChatClient实例 * @return ChatClient */ public synchronized ChatClient getChatClient() { return new ChatClient(); } /** * 创建Socket */ public void createSocket() { try { /** 初始化Socket,参数:主机ip(服务器主机ip) 端口号 */ socket = new Socket("192.168.1.142", 1314); /** 初始化写数据流 */ writer = new PrintWriter(socket.getOutputStream()); /** 初始化读数据流 */ reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); } catch (Exception e) { /** 打印错误信息 */ printErrorInfo("ChatClient.createSocket", e); e.printStackTrace(); } } /** * 向服务器发送消息 * @param chatMsg 信息内容 */ public void sendChatMsg(String chatMsg) { /** 将数据写入服务器端 */ writer.println(chatMsg); /** 刷新输出流(写入服务器),使Server马上收到该字符串 */ writer.flush(); } /** * 获取从服务器返回的数据 * @return String */ public String getChatMsg() { try { String str = reader.readLine(); return str; } catch (Exception e) { /** 打印错误信息 */ printErrorInfo("ChatClient.getChatMsg", e); e.printStackTrace(); } return null; } /** * 关闭IO流 */ public void destroySocket() { try { if (reader != null) { reader.close(); } if (writer != null) { writer.close(); } if (socket != null) { socket.close(); } } catch (IOException e) { /** 打印错误信息 */ printErrorInfo("ChatClient.destroySocket", e); e.printStackTrace(); } } /** * 打印错误信息 * @param tag 产生错误信息的方法 * @param info 错误信息 */ private void printErrorInfo(String tag, Exception info) { Log.d(tag + " Error", "Error: " + info); }}
2、app主Activity代码
MainActivity.java:package com.practice.noyet.chatroom;import android.os.AsyncTask;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.ArrayAdapter;import android.widget.ListView;import android.widget.TextView;import com.practice.noyet.chatroom.socket.ChatClient;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity implements View.OnClickListener { /** 带清除标志的客户端A的数据输入框 */ private ClearEditText aContent; /** 客户端A的数据提交按钮 */ private TextView aSend; /** 带清除标志的客户端B的数据输入框 */ private ClearEditText bContent; /** 客户端B的数据提交按钮 */ private TextView bSend; /** 显示从服务器端返回的数据 */ private ListView mListView; /** 用户A的客户端 */ private ChatClient clientA; /** 用户B的客户端 */ private ChatClient clientB; private ArrayAdapter<String> mAdapter; private List<String> list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); initEvent(); } /** * 初始化控件 */ private void initView() { new MyAsyncTask().execute(1); aContent = (ClearEditText) findViewById(R.id.a_client_cet); aSend = (TextView) findViewById(R.id.a_client_send); bContent = (ClearEditText) findViewById(R.id.b_client_cet); bSend = (TextView) findViewById(R.id.b_client_send); mListView = (ListView) findViewById(R.id.chat_content_listview); } /** * 初始化数据 */ private void initData() { list = new ArrayList<>(); mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, list); mListView.setAdapter(mAdapter); } /** * 初始化监听事件 */ private void initEvent() { aSend.setOnClickListener(this); bSend.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.a_client_send: //向服务器发送消息 clientA.sendChatMsg(aContent.getText().toString()); //获取从服务器返回的数据 new MyAsyncTask().execute(2); break; case R.id.b_client_send: //向服务器发送消息 clientB.sendChatMsg(bContent.getText().toString()); //获取从服务器返回的数据 new MyAsyncTask().execute(3); break; } } @Override protected void onDestroy() { super.onDestroy(); clientA.destroySocket(); clientB.destroySocket(); } private class MyAsyncTask extends AsyncTask<Integer, Integer, Integer> { @Override protected Integer doInBackground(Integer... integers) { if (integers[0] == 1) { clientA = new ChatClient(); clientB = new ChatClient(); } else if (integers[0] == 2) { list.add("Client A: " + clientA.getChatMsg()); } else if (integers[0] == 3) { list.add("Client B: " + clientA.getChatMsg()); } return integers[0]; } @Override protected void onPostExecute(Integer integer) { super.onPostExecute(integer); if (integer == 2 || integer == 3) { mAdapter.notifyDataSetChanged(); } } }}
3、拥有清除按钮的自定义EditText代码
ClearEditText.java:package com.practice.noyet.chatroom;import android.content.Context;import android.graphics.drawable.Drawable;import android.text.Editable;import android.text.TextWatcher;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.View.OnFocusChangeListener;import android.view.animation.Animation;import android.view.animation.CycleInterpolator;import android.view.animation.TranslateAnimation;import android.widget.EditText;public class ClearEditText extends EditText implements OnFocusChangeListener, TextWatcher { /** * 删除按钮的引用 */ private Drawable mClearDrawable; public ClearEditText(Context context) { this(context, null); } public ClearEditText(Context context, AttributeSet attrs) { /** 这里构造方法也很重要,不加这个很多属性不能再XML里面定义 */ this(context, attrs, android.R.attr.editTextStyle); } public ClearEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { /** 获取EditText的DrawableRight,假如没有设置我们就使用默认的图片 */ mClearDrawable = getCompoundDrawables()[2]; if (mClearDrawable == null) { mClearDrawable = getResources() .getDrawable(R.drawable.del); } mClearDrawable.setBounds(0, 0, mClearDrawable.getIntrinsicWidth(), mClearDrawable.getIntrinsicHeight()); setClearIconVisible(false); setOnFocusChangeListener(this); addTextChangedListener(this); } /** * 因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件 * 当我们按下的位置 在 EditText的宽度 - 图标到控件右边的间距 - 图标的宽度 和 * EditText的宽度 - 图标到控件右边的间距之间我们就算点击了图标,竖直方向没有考虑 */ @Override public boolean onTouchEvent(MotionEvent event) { if (getCompoundDrawables()[2] != null) { if (event.getAction() == MotionEvent.ACTION_UP) { boolean touchable = event.getX() > (getWidth() - getPaddingRight() - mClearDrawable.getIntrinsicWidth()) && (event.getX() < ((getWidth() - getPaddingRight()))); if (touchable) { this.setText(""); /** 晃动动画 */ setShakeAnimation(); } } } return super.onTouchEvent(event); } /** * 当ClearEditText焦点发生变化的时候,判断里面字符串长度设置清除图标的显示与隐藏 */ @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { setClearIconVisible(getText().length() > 0); } else { setClearIconVisible(false); } } /** * 设置清除图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去 * @param visible 删除按钮是否见 */ protected void setClearIconVisible(boolean visible) { Drawable right = visible ? mClearDrawable : null; setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], right, getCompoundDrawables()[3]); } /** * 当输入框里面内容发生变化的时候回调的方法 */ @Override public void onTextChanged(CharSequence s, int start, int count, int after) { setClearIconVisible(s.length() > 0); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } /** * 设置晃动动画 */ public void setShakeAnimation() { this.setAnimation(shakeAnimation(5)); } /** * 晃动动画 * @param counts 1秒钟晃动多少下 * @return Animation */ public static Animation shakeAnimation(int counts) { Animation translateAnimation = new TranslateAnimation(0, 10, 0, 0); translateAnimation.setInterpolator(new CycleInterpolator(counts)); translateAnimation.setDuration(1000); return translateAnimation; }}
4、界面布局
activity_main.xml:<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <LinearLayout android:id="@+id/aclient" android:gravity="center_vertical" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="客户端A" android:textSize="16sp" android:layout_weight="2" android:layout_width="0dp" android:layout_height="wrap_content" /> <com.practice.noyet.chatroom.ClearEditText android:id="@+id/a_client_cet" android:paddingLeft="15dp" android:textSize="16sp" android:singleLine="true" android:textColorHint="@android:color/darker_gray" android:hint="请输入要传入服务器的字符串" android:background="@drawable/input" android:layout_weight="6" android:layout_width="0dp" android:layout_height="30dp" /> <TextView android:id="@+id/a_client_send" android:paddingLeft="5dp" android:text="发送" android:textSize="16sp" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:id="@+id/bclient" android:layout_marginTop="10dp" android:layout_below="@id/aclient" android:gravity="center_vertical" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="客户端B" android:textSize="16sp" android:layout_weight="2" android:layout_width="0dp" android:layout_height="wrap_content" /> <com.practice.noyet.chatroom.ClearEditText android:id="@+id/b_client_cet" android:paddingLeft="15dp" android:textSize="16sp" android:singleLine="true" android:textColorHint="@android:color/darker_gray" android:hint="请输入要传入服务器的字符串" android:background="@drawable/input" android:layout_weight="6" android:layout_width="0dp" android:layout_height="30dp" /> <TextView android:id="@+id/b_client_send" android:paddingLeft="5dp" android:text="发送" android:textSize="16sp" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:layout_marginTop="15dp" android:layout_below="@id/bclient" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:background="@drawable/content" android:id="@+id/chat_content_listview" android:dividerHeight="1dp" android:divider="@android:color/white" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout></RelativeLayout>
5、服务器端代码
ChatServer.java:package com.noyet.practice;import java.net.ServerSocket;import java.net.Socket;import java.util.ArrayList;import java.util.List;public class ChatServer { /** socket端口号 */ private final static int SOCKET_PORT = 1314; /** socket列表,接收从各个客户端传入的数据 */ public static List<Socket> list; public static void main(String[] args) { try { /** 给list分配空间 */ list = new ArrayList<Socket>(); /** 创建ServerSocket,用来监听客户端socket的连接请求 */ ServerSocket serverSocket = new ServerSocket(SOCKET_PORT); while (true) { /** 每当接收到客户端的Socket请求,服务器端也相应的创建一个Socket,并存入list中,用来读取数据 */ Socket socket = serverSocket.accept(); list.add(socket); /** 每连接一个客户端,启动一个单独的ServerThread线程为该客户端服务,并传入对应的Socket */ new Thread(new ServerThread(socket)).start(); } } catch (Exception e) { /** 打印错误信息 */ ServerThread.printErrorInfo("ChatServer.main", e); e.printStackTrace(); } }}ServerThread.java:package com.noyet.practice;import java.io.BufferedReader;import java.io.InputStreamReader;import java.io.PrintStream;import java.net.Socket;public class ServerThread implements Runnable { /** 定义当前线程所处理的Socket */ private Socket socket; /** 该线程所处理的Socket对应的输入流,从客服端读取数据 */ private BufferedReader reader; /** 该线程所处理的Socket对应的输出流 */ private PrintStream printStream; public ServerThread(Socket socket) { try { this.socket = socket; /** 初始化读数据流 */ reader = new BufferedReader(new InputStreamReader(this.socket.getInputStream())); } catch (Exception e) { /** 打印错误信息 */ printErrorInfo("ServerThread.ServerThread", e); e.printStackTrace(); } } public void run() { // TODO Auto-generated method stub try { String line; /** 采用循环不断地从Socket中读取客户端发送过来的数据 */ while ((line = reader.readLine()) != null && !line.equals("")) { /** 将读到的数据发送给每个客户端 */ for (Socket socket : ChatServer.list) { /** 初始化写数据流 */ printStream = new PrintStream(socket.getOutputStream()); /** 写入数据 */ printStream.println(line); } } } catch (Exception e) { /** 打印错误信息 */ printErrorInfo("ServerThread.run", e); e.printStackTrace(); } finally { if (printStream != null) { printStream.close(); } } } /** * 打印错误信息 * @param tag 产生错误信息的方法 * @param info 错误信息 */ public static void printErrorInfo(String tag, Exception info) { System.out.println(tag + " Error" + info); }}
6、效果图
- socket详解及简易聊天室编写
- 异步Socket编写的一个简易的聊天室
- Socket编程(简易聊天室客户端/服务器编写、CocoaAsyncSocket)
- socket简易聊天室
- java socket简易聊天室
- Socket编写点对点聊天室
- 安卓 socket简易聊天室
- 使用Socket构建简易聊天室
- Java简易聊天室程序socket
- Socket编程实现简易聊天室
- Socket编写一个简易的聊天室(相关知识点的总结)
- C#简易版Socket聊天室 附源码
- Socket套接字做简易聊天室
- 基于Socket的局域网简易聊天室
- java菜鸟 socket简易局域网聊天室
- php基于websocket搭建简易聊天室(socket)
- 基于Linux Socket的聊天室 编写
- 学习Socket编写的聊天室小程序
- 请关闭你的“等死模式”
- RDF-资源描述框架
- 在Myeclipse中移除项目对Hibernate的支持
- poj 1845
- Eclipse创建Maven Web工程
- socket详解及简易聊天室编写
- 串
- ios中git的那些事
- Servlet分页技术
- Listview中一些小的注意问题
- c#读取指定路径的配置文件
- OCiOS开发:地图与定位
- replaced element ,input,select
- test8.5