TCP能建立服务器与客户端的稳定连接。
服务器端创建TCP连接的流程:
1、初始化套接字环境
2、创建监听套接字
3、将监听套接字绑定到指定的服务端口上(服务器端的IP不指定就是绑定所有IP)
4、监听套接字在服务端口上持续监听发起连接的客户端
5、接收客户端的连接请求,同时产生一个稳定连接的套接字,服务器通过这个套接字与客户端稳定连接
监听套接字就是跑龙套的角色,它就像看家护院的狗,门外有人敲门,它就会叫唤。
主人立马就知道有客人来了。把客人请进来,进正屋上茶慢慢聊。狗不会跟人沟通。哈哈。
在服务器端,必须指定服务端口。至于服务器的地址可以指定为一个特定的IP也可以指定为ADDR_ANY即服务器的
任意IP。在客户端必须要知道服务器的IP和服务端口,如果服务器指定多个IP,只要知道服务器的任意一个IP再
加上服务端口,即可与服务器建立TCP连接。客户端本地的IP和端口可以指定,不指定也没毛病。绑不绑定无所谓的。
因为,客户端发起连接被服务器监听到的时候,就知道了该客户端的IP和端口。
// server
# define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <process.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
// 服务器端可以不指定IP但必须指定服务端口
enum { LOCAL_PORT = 33921 };
const char* strIP = nullptr;
const int BUF_MAX_LEN = 256;
void PrintErrorSerial()
{
cout << WSAGetLastError() << endl;
}
void RecvMsgProc(void* pParam)
{
Sleep(100);
SOCKET* pSock = (SOCKET*)pParam;
sockaddr_in sa;
int nLen = sizeof(sa);
char buf[BUF_MAX_LEN];
while (true)
{
int nCharCounts = recv(*pSock, buf, BUF_MAX_LEN, 0);
if (nCharCounts <= 0 || nCharCounts >= BUF_MAX_LEN)
break;
buf[nCharCounts] = '\0';
if (!getpeername(*pSock, (sockaddr*)&sa, &nLen))
cout << inet_ntoa(sa.sin_addr) << " " << htons(sa.sin_port) << " say: " << buf << endl;
}
delete pParam;
}
int main()
{
WSADATA wd;
if (WSAStartup(MAKEWORD(5, 12), &wd))
{
PrintErrorSerial();
return -1;
}
else cout << "服务器套接字环境初始化成功..." << endl;
SOCKET listenSock = socket(AF_INET, SOCK_STREAM, 0);
if (listenSock == INVALID_SOCKET)
{
cout << WSAGetLastError() << endl;
return -1;
}
else cout << "创建监听套接字成功..." << endl;
sockaddr_in sa{ AF_INET, htons(LOCAL_PORT) };
strIP == nullptr ? sa.sin_addr.S_un.S_addr = htonl(ADDR_ANY) :
sa.sin_addr.S_un.S_addr = inet_addr(strIP);
int nLen = sizeof(sa);
if (bind(listenSock, (const sockaddr*)&sa, sizeof(sa)))
{
cout << WSAGetLastError() << endl;
return -1;
}
else cout << "服务器绑定端口成功..." << endl;
listen(listenSock, SOMAXCONN);
cout << "服务器开始监听..." << endl;
if (!getsockname(listenSock, (sockaddr*)&sa, &nLen))
cout << "本地服务器IP: " << inet_ntoa(sa.sin_addr) << " 端口: " << htons(sa.sin_port) << endl;
SOCKET connectSock = INVALID_SOCKET;
while ((connectSock = accept(listenSock, (sockaddr*)&sa, &nLen)) != INVALID_SOCKET)
{
cout << "客户端IP: " << inet_ntoa(sa.sin_addr) << " 端口: " << htons(sa.sin_port) << " "
<< "连接到本地服务器" << endl;
_beginthread(RecvMsgProc, 0, new SOCKET(connectSock));
}
closesocket(listenSock);
return 0;
}
// client
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
// 在客户端必须要指明服务器的IP地址和端口号
enum { SERVICE_PORT = 33921 }; // 服务器的服务端口
const char* pStrSverIP = "192.168.0.233"; // 服务器的IP
int main()
{
WSAData wd;
WSAStartup(MAKEWORD(5, 12), &wd); //version:5.12
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
//绑定0端口:分配本机空闲端口
//绑定0IP:代表本机所有IP
sockaddr_in sa = { AF_INET };//绑定IP 0.0.0.0 和端口0,甚至不执行绑定与绑定0端口和0IP是相同
int n = bind(sock, (const sockaddr*)&sa, sizeof(sa));
if (n)
{
cout << "绑定失败:" << WSAGetLastError() << endl;
return -1;
}
int len = sizeof(sa);
n = getsockname(sock, (sockaddr*)&sa, &len);
if (!n)
{
cout << "本机信息:" << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) << endl;
}
n = getpeername(sock, (sockaddr*)&sa, &len);
if (n)
cout << "在建立连接之前获取不到对方信息!" << endl;
else
cout << "对方信息:" << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) << endl;
sa.sin_port = htons(SERVICE_PORT); // 指定服务器的服务端口
sa.sin_addr.S_un.S_addr = inet_addr(pStrSverIP); // 指定服务器的IP地址
n = connect(sock, (sockaddr*)&sa, sizeof(sa)); // 尝试连接目标服务器
if (n)
{
cout << "绑定失败:" << WSAGetLastError() << endl;
return -1;
}
n = getpeername(sock, (sockaddr*)&sa, &len); // TCP连接建立后获取对方IP和端口
if (n)
cout << "在建立连接之前获取不到对方信息!" << endl;
else
cout << "对方信息:" << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) << endl;
n = getsockname(sock, (sockaddr*)&sa, &len); // 获取本地的IP和端口
if (!n)
{
cout << "本机信息:" << inet_ntoa(sa.sin_addr) << "-" << htons(sa.sin_port) << endl;
}
char s[256];
while (true)
{
cin >> s;
send(sock, s, strlen(s), 0);
}
return 0;
}
测试结果