C++之socket编程
环境搭建
- 开发环境- Clion
本地安装了vs2017,可以选择使用vs自带的C++编译器,如下图:
对于vc++
- 引入库文件
socket依赖winsocket.h、winsocket.lib和winsocket.dll
在CMakeLists中:
#添加头文件搜索路径
link_libraries(ws2_32 wsock32)
全部配置:
cmake_minimum_required(VERSION 3.16)
project(demo2)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
#add_executable( )
#添加头文件搜索路径
link_libraries(ws2_32 wsock32)
# 遍历项目根目录下所有的 .cpp 文件
#file (GLOB_RECURSE files *.cpp)
#foreach (file ${files})
# string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
# add_executable (${exe} ${file} socketDemo/msg_client.cpp overr/overmain.cpp)
# message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe})
#endforeach ()
add_executable(main main.cpp)
add_executable(client socketDemo/msg_client.cpp)
add_executable(server socketDemo/msg_server.cpp)
#链接gcc-g++静态库
#target_link_libraries(demo2 libgcc.a)
#target_link_libraries(demo2 libstdc++.a)
#链接网络库(如需要)
#target_link_libraries(demo2 ws2_32)
#链接线程库(必须放到最后)
#target_link_libraries(demo2 libpthread.a)
获取网络中计算机的IP地址和计算机名
流式套接字编程
网络数据的传输是靠套接字实现的。套接字有三种类型:流式套接字(socket_stream)、数据包套接字(socket_dgram)和原始套接字(RAW)。
流式套接字是面向连接的,提供双向、有序、无重复且记录无边界的数据流服务。适用于处理大数据量,开销大
服务器端编程步骤
- 初始化阶段调用函数
WSAStartup
初始化Windows Sockets DLL,只有初始化成功后,才能调用其他Windows API函数。
void WSAStartup(
_In_ WORD wVersionRequested, // 所使用的win socket版本
_Out_ LPWSADATA lpWSAData //储存系统返回的winSocket信息
);
- 建立socket
初始化动态链接后,在服务端建立一个监听socket
socket(
_In_ int af, // 目前提供PF_INET(AF_INET)
_In_ int type, // socket type, e.g. socket_stream(TCP),socket_dgram(UDP)
_In_ int protocol // 通讯协议,默认值0
);
所有的WSASocket函数都可以通过调用 WSAGetLastError()获取上次错误
- 绑定端口
bind(
_In_ SOCKET s,
_In_reads_bytes_(namelen) const struct sockaddr FAR * name, // socket地址,IP
_In_ int namelen // name的长度
);
服务器端程序:
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ws2tcpip.h>
using namespace std;
#define BUF_SIZE 1024
void initialization();
int main()
{
//定义长度变量
int send_len = 0;
int recv_len = 0;
int len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[100];
//定义服务端套接字,接受请求套接字
SOCKET s_server;
SOCKET s_accept;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
SOCKADDR_IN accept_addr;
initialization();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(5010);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (bind(s_server, (SOCKADDR *) &server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
cout << "套接字绑定失败!" << endl;
WSACleanup();
} else
{
cout << "套接字绑定成功!" << endl;
}
//设置套接字为监听状态
if (listen(s_server, SOMAXCONN) < 0)
{
cout << "设置监听状态失败!" << endl;
WSACleanup();
} else
{
cout << "设置监听状态成功!" << endl;
}
cout << "服务端正在监听连接,请稍候...." << endl;
//接受连接请求
len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR *) &accept_addr, &len);
if (s_accept == SOCKET_ERROR)
{
cout << "连接失败!" << endl;
WSACleanup();
return 0;
}
cout << "连接建立,准备接受数据" << endl;
//接收数据
while (1)
{
recv_len = recv(s_accept, recv_buf, 100, 0);
if (recv_len < 0)
{
cout << "接受失败!" << endl;
break;
} else
{
cout << "客户端信息:" << recv_buf << endl;
}
// cout << "请输入回复信息:";
// cin >> send_buf;
send_len = send(s_accept, "ok..", 100, 0);
if (send_len < 0)
{
cout << "发送失败!" << endl;
break;
}
}
//关闭套接字
closesocket(s_server);
closesocket(s_accept);
//释放DLL资源
WSACleanup();
return 0;
}
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;
}
//填充服务端地址信息
}
客户端程序:
#include <iostream>
#include <string>
#include <WinSock2.h>
using namespace std;
void initialization2();
void client();
int main()
{
client();
system("pause");
return 0;
}
void client()
{
//定义长度变量
int send_len = 0;
int recv_len = 0;
//定义发送缓冲区和接受缓冲区
char send_buf[100];
char recv_buf[100];
//定义服务端套接字,接受请求套接字
SOCKET s_server;
//服务端地址客户端地址
SOCKADDR_IN server_addr;
initialization2();
//填充服务端信息
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(5010);
//创建套接字
s_server = socket(AF_INET, SOCK_STREAM, 0);
if (connect(s_server, (SOCKADDR *) &server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
cout << "服务器连接失败!" << endl;
WSACleanup();
} else
{
cout << "服务器连接成功!" << endl;
}
//发送,接收数据
while (1)
{
cout << "请输入发送信息:";
cin >> send_buf;
send_len = send(s_server, "give me", 100, 0);
if (send_len < 0)
{
cout << "发送失败!" << endl;
break;
}
recv_len = recv(s_server, recv_buf, 100, 0);
if (recv_len < 0)
{
cout << "receive err" << endl;
break;
}
else
{
cout << "服务端信息:" << recv_buf << endl;
}
}
//关闭套接字
closesocket(s_server);
//释放DLL资源
WSACleanup();
}
void initialization2()
{
//初始化套接字库
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;
}
//填充服务端地址信息
}
静态编译
为了让程序能够顺利移植到其他平台,需要静态链接
参考
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
:必须
cmake_minimum_required(VERSION 3.16)
project(demo3)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")
add_executable(demo3 main.cpp)
#链接静态库
#链接boost静态库(project_Name为你的项目名,*分别代表库名、mingw版本号、boost库版本号,如:libboost_system-mgw72-mt-s-1_65_1.a)
#target_link_libraries(demo3 libboost_*-mgw*-mt-s-*.a)
#链接gcc-g++静态库
target_link_libraries(demo3 libgcc.a)
target_link_libraries(demo3 libstdc++.a)
#链接网络库(如需要)
target_link_libraries(demo3 ws2_32)
#链接线程库(必须放到最后)
target_link_libraries(demo3 libpthread.a)