TCP例子
服务端
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
SOCKET sClient = (SOCKET)lpParameter;
while (true)
{
// 此处PACKAGE为自定义数据结构
PACKAGE pkgForm;
// 接收数据并对数据反序列化
int nRet = recv(sClient, (char*)&pkgForm, sizeof(PACKAGE), 0);
if (nRet == 0 nRet == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAECONNRESET)
{
printf("远程主机强迫关闭了一个现有的连接\r\n");
// 当客户端断开连接时,关闭socket并退出线程
closesocket(sClient);
return 0;
}
else
{
// 其他问题则跳过本次循环
continue;
}
}
// TODO: 进行包的数据分发由各自函数解析函数
// TODO: 需要将socket存入一个链表或动态数组中进行保存,以便消息广播等作用。在多线程中访问全局数据需要做线程同步
// 缓解cpu压力
Sleep(1);
}
return 0;
}
int main()
{
initialization();
//1)创建socket
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockServer == INVALID_SOCKET)
{
cout << "socket 创建失败 \r\n" << endl;
return 0;
}
//绑定端口 127.0.0.1 回环地址
sockaddr_in siServer;
siServer.sin_family = AF_INET;
siServer.sin_port = htons(0x9527); // 服务器监听端口
siServer.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // 服务器地址,也可以是0.0.0.0
int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer));
if (nRet == SOCKET_ERROR)
{
cout << "绑定地址失败 \r\n" << endl;
return 0;
}
else
{
cout << "服务器启动完毕 \r\n" << endl;
}
if (listen(sockServer, SOMAXCONN) == SOCKET_ERROR)
{
cout << "端口监听失败 \r\n" << endl;
}
cout << "服务端正在监听连接,请稍候...." << endl;
// 循环读取消息
while (true)
{
//接受连接请求
sockaddr_in siAccept;
int nLen = sizeof(SOCKADDR_IN);
// 当有客户连接是阻塞取消
SOCKET sClient = ::accept(sockServer, (SOCKADDR*)&siAccept, &nLen);
if (sClient == SOCKET_ERROR) {
cout << "连接失败!" << endl;
WSACleanup();
return 0;
}
cout << "连接建立[" << inet_ntoa(siAccept.sin_addr) << ":" << ntohs(siAccept.sin_port) << "],准备接受数据" << endl;
// 连接建立成功则启动一个线程用来和客户端通讯
// 将客户端的socket传入线程中用来通讯
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, (PVOID)sClient, 0, NULL);
if (hThread == NULL)
{
cout << "工作线程建立失败" << endl;
closesocket(sClient);
continue;
}
else
{
cout << "工作线程建立成功" << endl;
}
// 关闭线程句柄,但是线程仍在运行
CloseHandle(hThread);
Sleep(1);
}
//关闭套接字
closesocket(sockServer);
//释放DLL资源
WSACleanup();
}
客户端
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
// 自定义用户消息,用来多线程中同步修改UI数据
#define WM_UPDATE_UI WM_USER + 0x1
/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
}
// 多线程处理服务器返回消息
DWORD WINAPI RecvSockMessage(LPVOID lpParameter)
{
// 类中调用多线程的时候将类传入函数
CClientDlg* pThis = (CClientDlg*)lpParameter;
while (true)
{
// 反序列化
// PACKAGE同服务端
PACKAGE pkgForm;
// 接收服务端返回消息,若无返回则会阻塞
int nRet = recv(pThis->m_socketClient, (char*)&pkgForm, sizeof(PACKAGE), 0);
if (nRet == 0 nRet == SOCKET_ERROR)
{
continue;
}
// 线程安全的将每个封包传入类中的封包队列中
pThis->m_clsLock.Lock();
pThis->m_lstPakToHandle.push_back(pkgForm);
pThis->m_clsLock.Unlock();
// 发送用户自定义消息进行UI更新以及数据包处理
pThis->PostMessage(WM_UPDATE_UI);
Sleep(1);
}
return 0;
}
// 用户自定义消息的处理函数
afx_msg LRESULT CClientDlg::OnUpdateUI(WPARAM wParam, LPARAM lParam)
{
// 线程安全获取封包
m_clsLock.Lock();
auto pkgForm = m_lstPakToHandle.front();
// 获取一个包将队列中的包去除一个
m_lstPakToHandle.pop_front();
m_clsLock.Unlock();
// TODO 数据包分发函数进行处理
return 0;
}
// 在不通环境下将此段代码加入各自的初始化函数中
void InitSocket()
{
initialization();
// 初始服务器数据
m_serverInfo.m_wPort = htons(0x9527); // 服务器端口
m_serverInfo.m_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务器IP
// 服务器连接结构体
SOCKADDR_IN sServerAddr;
sServerAddr.sin_family = AF_INET;
sServerAddr.sin_addr = m_serverInfo.m_addr;
sServerAddr.sin_port = m_serverInfo.m_wPort;
// 创建socket
// m_socketClient 为全局或类中成员,类型为SOCKET
m_socketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (m_socketClient == INVALID_SOCKET)
{
AfxMessageBox("程序异常,请检查软件权限");
exit(0);
}
// 连接服务器
if (connect(m_socketClient, (SOCKADDR*)&sServerAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
AfxMessageBox("服务器连接失败");
WSACleanup();
exit(0);
}
// 启动收数据线程
m_hThread = CreateThread(NULL, 0, RecvSockMessage, this, 0, NULL);
if (m_hThread == NULL)
{
AfxMessageBox("程序异常 \r\n");
exit(0);
}
// 当客户端和服务器连接成功后,可以使用如下代码获取客户端的本地端口
sockaddr_in si;
int nLen = sizeof(si);
getsockname(m_socketClient, (sockaddr*)&si, &nLen);
m_localUserInfo.m_wPort = si.sin_port;
// TODO: 发送自己的数据包,比如登录包
}
UDP例子
服务端
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
int main()
{
initialization();
// 创建socket
SOCKET sockServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // udp协议
if (sockServer == INVALID_SOCKET)
{
cout << "socket 创建失败 \r\n");
return 0;
}
//绑定服务器IP和端口
sockaddr_in siServer;
siServer.sin_family = AF_INET;
siServer.sin_port = htons(0x9527);
siServer.sin_addr.S_un.S_addr = inet_addr("0.0.0.0");
//
int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer));
if (nRet == SOCKET_ERROR)
{
printf("绑定地址失败 \r\n");
return 0;
}
else
{
printf("服务器启动完毕 \r\n");
}
// 维护一个在线列表
list<USERINFO> lstOnlineClient;
// 循环读取消息
while (true)
{
sockaddr_in siClient = { 0 };
int nLen = sizeof(siClient);
// 反序列化
PACKAGE pkgForm;
int nRet = recvfrom(sockServer, (char*)&pkgForm, sizeof(PACKAGE), 0, (sockaddr*)&siClient, &nLen);
if (nRet == 0 nRet == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAECONNRESET)
{
printf("远程主机强迫关闭了一个现有的连接\r\n");
continue;
}
else
{
continue;
}
}
// TODO: 数据处理
}
// 释放资源
closesocket(sockServer);
WSACleanup();
}
客户端
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Winsock2.h>
#pragma comment(lib, "Ws2_32.lib")
#define WM_UPDATE_UI WM_USER + 0x1
/// <summary>
/// 初始化套接字库
/// </summary>
void initialization() {
WORD w_req = MAKEWORD(2, 2);//版本号
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if (err != 0) {
cout << "初始化套接字库失败!" << endl;
}
else {
cout << "初始化套接字库成功!" << endl;
}
//检测版本号
if (LOBYTE(wsadata.wVersion) != 2 HIBYTE(wsadata.wHighVersion) != 2) {
cout << "套接字库版本号不符!" << endl;
WSACleanup();
}
else {
cout << "套接字库版本正确!" << endl;
}
//填充服务端地址信息
}
// 多线程处理服务器返回消息
DWORD WINAPI RecvSockMessage(LPVOID lpParameter)
{
// 类中调用多线程的时候将类传入函数
CClientDlg* pThis = (CClientDlg*)lpParameter;
while (true)
{
sockaddr_in siClient = { 0 };
int nLen = sizeof(siClient);
// 反序列化
// PACKAGE同服务端
PACKAGE pkgForm;
// 接收服务端返回消息,若无返回则会阻塞
int nRet = recvfrom(pThis->m_socketClient, (char*)&pkgForm, sizeof(PACKAGE), 0, (sockaddr*)&siClient, &nLen);
if (nRet == 0 nRet == SOCKET_ERROR)
{
continue;
}
// 线程安全的将每个封包传入类中的封包队列中
pThis->m_clsLock.Lock();
pThis->m_lstPakToHandle.push_back(pkgForm);
pThis->m_clsLock.Unlock();
// 发送用户自定义消息进行UI更新以及数据包处理
pThis->PostMessage(WM_UPDATE_UI);
Sleep(1);
}
return 0;
}
// 用户自定义消息的处理函数
afx_msg LRESULT CClientDlg::OnUpdateUI(WPARAM wParam, LPARAM lParam)
{
// 线程安全获取封包
m_clsLock.Lock();
auto pkgForm = m_lstPakToHandle.front();
// 获取一个包将队列中的包去除一个
m_lstPakToHandle.pop_front();
m_clsLock.Unlock();
// TODO 数据包分发函数进行处理
return 0;
}
// 在不通环境下将此段代码加入各自的初始化函数中
void InitSocket()
{
initialization();
// 初始服务器数据
m_serverInfo.m_wPort = htons(0x9527); // 服务器端口
m_serverInfo.m_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务器IP
// 创建socket
// m_socketClient 为全局或类中成员,类型为SOCKET
m_socketClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (m_socketClient == INVALID_SOCKET)
{
AfxMessageBox("程序异常,请检查软件权限");
exit(0);
}
// 启动收数据线程
m_hThread = CreateThread(NULL, 0, RecvSockMessage, this, 0, NULL);
if (m_hThread == NULL)
{
AfxMessageBox("程序异常 \r\n");
exit(0);
}
// 当客户端发送一次封包后,可以使用如下代码获取客户端的本地端口
sockaddr_in si;
int nLen = sizeof(si);
getsockname(m_socketClient, (sockaddr*)&si, &nLen);
m_localUserInfo.m_wPort = si.sin_port;
// TODO: 发送自己的数据包,比如登录包
}
Comments