Socket
概念
socket俗称“套接字”,是网络通信中的概念,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远程主机的IP地址,远程进程的协议端口。它分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层的TCP和UDP协议。
作用
能够唯一标识网络中的进程,进行进程间通信。比如:两个进程(程序)进行通讯最基本的一个前提能够有唯一的标识,在本地进程通讯中我们可以使用PID来唯一标识一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,但是我们知道ip地址可以标识主机,而TCP层协议和端口号可以唯一标识主机的一个进程,这样我们可以利用ip地址,协议,端口号唯一标识网络中的一个进程。
用法
客户端
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;
private Button mSendButton;
private TextView mMessageTextView;
private EditText mMessageEditText;
private PrintWriter mPrintWriter;
private Socket mClientSocket;
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MESSAGE_RECEIVE_NEW_MSG:
mMessageTextView.setText(mMessageTextView.getText()+(String)msg.obj);
break;
case MESSAGE_SOCKET_CONNECTED:
mSendButton.setEnabled(true);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMessageTextView = (TextView) findViewById(R.id.msg_container);
mSendButton = (Button) findViewById(R.id.send);
mSendButton.setOnClickListener(this);
mMessageEditText = (EditText) findViewById(R.id.msg);
Intent intent = new Intent(this,TcpServerService.class);
startService(intent);
new Thread(){
@Override
public void run() {
super.run();
//开启一个子线程,连接服务端
connectTcpServer();
}
}.start();
}
//格式化时间
private String formatDataTime(long time){
return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}
private void connectTcpServer() {
Socket socket=null;
while(socket==null){
try {
socket=new Socket("localhost",8688);
mClientSocket=socket;
mPrintWriter=new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())),true);
//当连接服务器端成功时,发送按钮是可以点的
mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
System.out.println("connect server success");
} catch (IOException e) {
SystemClock.sleep(1000);
System.out.println("connect faild.....");
}
}
try {
//接收服务器端的消息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!MainActivity.this.isFinishing()){
String msg = br.readLine();
System.out.println("receive :"+msg);
if(msg !=null){
String time=formatDataTime(System.currentTimeMillis());
final String showedMsg ="server " + time + ":" + msg
+ "\n";
//服务端发来消息后,发到主线程,显示界面上。
mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG,showedMsg).sendToTarget();
}
}
System.out.println("quit...");
MyUtils.close(mPrintWriter);
MyUtils.close(br);
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
if (v == mSendButton) {
final String msg = mMessageEditText.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
mPrintWriter.println(msg);
mMessageEditText.setText("");
String time = formatDataTime(System.currentTimeMillis());
final String showedMsg = "self " + time + ":" + msg + "\n";
mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
}
}
}
@Override
protected void onDestroy() {
//退出界面时,关闭socket
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
服务端
public class TcpServerService extends Service {
private boolean mIsServiceDestoryed = false;
private String[] mDefinedMessages = new String[] {
"你好,亲",
"请问你叫什么名字呀?",
"今天北京天气怎么样啊。。。",
"你知道吗?我可是可以和多个人同时聊天的哦",
"给你讲个笑话吧:据说爱笑的人运气不会太差,不知道真假。"
};
public TcpServerService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Override
public void onDestroy() {
mIsServiceDestoryed=true;
super.onDestroy();
}
private class TcpServer implements Runnable{
@Override
public void run() {
ServerSocket serverSocket=null;
try {
serverSocket=new ServerSocket(8688);
}catch (Exception e){
System.out.println("建立tcp 服务端连接失败,端口:8688");
e.printStackTrace();
}
while(!mIsServiceDestoryed){
try {
//接受客户端请求
final Socket client = serverSocket.accept();
System.out.println("accept");
new Thread(){
@Override
public void run() {
super.run();
try {
responseClient(client);
}catch (IOException e){
e.printStackTrace();
}
};
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException{
//用于接收客户端消息
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
//用于向客户端发送消息
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
out.println("欢迎来到聊天室。。。");
while(!mIsServiceDestoryed){
String str = in.readLine();
System.out.println("消息来自客户端:"+str);
if(str==null){
break;
}
int i = new Random().nextInt(mDefinedMessages.length);
String msg = mDefinedMessages[i];
out.println(msg);
System.out.println("send"+msg);
}
System.out.println("client quit.");
//关闭流
MyUtils.close(out);
MyUtils.close(in);
client.close();
}
}
MyUtils
public class MyUtils {
public static void close(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void executeInThread(Runnable runnable) {
new Thread(runnable).start();
}
}
运行效果
应用场景
socket在Android开发中不是很常用,主要是在面试的时候,常常会碰到面试题让你现场手搓一个Socket,所以下面的代码纯java的,供你面试时参考
客户端:
public class Client {
public static void main(String[] args) throws Exception, IOException {
Socket socket = new Socket("172.60.20.168",30000);
//如果能够执行到这里 说明建立成功了
//这个输入流读取客户端发送的内容
BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream(),"utf8"));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()),true);
//服务端的问候语
String string = br.readLine();
System.out.println(string);
Scanner scanner = new Scanner(System.in);
while(true)
{
String request=scanner.nextLine();
pw.println(request);
//等待服务器的相应
String response=br.readLine();
System.out.println(response);
if(response.equals("bye")){
break;
}
}
br.close();
pw.close();
scanner.close();
socket.close();
}
}
服务端:
public class Server {
ServerSocket serverSocket;
List<Socket> sockets=new ArrayList<Socket>();
private void start()
{
//具体业务逻辑
try {
//端口号 0--65535一般用10000-65535
serverSocket=new ServerSocket(30000);
System.out.println("服务器已经启动。。。。");
//等待作为客户端socket进行连接
while(true){
Socket socket = serverSocket.accept();
sockets.add(socket);
new MyThread(socket).start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//服务端要用一个单独的线程 为客户端服务
private class MyThread extends Thread{
Socket socket;
public MyThread(Socket socket) {
super();
this.socket = socket;
}
public void run() {
// TODO Auto-generated method stub
super.run();
//服务端的输入流接受客户端发来的信息
try {
//连接已经建立起来 服务端利用上面accept方法与客户端进行通信
//采用IO流的形式进行通信
BufferedReader br = new BufferedReader(
new InputStreamReader(socket.getInputStream(),
"utf8"));
while(true)
{
String request = br.readLine();
//把客户端写入的话转发给所有socket
for(Socket s:sockets){
PrintWriter pw=new PrintWriter(new OutputStreamWriter(
s.getOutputStream(), "utf8"),true);
pw.println(request);
}
}
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Server server = new Server();
server.start();
}
}
PS: