目录
一、TCP通信流程
二、主要API分析
1.网络初始化
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
- wVersionRequested:用于指定Windows Sockets API的版本。
- lpWSAData:指向WSADATA结构的指针,用于接收初始化的相关信息。
2.创建套接字(socket)
SOCKET socket(int af, int type, int protocol);
- af:地址族,如AF_INET(IPv4)。
- type:套接字类型,如SOCK_STREAM(流式套接字,用于TCP)。
- protocol:协议类型,通常设为0表示使用默认协议。
3.绑定端口(bind)
int bind(SOCKET s, const struct sockaddr* name, int namelen);
- s:要绑定的套接字。
- name:指向包含地址和端口信息的sockaddr结构体的指针。
- namelen:sockaddr结构体的长度。
4.监听(listen)
int listen(SOCKET s, int backlog);
- s:要设置为监听状态的套接字。
- backlog:等待连接队列的最大长度。
5.客户端连接(connect)
int connect(SOCKET s, const struct sockaddr* name, int namelen);
- s:要连接的套接字。
- name:指向包含服务端地址和端口信息的sockaddr结构指针。
- namelen:sockaddr结构的长度。
6.接受连接(accept)
SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);
- s:正在监听的套接字。
- addr:指向sockaddr结构的指针,用于接收连接客户端的地址信息,可以为空。
- addrlen:指向addr结构长度的指针,可以为空。
7.接收数据(recv)
int recv(SOCKET s, char* buf, int len, int flags);
- s:接收数据的套接字。
- buf:用于存储接收数据的缓冲区。
- len:缓冲区的大小。
- flags:标志位,通常设为0。
8.发送数据(send)
int send(SOCKET s, const char* buf, int len, int flags);
- s:发送数据的套接字。
- buf:要发送数据的缓冲区。
- len:要发送数据的长度。
- flags:标志位,通常设为0。
9.关闭套接字(closesocket)
int closesocket(SOCKET s);
- s:要关闭的套接字。
三、转换函数
1.htons函数(端口号)
htons用于将主机字节序(Host Byte Order)转换为网络字节序(Network Byte Order)。
在网络通信中,不同的系统可能使用不同的字节顺序来表示数值,为了确保在网络中正确地传输数据,需要将数据转换为网络字节序,即将小端序转换为大端序。
u_short htons(u_short hostshort);
- hostshort:一个16位的无符号整数,表示主机字节序的值。
2.inet_pton函数(IP地址)
int inet_pton(int af, const char *src, void *dst);
inet_pton用于将字符串形式的 IP 地址转换为网络字节序的二进制形式。
- af:地址族(AF_INET-IPv4,AF_INET6-IPv6)。
- src:IP地址字符串。
- dst:用于存储转换结果。
四、服务端代码
#include<iostream>
#include<cstring>
#include<WinSock2.h>
#include<thread>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main(void)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket == INVALID_SOCKET) {
cerr << "error" << endl;
return -1;
}
struct sockaddr_in local = { 0 };
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = htons(9999);
if (bind(listen_socket, (struct sockaddr*)&local, sizeof local) == INVALID_SOCKET) {
cerr << "error" << endl;
return -1;
}
if (listen(listen_socket, 128) == -1) {
cerr << "error" << endl;
return -1;
}
while (true){
SOCKET client_socket = accept(listen_socket, nullptr, nullptr);
if (client_socket == INVALID_SOCKET) {
continue;
}
cout << "client connect: " << client_socket << endl;
thread th([](SOCKET client_socket){
while (true) {
char buffer[1024] = { 0 };
int ret = recv(client_socket, buffer, sizeof buffer, 0);
if (ret <= 0) {
cout << "client disconnect: " << client_socket << endl;
break;
}
cout << buffer << endl;
send(client_socket, buffer, strlen(buffer), 0);
}
closesocket(client_socket);
}, client_socket);
th.detach();
}
return 0;
}
五、客户端代码
#include<iostream>
#include<cstring>
#include<Winsock2.h>
#include<WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main(void)
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (client_socket == INVALID_SOCKET) {
cerr << "error" << endl;
return -1;
}
struct sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons(9999);
inet_pton(AF_INET, "127.0.0.1", &target.sin_addr.s_addr);
//target.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(client_socket, (struct sockaddr*)&target, sizeof target) == INVALID_SOCKET) {
cerr << "error" << endl;
closesocket(client_socket);
return -1;
}
while (true) {
char buffer1[1024] = { 0 };
cout << "enter: ";
cin >> buffer1;
send(client_socket, buffer1, strlen(buffer1), 0);
char buffer2[1024] = { 0 };
int ret = recv(client_socket, buffer2, sizeof buffer2, 0);
if (ret <= 0) {
cout << "server disconnect." << endl;
}
cout << buffer2 << endl;
}
closesocket(client_socket);
return 0;
}
相关文章推荐:
参考视频:
https://www.bilibili.com/video/BV1aW4y1w7Ui/?spm_id_from=333.337.search-card.all.click