一、socket通訊開發步驟
1、建立socket
1.1、指定服務端相關資訊系
指定socket版本号 如: winsock_ver = MAKEWORD(2, 2);
addr_len = sizeof(SOCKADDR_IN);
//指定ip位址族
addr_svr.sin_family = AF_INET;
//轉換網絡編碼,指定偵聽本地端口
addr_svr.sin_port = ::htons(SERVER_PORT);
//設定接收任意位址的連接配接
addr_svr.sin_addr.S_un.S_addr = ADDR_ANY;
//設定某一指定記憶體内容為0;
memset(buf_ip, 0, IP_BUF_SIZE)将buf_ip全部指定為0, IP_BUF_SIZE:IP位址長度
1.2、啟動socket
//WSAStartup,即WSA(Windows Sockets Asynchronous,Windows異步套接字)的啟動指令。0表示成功
ret_val = ::WSAStartup(winsock_ver, &wsa_data);
if (ret_val != 0)
{
cerr << "WSA failed to start up!Error code: " << ::WSAGetLastError() << "\n";
system("pause");
exit(1);
}
cout << "WSA started up successfully...\n";
1.3、建立服務端socket
//建立socket socket如果調用成功就傳回新建立的套接字的描述符
sock_svr = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock_svr == INVALID_SOCKET)
{
cerr << "Failed to create server socket!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
cout << "Server socket created successfully...\n";
2、綁定
//bind 綁定要通信的程式,成為伺服器,如果函數執行成功,傳回值為0,否則為SOCKET_ERROR。
//sock_svr就是第二步成功買回的,不,是申請到的接口
//(SOCKADDR*)&addr_svr 是指程序具體位址,就像具體電話号碼一樣,他的類型是一個結構體
//addr_len就是位置的長度
ret_val = ::bind(sock_svr, (SOCKADDR*)&addr_svr, addr_len);
if (ret_val != 0)
{
cerr << "Failed to bind server socket!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
cout << "Server socket bound successfully...\n";
3、監聽端口
ret_val = ::listen(sock_svr, SOMAXCONN);
if (ret_val == SOCKET_ERROR)
{
cerr << "Server socket failed to listen!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
cout << "Server socket started to listen...\n";
//
cout << "Server started successfully..." << endl;
4、等待用戶端,
while (true)
{
//接受用戶端的資訊
sock_clt = ::accept(sock_svr, (SOCKADDR*)&addr_clt, &addr_len);
cout << "ssss " << sock_clt << endl;
if (sock_clt == INVALID_SOCKET)
{
cout << "ssss " << sock_clt <<endl;
cerr << "Failed to accept client!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
5、接收用戶端發送的資訊、發送資訊給用戶端
char buf_msg[MSG_BUF_SIZE]; // 接收資訊
int ret_val = 0; //傳回讀入位元組數
int snd_result = 0; //傳回發送資訊位元組數
do
{
memset(buf_msg, 0, MSG_BUF_SIZE);
//接收資料 若無錯誤,傳回讀入位元組數
ret_val = ::recv(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
if (ret_val > 0)
{
//如果接送的資訊為“exit”,則關閉連接配接
if (strcmp(buf_msg, "exit") == 0)
{
cout << "Client requests to close the connection..." << endl;
break;
}
//列印接收資訊
cout << "Message received: " << buf_msg << endl;
//發送消息給用戶端
char send_to_client[SEND_BUF_SIZE]; //發送給用戶端的資訊
cout << "enter the message that you want to send client: " << flush;
cin.getline(send_to_client, SEND_BUF_SIZE);//控制台輸入需要發送資訊
cout << "enter the message " << send_to_client << endl;
snd_result = ::send(sock_clt, send_to_client, static_cast<int>(strlen(send_to_client)), 0);
//snd_result = ::send(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
//如果發送失敗,即snd_result=-1
if (snd_result == SOCKET_ERROR)
{
cerr << "Failed to send message to client!Error code: " << ::GetLastError() << "\n";
::closesocket(sock_clt);
system("pause");
return 1;
}
}//接受資訊失敗ret_val==0
else if (ret_val == 0)
{
cout << "connection closed..." << endl;
}
else
{
cerr << "Failed to receive message from client!Error code: " << ::GetLastError() << "\n";
::closesocket(sock_clt);
system("pause");
return 1;
}
} while (ret_val > 0);
//
ret_val = ::shutdown(sock_clt, SD_SEND);
if (ret_val == SOCKET_ERROR)
{
cerr << "Failed to shutdown the client socket!Error code: " << ::GetLastError() << "\n";
::closesocket(sock_clt);
system("pause");
return 1;
}
6、建立用戶端socket
6.1、同樣先建立socket
WSADATA wsa_data; //WSADATA變量,包含windows socket執行的資訊
int i_result = 0; //接收傳回值
SOCKET sock_client = INVALID_SOCKET;
addrinfo *result = nullptr, hints;
//初始化winsock動态庫(ws2_32.dll),MAKEWORD(2, 2)用于請求使用winsock2.2版本
i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (i_result != 0) {
cerr << "WSAStartup() function failed: " << i_result << "\n";
system("pause");
return 1;
}
SecureZeroMemory(&hints, sizeof(hints));//其作用是用0來填充一塊記憶體區域
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
//getaddrinfo函數能夠處理名字到位址以及服務到端口這兩種轉換,
//傳回的是一個sockaddr結構的連結清單而不是一個位址清單。
//這些sockaddr結構随後可由套接口函數直接使用
i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
if (i_result != 0) {
cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
WSACleanup();
system("pause");
return 1;
}
//建立套接字
sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock_client == INVALID_SOCKET) {
cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
WSACleanup();
system("pause");
return 1;
}
6.2、連接配接伺服器
//連接配接伺服器
i_result = connect(sock_client, result->ai_addr, result->ai_addrlen);
if (i_result == SOCKET_ERROR) {
cerr << "connect() function failed with error: " << WSAGetLastError() << "\n";
WSACleanup();
system("pause");
return 1;
}
cout << "connect server successfully..." << endl;
//釋放由getaddrinfo傳回的存儲空間,包括addrinfo結構、ai_addr結構和ai_canonname字元串
freeaddrinfo(result);
6.3、發送資訊給服務端
//發送資訊給服務端
char send_buf[SEND_BUF_SIZE];
int recv_result = 0; //接收伺服器傳回的資料的傳回值
//SecureZeroMemory(send_buf, SEND_BUF_SIZE);
do {
cout << "enter the message that you want to send: " << flush;
cin.getline(send_buf, SEND_BUF_SIZE);
i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0);
if (i_result == SOCKET_ERROR) {
cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
closesocket(sock_client);
WSACleanup();
system("pause");
return 1;
}
cout << "Bytes sent: " << i_result << endl;
//接收伺服器傳回的資料
char rec_from_server[SEND_BUF_SIZE];
recv_result = recv(sock_client, rec_from_server, SEND_BUF_SIZE, 0);
//recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0);
if (recv_result > 0) {
cout << "feedback from server: " << rec_from_server << endl;
//cout << "feedback from server: " << send_buf << endl;
}
else if (recv_result == 0) {
cout << "connection closed..." << endl;
}
else {
cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
closesocket(sock_client);
WSACleanup();
system("pause");
return 1;
}
} while (recv_result > 0);
7、關閉socket
i_result = shutdown(sock_client, SD_SEND);
if (i_result == SOCKET_ERROR) {
cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
closesocket(sock_client);
WSACleanup();
system("pause");
return 1;
}
closesocket(sock_client);
WSACleanup();
cout << "socket closed..." << endl;
服務端代碼:
1、socketserver.h
#pragma once
#ifndef SOCKETSERVER_H
#define SOCKETSERVER_H
#include <Winsock2.h>
#include <windows.h>
#pragma comment (lib, "ws2_32.lib")
#define IP_BUF_SIZE 129
class Server
{
public:
Server();
~Server();
Server(const Server &) = delete;
Server & operator=(const Server &) = delete;
void WaitForClient();
private:
WORD winsock_ver; //socket版本
WSADATA wsa_data; //存儲被WSAStartup函數調用後傳回的Windows Sockets資料
SOCKET sock_svr; //服務端socket
SOCKET sock_clt; //用戶端socket
HANDLE h_thread; //建立的線程
SOCKADDR_IN addr_svr; //存放了服務端位址族、端口、ip位址,用來處理通信位址
SOCKADDR_IN addr_clt; //存放了用戶端位址族、端口、ip位址,用來處理通信位址
int ret_val;
int addr_len;
char buf_ip[IP_BUF_SIZE];
};
#endif
2、socketserver.cpp
#include "pch.h"
#include "socketserver.h"
#include <iostream>
#include <WS2tcpip.h>
using namespace std;
#define SERVER_PORT 5000 //端口
#define MSG_BUF_SIZE 1024 //接送資訊最大長度
const int SEND_BUF_SIZE = 1024; //發送資訊最大長度
Server::Server()
{
cout << "Initializing server...\n";
//
winsock_ver = MAKEWORD(2, 2); //指定socket版本2.2
addr_len = sizeof(SOCKADDR_IN);
addr_svr.sin_family = AF_INET; //ip位址族
addr_svr.sin_port = ::htons(SERVER_PORT); //轉換網絡編碼,偵聽本地5000端口
addr_svr.sin_addr.S_un.S_addr = ADDR_ANY; //接收任意位址的連接配接
//memset某一塊記憶體中的内容全部設定為指定的值,
//這個函數通常為新申請的記憶體做初始化工作。
memset(buf_ip, 0, IP_BUF_SIZE);//将buf_ip全部指定為0
//WSAStartup,即WSA(Windows Sockets Asynchronous,Windows異步套接字)的啟動指令。0表示成功
ret_val = ::WSAStartup(winsock_ver, &wsa_data);
if (ret_val != 0)
{
cerr << "WSA failed to start up!Error code: " << ::WSAGetLastError() << "\n";
system("pause");
exit(1);
}
cout << "WSA started up successfully...\n";
//建立socket socket如果調用成功就傳回新建立的套接字的描述符
sock_svr = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock_svr == INVALID_SOCKET)
{
cerr << "Failed to create server socket!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
cout << "Server socket created successfully...\n";
//bind 綁定要通信的程式,成為伺服器,如果函數執行成功,傳回值為0,否則為SOCKET_ERROR。
//sock_svr就是第二步成功買回的,不,是申請到的接口
//(SOCKADDR*)&addr_svr 是指程序具體位址,就像具體電話号碼一樣,他的類型是一個結構體
//addr_len就是位置的長度
ret_val = ::bind(sock_svr, (SOCKADDR*)&addr_svr, addr_len);
if (ret_val != 0)
{
cerr << "Failed to bind server socket!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
cout << "Server socket bound successfully...\n";
//監聽端口
ret_val = ::listen(sock_svr, SOMAXCONN);
if (ret_val == SOCKET_ERROR)
{
cerr << "Server socket failed to listen!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
cout << "Server socket started to listen...\n";
//
cout << "Server started successfully..." << endl;
cout << "ret_val..." << ret_val << endl;
}
Server::~Server()
{
::closesocket(sock_svr);
::closesocket(sock_clt);
::WSACleanup();
cout << "Socket closed..." << endl;
}
DWORD WINAPI CreateClientThread(LPVOID lpParameter);
//等待用戶端
void Server::WaitForClient()
{
while (true)
{
//接受用戶端的資訊
sock_clt = ::accept(sock_svr, (SOCKADDR*)&addr_clt, &addr_len);
cout << "ssss " << sock_clt << endl;
if (sock_clt == INVALID_SOCKET)
{
cout << "ssss " << sock_clt <<endl;
cerr << "Failed to accept client!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup();
system("pause");
exit(1);
}
//::InetNtop(addr_clt.sin_family, &addr_clt, buf_ip, IP_BUF_SIZE);
//cout << "A new client connected...IP address: " << buf_ip << ", port number: " << ::ntohs(addr_clt.sin_port) << endl;
//:CreateThread:新的線程的函數,該函數在主線程的基礎上建立一個新線程。線程終止運作後,
//線程對象仍然在系統中,必須通過CloseHandle函數來關閉該線程對象
h_thread = ::CreateThread(nullptr, 0, CreateClientThread, (LPVOID)sock_clt, 0, nullptr);
if (h_thread == NULL)
{
cerr << "Failed to create a new thread!Error code: " << ::WSAGetLastError() << "\n";
::WSACleanup(); //終止Winsock 2 DLL (Ws2_32.dll) 的使用
system("pause");
exit(1);
}
//關閉線程關閉一個核心對象。其中包括檔案、檔案映射、程序、線程、安全和同步對象等。
//在CreateThread成功之後會傳回一個hThread的handle,且核心對象的計數加1,CloseHandle之後,
//引用計數減1,當變為0時,系統删除核心對象。
::CloseHandle(h_thread);
}
}
//建立用戶端線程
DWORD WINAPI CreateClientThread(LPVOID lpParameter)
{
SOCKET sock_clt = (SOCKET)lpParameter; //用戶端
char buf_msg[MSG_BUF_SIZE]; // 接收資訊
int ret_val = 0; //傳回讀入位元組數
int snd_result = 0; //傳回發送資訊位元組數
do
{
memset(buf_msg, 0, MSG_BUF_SIZE);
//接收資料 若無錯誤,傳回讀入位元組數
ret_val = ::recv(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
if (ret_val > 0)
{
//如果接送的資訊為“exit”,則關閉連接配接
if (strcmp(buf_msg, "exit") == 0)
{
cout << "Client requests to close the connection..." << endl;
break;
}
//列印接收資訊
cout << "Message received: " << buf_msg << endl;
//發送消息給用戶端
char send_to_client[SEND_BUF_SIZE]; //發送給用戶端的資訊
cout << "enter the message that you want to send client: " << flush;
cin.getline(send_to_client, SEND_BUF_SIZE);//控制台輸入需要發送資訊
cout << "enter the message " << send_to_client << endl;
snd_result = ::send(sock_clt, send_to_client, static_cast<int>(strlen(send_to_client)), 0);
//snd_result = ::send(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
//如果發送失敗,即snd_result=-1
if (snd_result == SOCKET_ERROR)
{
cerr << "Failed to send message to client!Error code: " << ::GetLastError() << "\n";
::closesocket(sock_clt);
system("pause");
return 1;
}
}//接受資訊失敗ret_val==0
else if (ret_val == 0)
{
cout << "connection closed..." << endl;
}
else
{
cerr << "Failed to receive message from client!Error code: " << ::GetLastError() << "\n";
::closesocket(sock_clt);
system("pause");
return 1;
}
} while (ret_val > 0);
//
ret_val = ::shutdown(sock_clt, SD_SEND);
if (ret_val == SOCKET_ERROR)
{
cerr << "Failed to shutdown the client socket!Error code: " << ::GetLastError() << "\n";
::closesocket(sock_clt);
system("pause");
return 1;
}
return 0;
}
3、啟動服務端代碼
#include "pch.h"
#include "socketserver.h"
int main()
{
Server svr;
svr.WaitForClient();
system("pause");
return 0;
}
4、用戶端代碼
#include <iostream>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
using std::cin;
using std::cerr;
using std::cout;
using std::endl;
using std::flush;
const char DEFAULT_PORT[] = "5000";
const int SEND_BUF_SIZE = 1024;
//用戶端
int main() {
WSADATA wsa_data; //WSADATA變量,包含windows socket執行的資訊
int i_result = 0; //接收傳回值
SOCKET sock_client = INVALID_SOCKET;
addrinfo *result = nullptr, hints;
//初始化winsock動态庫(ws2_32.dll),MAKEWORD(2, 2)用于請求使用winsock2.2版本
i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
if (i_result != 0) {
cerr << "WSAStartup() function failed: " << i_result << "\n";
system("pause");
return 1;
}
SecureZeroMemory(&hints, sizeof(hints));//其作用是用0來填充一塊記憶體區域
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
//getaddrinfo函數能夠處理名字到位址以及服務到端口這兩種轉換,
//傳回的是一個sockaddr結構的連結清單而不是一個位址清單。
//這些sockaddr結構随後可由套接口函數直接使用
i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
if (i_result != 0) {
cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
WSACleanup();
system("pause");
return 1;
}
//建立套接字
sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock_client == INVALID_SOCKET) {
cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
WSACleanup();
system("pause");
return 1;
}
//連接配接伺服器
i_result = connect(sock_client, result->ai_addr, result->ai_addrlen);
if (i_result == SOCKET_ERROR) {
cerr << "connect() function failed with error: " << WSAGetLastError() << "\n";
WSACleanup();
system("pause");
return 1;
}
cout << "connect server successfully..." << endl;
//釋放由getaddrinfo傳回的存儲空間,包括addrinfo結構、ai_addr結構和ai_canonname字元串
freeaddrinfo(result);
//發送資訊給服務端
char send_buf[SEND_BUF_SIZE];
int recv_result = 0; //接收伺服器傳回的資料的傳回值
//SecureZeroMemory(send_buf, SEND_BUF_SIZE);
do {
cout << "enter the message that you want to send: " << flush;
cin.getline(send_buf, SEND_BUF_SIZE);
i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0);
if (i_result == SOCKET_ERROR) {
cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
closesocket(sock_client);
WSACleanup();
system("pause");
return 1;
}
cout << "Bytes sent: " << i_result << endl;
//接收伺服器傳回的資料
char rec_from_server[SEND_BUF_SIZE];
recv_result = recv(sock_client, rec_from_server, SEND_BUF_SIZE, 0);
//recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0);
if (recv_result > 0) {
cout << "feedback from server: " << rec_from_server << endl;
//cout << "feedback from server: " << send_buf << endl;
}
else if (recv_result == 0) {
cout << "connection closed..." << endl;
}
else {
cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
closesocket(sock_client);
WSACleanup();
system("pause");
return 1;
}
} while (recv_result > 0);
//
i_result = shutdown(sock_client, SD_SEND);
if (i_result == SOCKET_ERROR) {
cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
closesocket(sock_client);
WSACleanup();
system("pause");
return 1;
}
closesocket(sock_client);
WSACleanup();
cout << "socket closed..." << endl;
system("pause");
return 0;
}