今天小編給大家分享一下怎么用C++ SOCKET多線程實現聊天小程序的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
什么是網絡協議?
計算機網絡中,各個實體之間的數據交換必須遵守事先約定好的規則,這些規則就稱為協議。
網絡協議的組成要素有:
1.語法,數據與控制信息的結構或格式
2.語義:需要發出何種控制信息,完成哪些動作以及做出何種響應
3.時序:事件實現順序的詳細說明
在一個網絡協議中,通信的實體的相同層次的結構必須執行相同的協議,這是協議的對等性原則。
TCP/IP體系結構與SOCKET

關于TCP/IP體系結構的詳細內容本文不做論述,如果你沒有這方面的知識想要快速理解這個東西,可以把網絡通信類比成兩個人之間寫信。你的信件就是通信過程中要傳遞的消息或者數據,而網絡協議對你的“信件”進行了包裝,比如給你貼了郵票、包了信封、投進了郵箱,然后你的“信件”就能通過郵局送到收信人那里。
SOCKET(套接字)是TCP/IP網絡操作系統為網絡程序開發提供的典型網絡編程界面,進程通過SOCKET發送消息和接收消息。你可以把SOCKET看作一道“門”,發送消息的進程從“門”把消息推出去;消息被推出之后利用下層的通信設施傳遞到接收進程所在的“門”;然后接收進程再從“門”把消息拉進去。套接字SOCKET又分為數據報套接字和流式套接字,分別使用UDP協議和TCP協議。
SOCKET編程
我們嘗試編寫一個單播聊天室,這個聊天室可以讓多個客戶端與服務器端進行連接,而單播的意思是各個客戶端只能與服務端進行單獨通信,不同客戶端之間無法通信。為了實現這個目標我們還需要用到多線程。整體實現思路如下圖:

話不多說,上代碼。
Server端
#include "stdafx.h"
#include<WinSock2.h>
#include<string.h>
#include<iostream>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
const int PORT = 8000;
#define IP "127.0.0.1"
#define MaxClient 10//最多能接受同時在線的客戶端數量,可以隨意修改
#define MaxBufSize 1024
int num =0;//客戶端數量計數器
#define _CRT_SECURE_NO_WARINGS
//服務線程
DWORD WINAPI SeverThread(LPVOID lpParameter)
{
//新建一個SOCKET用于通信
SOCKET *ClientSocket = (SOCKET*)lpParameter;
int receByt = 0;
char RecvBuf[MaxBufSize];
char SendBuf[MaxBufSize];
char exitBuf[5];
//開始接收
while (1)
{
receByt = recv(*ClientSocket, RecvBuf, sizeof(RecvBuf), 0);
if (receByt > 0)
{
//當客戶端發來的消息是“exit”,就關閉連接
if (strlen(RecvBuf)==4)
{
for (int i = 0; i < 5; i++)
{
exitBuf[i] = RecvBuf[i];
}
int flag = strcmp(exitBuf, "exit");
if (flag==0)//接收到exit消息
{
cout << "client " << *ClientSocket << " exit!" << endl;
num--;
send(*ClientSocket, "Your server has been closed", sizeof(SendBuf), 0);
closesocket(*ClientSocket);
return 0;
}
}
cout << "receive message :" << RecvBuf << " from client:" << *ClientSocket << endl;
}
else
{
//下面說到的客戶端關閉連接是指客戶端掉線了
if (WSAGetLastError() == 10054)//檢測到客戶端關閉連接
{
cout << "client " << *ClientSocket << " exit!" << endl;
closesocket(*ClientSocket);
num--;
return 0;
}
else//接收失敗顯示錯誤信息
{
cout << "failed to receive,Error:" << WSAGetLastError() << endl;
break;
}
}
memset(RecvBuf, 0, 1024);
cout << "input your message to client:" << endl;
scanf_s("%s",SendBuf,MaxBufSize);
int k = 0;
k = send(*ClientSocket, SendBuf, sizeof(SendBuf), 0);
if (k < 0)
{
if (WSAGetLastError()==10054)//檢測到客戶端主動關閉連接
{
cout << "client " << *ClientSocket << " exit!" << endl;
closesocket(*ClientSocket);
num--;
return 0;
}
else//發送失敗顯示錯誤信息
cout << "failed to send, Error:" << WSAGetLastError()<<endl;
}
memset(SendBuf, 0, 1024);
}
if (*ClientSocket != INVALID_SOCKET)
{
closesocket(*ClientSocket);
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
WSAData wsd;
WSAStartup(MAKEWORD(2, 2), &wsd);
SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN ListenAddr;
ListenAddr.sin_family = AF_INET;
ListenAddr.sin_addr.S_un.S_addr = INADDR_ANY;//本機ip
ListenAddr.sin_port = htons(PORT);
//綁定監聽端口
int n;
n = bind(ListenSocket, (LPSOCKADDR)&ListenAddr, sizeof(ListenAddr));
if (n == SOCKET_ERROR)
{
cout << "failed to bind!" << endl;
return -1;
}
else
{
cout << "bind success to:" << PORT << endl;
}
//開始監聽
int l = listen(ListenSocket, MaxClient);
if (l == 0)
{
cout << "server ready, wait to requirement..." << endl;
}
else
{
cout << "Error:" << GetLastError() << "listen return" << l << endl;
}
while (1)
{
//循環接收客戶端連接請求并創建服務線程
if(num < MaxClient)
{
SOCKET *ClientSocket=new SOCKET;
HANDLE hThread;
int SockAddrlen = sizeof(sockaddr);
*ClientSocket = accept(ListenSocket, 0, 0);
cout << "client " << *ClientSocket << " has connect to server" << endl;
num++;
hThread = CreateThread(NULL, NULL, &SeverThread, (LPVOID)ClientSocket, 0, NULL);
CloseHandle(hThread);
}
else
{
cout << "Max Client!Please wait for accept..." << endl;
}
}
closesocket(ListenSocket);
WSACleanup();
return 0;
}在這個服務器端,每有一個新的客戶端請求建立連接,服務器都會新開一個線程為一個客戶端提供服務,并在這個線程中新建立一個SOCKET用于與客戶端進行通信,同時服務器也應該能夠在不同階段(接收或發送)檢測客戶端是否已經斷開連接,以便及時釋放資源。
Client端
#include "stdafx.h"
#include<iostream>
#include<cstdio>
#include<string>
#include<Winsock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
const int PORT = 8000;
#define MaxBufSize 1024
#define _CRT_SECURE_NO_WARINGS
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsd;
WSAStartup(MAKEWORD(2, 2), &wsd);
SOCKET SocketClient = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN ClientAddr;
ClientAddr.sin_family = AF_INET;
ClientAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
ClientAddr.sin_port = htons(PORT);
int n = 0;
n = connect(SocketClient, (struct sockaddr*)&ClientAddr, sizeof(ClientAddr));
if (n == SOCKET_ERROR)
{
cout << "failed to connect" << endl;
return -1;
}
cout << "success to connect to Server" << endl;
char info[1024];//數據輸入緩沖區
char SendBuff[MaxBufSize];//發送數據緩沖區
char RecvBuff[MaxBufSize];//接收數據緩沖區
while (1)
{
cout << "input your message:" << endl;
scanf_s("%s",&info,MaxBufSize);
if (info[0] == '\0')
break;
strcpy(SendBuff, info);
memset(info, 0, sizeof(info));
int k = 0;
k = send(SocketClient, SendBuff, sizeof(SendBuff), 0);
memset(SendBuff, 0, sizeof(SendBuff));
if (k < 0)
{
cout << WSAGetLastError() << endl;
cout << "failed to send" << endl;
}
int n = 0;
n = recv(SocketClient, RecvBuff, sizeof(RecvBuff), 0);
if (n>0)
{
cout << "receive message from Server:" << RecvBuff << endl;
memset(RecvBuff, 0, sizeof(RecvBuff));
}
}
closesocket(SocketClient);
WSACleanup();
return 0;
}在本例中,客戶端與服務器建立連接后,必須由客戶端先發送消息才能開啟對話。支持中英文聊天,一次最多發送1024個字節的數據。你要建立多個客戶端的話只需要再新建幾個工程然后把Client的代碼復制進去運行即可?;蛘咧苯佣鄰椭茙讉€編譯生成的exe程序。
以上就是“怎么用C++ SOCKET多線程實現聊天小程序”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。