一個服務器連多個客戶端,支持服務器往客戶端推送消息,也支持客戶端往服務器發送消息。先看效果:
源碼下載:https://download.csdn.net/download/a497785609/12263264
服務器端代碼:(僅列出主要代碼,完整代碼請下載資源源碼)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Server
{
public partial class Server : Form
{
private static Socket _server;
private static readonly byte[] Buffer = new byte[1024];
private readonly Dictionary<string, Socket> _sockets = new Dictionary<string, Socket>();
private readonly Dictionary<string, Thread> _threads = new Dictionary<string, Thread>();
public Server()
{
InitializeComponent();
}
public void UpdateUI(Action action)
{
if (InvokeRequired)
Invoke(action);
else
action();
}
public void Log(string msg)
{
UpdateUI(() =>
{
txtLog.Select(txtLog.TextLength, 0);
txtLog.ScrollToCaret();
txtLog.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + msg + Environment.NewLine);
});
}
private void btnStart_Click(object sender, EventArgs e)
{
var ip = IPAddress.Parse(txtServer.Text.Trim());
_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_server.Bind(new IPEndPoint(ip, Convert.ToInt32(txtPort.Text.Trim())));
_server.Listen(10); //設定最多10個排隊連接請求
Log("服務器已開始偵聽..." + _server.LocalEndPoint);
var thread = new Thread(ListenClient);
thread.Start();
}
private void ListenClient()
{
while (true)
{
var socket = _server.Accept();
Log(string.Format("客戶端 [{0}] 連接成功!", socket.RemoteEndPoint));
var thread = new Thread(ReceiveMessage);
thread.Start(socket);
if (!_sockets.ContainsKey(socket.RemoteEndPoint.ToString()))
{
_sockets.Add(socket.RemoteEndPoint.ToString(), socket);
ShowClient();
}
if (!_threads.ContainsKey(socket.RemoteEndPoint.ToString()))
{
_threads.Add(socket.RemoteEndPoint.ToString(), thread);
}
}
}
private void ReceiveMessage(object clientSocket)
{
var socket = (Socket)clientSocket;
while (true)
try
{
var count = socket.Receive(Buffer);
var txt = Encoding.UTF8.GetString(Buffer, 0, count);
if (txt == "off" || txt == "")
{
if (_sockets.ContainsKey(socket.RemoteEndPoint.ToString()))
{
_sockets.Remove(socket.RemoteEndPoint.ToString());
ShowClient();
Log(string.Format("接收客戶端 [{0}] 已斷開", socket.RemoteEndPoint));
}
if (_threads.ContainsKey(socket.RemoteEndPoint.ToString()))
{
_threads[socket.RemoteEndPoint.ToString()].Abort();
}
}
else
{
Log(string.Format("接收客戶端 [{0}] 消息 [{1}]", socket.RemoteEndPoint, txt));
}
}
catch (Exception ex)
{
if (socket != null && socket.Connected)
{
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
break;
}
}
private void btnSend_Click(object sender, EventArgs e)
{
var msg = txtMsg.Text.Trim();
try
{
//向所有連上的socket發送消息
foreach (var pair in _sockets)
if (pair.Value.Connected)
{
pair.Value.Send(Encoding.UTF8.GetBytes(msg));
Log("向客戶端發送消息:" + msg);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Server_FormClosing(object sender, FormClosingEventArgs e)
{
foreach (var pair in _sockets)
if (pair.Value != null)
{
if (pair.Value.Connected && pair.Value.Available > 0)
{
pair.Value.Send(Encoding.UTF8.GetBytes("off"));
pair.Value.Shutdown(SocketShutdown.Both);
pair.Value.Disconnect(false);
}
pair.Value.Close();
pair.Value.Dispose();
}
var tt = Process.GetProcessById(Process.GetCurrentProcess().Id);
tt.Kill();
}
public void ShowClient()
{
UpdateUI(() =>
{
listBox.Items.Clear();
foreach (var pair in _sockets) listBox.Items.Add(pair.Key);
});
}
private void btnClear_Click(object sender, EventArgs e)
{
txtLog.Text = "";
}
}
}
客戶端端代碼:(僅列出主要代碼,完整代碼請下載資源源碼)
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace Client
{
public partial class Client : Form
{
private static readonly byte[] Buffer = new byte[1024];
private bool _isContinue = true;
private Thread _thread;
private Socket socket;
public Client()
{
InitializeComponent();
}
public void UpdateUI(Action action)
{
if (InvokeRequired)
Invoke(action);
else
action();
}
public void Log(string msg)
{
UpdateUI(() =>
{
txtLog.Select(txtLog.TextLength, 0);
txtLog.ScrollToCaret();
txtLog.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + msg + Environment.NewLine);
});
}
private void btnStart_Click(object sender, EventArgs e)
{
if (socket != null && socket.IsBound)
{
Log("已連接,請勿重複連接");
return;
}
var ip = IPAddress.Parse(txtServer.Text.Trim());
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
socket.Connect(new IPEndPoint(ip, Convert.ToInt32(txtPort.Text.Trim())));
Log("連接服務器成功");
socket.Send(Encoding.UTF8.GetBytes(txtName.Text.Trim() + "|Login"));
//新開線程,偵聽服務器端返回信息
_thread = new Thread(ReceiveListener);
_thread.Start();
}
catch
{
Log("連接服務器失敗,請檢查服務器是否已啓動!");
}
}
private void ReceiveListener()
{
try
{
while (_isContinue)
if (socket != null && socket.Connected && socket.Blocking)
{
var count = socket.Receive(Buffer);
var txt = Encoding.UTF8.GetString(Buffer, 0, count);
if (txt == "off")
{
_isContinue = false;
Log("服務器已關閉");
}
else
{
Log(string.Format("接收到服務器 [{0}] 消息 [{1}]", socket.RemoteEndPoint, txt));
}
}
else
{
_isContinue = false;
Log("服務器已關閉");
_thread.Abort();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private void btnSend_Click(object sender, EventArgs e)
{
try
{
var msg = txtMsg.Text.Trim();
if (socket == null)
{
Log("抱歉,請先連接服務器!");
return;
}
if (socket.IsBound && socket.Connected)
{
socket.Send(Encoding.UTF8.GetBytes(txtName.Text.Trim() + "|" + msg));
Log("向服務器發送消息:" + msg);
}
else
{
Log("抱歉,Socket已斷開!");
return;
}
}
catch (Exception ex)
{
Log(ex.Message);
}
}
private void Client_FormClosing(object sender, FormClosingEventArgs e)
{
if (socket != null)
{
if (socket.Connected)
{
socket.Send(Encoding.UTF8.GetBytes("off"));
socket.Shutdown(SocketShutdown.Both);
}
//socket.Disconnect(false);
socket.Close();
socket.Dispose();
}
var tt = Process.GetProcessById(Process.GetCurrentProcess().Id);
tt.Kill();
}
private void btnClear_Click(object sender, EventArgs e)
{
txtLog.Text = "";
}
}
}
源碼下載:https://download.csdn.net/download/a497785609/12263264
===============================補充====================================
配合Android手機客戶端,可以實現手機與服務器間的socket連接,可以發揮的功能就很多了,比如:
1.手機發送命令,由服務器中轉,遠程控制電腦。
2.服務器端往手機推送消息等。
Android客戶端代碼:(僅供參考)
package com.rc114.ui;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Html;
import android.text.InputType;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.example.rc114.R;
import com.rc114.updater.Notifyer;
import org.w3c.dom.Text;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MySocket extends Activity {
Socket socket;
EditText txtMsg;
EditText txtLog;
public final int NEWMESSAGE = 1;
public final int Exception = 2;
public final int Receive = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//隱藏標題欄
setContentView(R.layout.layout_socket);
txtMsg = findViewById(R.id.txtMsg);
txtLog = findViewById(R.id.txtLog);
//連接測試
Button btnConn = findViewById(R.id.btnConn);
btnConn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
if (socket != null && !socket.isClosed()) {
Message message = new Message();
message.what = NEWMESSAGE;
message.obj = "已連接,請勿重複連接";
handler.sendMessage(message);
} else {
String host = "120.78.204.206";
int port = 2020;
socket = new Socket(host, port);
if (socket.isConnected()) {
Message message = new Message();
message.what = NEWMESSAGE;
message.obj = "連接成功";
handler.sendMessage(message);
Runnable handler = new LinstenHandler(socket);
Thread thread = new Thread(handler);
thread.start();
}
}
} catch (IOException e) {
Message message = new Message();
message.what = Exception;
message.obj = e.getMessage();
handler.sendMessage(message);
e.printStackTrace();
}
}
}).start();
}
});
//發送信息
Button btnSend = findViewById(R.id.btnSend);
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
public void run() {
try {
String txt = txtMsg.getText().toString();
if (socket.isConnected()) {
socket.getOutputStream().write(txt.getBytes("UTF-8"));
Message message = new Message();
message.what = NEWMESSAGE;
message.obj = "發送:" + txt;
handler.sendMessage(message);
}
} catch (IOException e) {
Message message = new Message();
message.what = Exception;
message.obj = e.getMessage();
handler.sendMessage(message);
e.printStackTrace();
}
}
}.start();
}
});
//清除記錄
Button btnClear = findViewById(R.id.btnClear);
btnClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
txtLog.setText("");
}
});
//斷開連接
Button btnClose = findViewById(R.id.btnClose);
btnClose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
public void run() {
try {
if (socket != null) {
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
}
Message message = new Message();
message.what = NEWMESSAGE;
message.obj = "斷開連接";
handler.sendMessage(message);
} catch (IOException e) {
Message message = new Message();
message.what = Exception;
message.obj = e.getMessage();
handler.sendMessage(message);
e.printStackTrace();
}
}
}.start();
}
});
}
class LinstenHandler implements Runnable {
private Socket socket;
public LinstenHandler(Socket i) {
socket = i;
}
public void run() {
try {
while (true)
if (socket != null && socket.isConnected() && socket.isBound()) {
// 讀取數據
InputStream inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len;
while ((len = inputStream.read(bytes)) != -1) {
String txt = (new String(bytes, 0, len, "UTF-8"));
Message message = new Message();
message.what = Receive;
message.obj = "接收:" + txt;
handler.sendMessage(message);
}
}
} catch (IOException e) {
Message message = new Message();
message.what = Exception;
message.obj = e.getMessage();
handler.sendMessage(message);
e.printStackTrace();
}
}
}
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case Exception:
case NEWMESSAGE: {
String s = (txtLog.getText() + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()) + (String) msg.obj + "\n");
txtLog.setText(s);
txtLog.setSelection(s.length(), s.length());
}
break;
case Receive: {
String s = (txtLog.getText() + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ").format(new Date()) + (String) msg.obj + "\n");
txtLog.setText(s);
txtLog.setSelection(s.length(), s.length());
new Notifyer().notifiy("收到通知", (String) msg.obj, null, true);
}
break;
default:
break;
}
}
};
}
對應視圖:layout_socket.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Socket測試"
android:textColor="@android:color/black"
android:textSize="16dp"
android:textStyle="bold" />
<EditText
android:id="@+id/txtMsg"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_margin="3dp"
android:background="@drawable/edit_text"
android:gravity="top"
android:padding="3dp"
android:singleLine="false"
android:text="測試信息"
android:textSize="12dp" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnConn"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginRight="5dp"
android:background="@drawable/button_default"
android:text="連接"
android:textColor="#ffffff"
android:textSize="12dp" />
<Button
android:id="@+id/btnSend"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginRight="5dp"
android:background="@drawable/button_default"
android:text="發送"
android:textColor="#ffffff"
android:textSize="12dp" />
<Button
android:id="@+id/btnClose"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginRight="5dp"
android:background="@drawable/button_default"
android:text="斷開"
android:textColor="#ffffff"
android:textSize="12dp" />
<Button
android:id="@+id/btnClear"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:layout_marginRight="5dp"
android:background="@drawable/button_default"
android:text="清除"
android:textColor="#ffffff"
android:textSize="12dp" />
</LinearLayout>
<EditText
android:id="@+id/txtLog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:background="@drawable/edit_text"
android:gravity="top"
android:singleLine="false"
android:layout_weight="1"
android:textSize="12dp" />
</LinearLayout>
效果圖:
源碼下載:https://download.csdn.net/download/a497785609/12263264 (僅Windows程序,不含Android客戶端)