目录
1.服务端
1.1使用 WinSock2 编写 TCP 服务端程序
这篇文章将介绍如何在 Windows 系统上使用 WinSock2 API 编写一个简单的 TCP 服务端程序。这个服务端监听特定端口,并接收来自客户端的数据,然后将其回传,直至客户端断开连接。
1.2环境准备
在 Windows 系统上使用 WinSock2 API 进行网络编程,首先需要包含 <WinSock2.h>
头文件,并链接 ws2_32.lib
库。具体代码见以下部分。
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
1.3步骤解析
1. 初始化 WinSock 库
在开始网络编程之前,需要使用 WSAStartup()
初始化 WinSock 库:
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
MAKEWORD(2, 2)
指定使用 WinSock 2.2 版本。
2. 创建套接字
通过调用 socket()
函数创建一个套接字,用于侦听客户端的连接请求:
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == listen_socket)
{
printf("create listen socket failed !!! errcode:%d\n", GetLastError());
return -1;
}
此处使用 AF_INET
表示 IPv4 地址族,SOCK_STREAM
表示使用 TCP 协议。
3. 绑定端口
创建完套接字后,需要将其绑定到指定的 IP 地址和端口号。sockaddr_in
结构体用来存储这些信息:
struct sockaddr_in local = { 0 };
local.sin_family = AF_INET;
local.sin_port = htons(8080); // 指定监听端口
local.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有本地 IP 地址
if (INVALID_SOCKET == bind(listen_socket, (struct sockaddr*)&local, sizeof(local)))
{
printf("bind socket failed !!! errcode:%d\n", GetLastError());
return -1;
}
4. 开始监听连接
通过 listen()
函数将套接字设置为监听状态,并指定最多允许 10 个连接请求排队:
if (INVALID_SOCKET == listen(listen_socket, 10))
{
printf("start socket failed !!! errcode:%d\n", GetLastError());
return -1;
}
5. 接受客户端连接
使用 accept()
函数等待客户端连接。该函数为阻塞调用,只有当有客户端连接时才会返回一个新的套接字,用于与客户端通信。
SOCKET client_socket = accept(listen_socket, NULL, NULL);
if (INVALID_SOCKET == client_socket)
{
printf("socket accept failed !!! errcode:%d\n", GetLastError());
return -1;
}
6. 数据接收与发送
服务端进入数据接收循环,通过 recv()
函数接收客户端发来的数据,并使用 send()
函数将数据回传给客户端:
char buff[1024] = { 0 };
while (1)
{
if (0 >= recv(client_socket, buff, sizeof(buff), 0))
break;
send(client_socket, buff, strlen(buff), 0);
printf("%s\n", buff);
}
7. 关闭套接字
当客户端断开连接时,通过 closesocket()
函数关闭客户端的套接字:
closesocket(client_socket);
1.4完整代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <WinSock2.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32.lib")
// 服务端
int main()
{
// windows上使用网络功能需要开启网络权限
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
// 1.创建socket套接字
/*
SOCKET socket(
int af, // 协议地址蔟 (使用的是IPV4/IPV6 AF_INET/AF_INET6)
int type, // 类型 (流式协议/帧式协议 SOCK_STRAM/SOCK_DGRAM)
int protocol // 保护协议 (做上层协议一般不管,直接赋0)
)
*/
SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == listen_socket) // 无效的socket的意思等于-1
{
printf("create listen socket failed !!! errcode:%d\n", GetLastError());
return -1;
}
// 2.给这个socket绑定一个端口号
/*
sockaddr_in 原型
struct sockaddr_in {
ADDRESS_FAMILY sin_family; // 协议地址蔟 (使用的是IPV4/IPV6 AF_INET/AF_INET6)
USHORT sin_port; // 端口号
IN_ADDR sin_addr; // IP地址
CHAR sin_zero[8]; // 保留字节
};
*/
struct sockaddr_in local = { 0 };
local.sin_family = AF_INET;
local.sin_port = htons(8080);
local.sin_addr; // 服务端 选项 网卡 127.0.0.1(本地环回) 至接收哪个网卡的数据 一般写全零地址,表示全部都接收0.0.0.0
//local.sin_addr.s_addr = htonl(INADDR_ANY);
/*
绑定:bind(
SOCKET s,
const struct sockaddr * name,
int namelen
);
*/
if (INVALID_SOCKET == bind(listen_socket, (struct sockaddr*)&local, sizeof(local)))
{
printf("create socket failed !!! errcode:%d\n", GetLastError());
return -1;
}
// 3.给这个socket开启监听属性
/*
listen(
SOCKET s,
int backlog
);
*/
if (INVALID_SOCKET == listen(listen_socket, 10))
{
printf("start socket failed !!! errcode:%d\n", GetLastError());
return -1;
}
// 4.等待客户端连接
// 返回的客户端socket才是跟客户端可以通讯的一个socket
// accept是一个阻塞函数,等到有客户端连接进来就接受连接,然后返回,否则就阻塞
/*
accept(
SOCKET s, //监听socket
struct sockaddr * addr, // 客户端的IP地址和端口号
int * addrlen // 结构的大小
);
*/
while (1)
{
SOCKET client_socket = accept(listen_socket, NULL, NULL);
if (INVALID_SOCKET == client_socket)
{
printf("socket accept failed !!! errcode:%d\n", GetLastError());
return -1;
continue;
}
// 5.开始通讯
while (1) { // 当客户端断开的时候再控制断开
char buff[1024] = { 0 };
/*
recv(
SOCKET s, // 客户端socket
char * buf, // 接受的数据存到哪里
int len, // 接受的长度
int flags // 0
);
*/
if (0 >= recv(client_socket, buff, sizeof(buff), 0))
break;
send(client_socket, buff, 20, 0);
printf("%s\n", buff);
}
// 6.关闭连接
closesocket(client_socket);
}
}
2.客户端
2.1使用 WinSock2 编写 TCP 客户端程序
这篇文章将介绍如何在 Windows 系统上使用 WinSock2 API 编写一个简单的 TCP 客户端程序。这个客户端用于连接到特定 IP 和端口的服务器,并与服务器进行数据交换。
2.2环境准备
在 Windows 系统上进行 WinSock2 网络编程时,需要包含 <WinSock2.h>
头文件,并链接 ws2_32.lib
库。为了避免编译警告,还可以使用 _CRT_SECURE_NO_WARNINGS
和 _WINSOCK_DEPRECATED_NO_WARNINGS
宏。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
2.3步骤解析
1. 初始化 WinSock 库
首先,需要使用 WSAStartup()
初始化 WinSock 库。这一步设置 WinSock 环境,以便后续的网络编程函数能够正常工作:
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
其中,MAKEWORD(2, 2)
指定了使用 WinSock 2.2 版本。
2. 创建客户端套接字
通过调用 socket()
函数创建一个 TCP 套接字,用于与服务器的通信:
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == client_socket)
{
printf("create socket failed !!!\n");
return -1;
}
这里使用 AF_INET
表示 IPv4 地址族,SOCK_STREAM
表示 TCP 协议。
3. 连接服务器
创建套接字后,需要设置服务器的 IP 地址和端口,并通过 connect()
函数发起连接请求。
struct sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons(8080);
target.sin_addr.s_addr = inet_addr("127.0.0.1");
if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target)))
{
printf("connect server failed !!!\n");
closesocket(client_socket);
return -1;
}
此处使用 127.0.0.1
表示本地 IP 地址,端口号为 8080
。
4. 数据发送和接收
在成功连接到服务器后,客户端可以发送和接收数据。通过 scanf()
获取用户输入,然后用 send()
函数发送数据给服务器。服务器响应的数据则通过 recv()
函数接收。
while (1)
{
char buff[1024] = { 0 };
scanf("%s", buff); // 获取用户输入的数据
send(client_socket, buff, sizeof(buff), 0); // 发送数据给服务器
char recv_buff[1024] = { 0 };
if (0 >= recv(client_socket, recv_buff, 20, 0)) // 接收服务器返回的数据
break;
printf("接收到数据%s\n", recv_buff); // 输出接收到的服务器响应
}
5. 关闭连接
当客户端完成通讯或退出时,使用 closesocket()
关闭与服务器的连接,释放资源:
closesocket(client_socket);
2.4完整代码
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
// 1.创建socket套接字
SOCKET client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (INVALID_SOCKET == client_socket)
{
printf("create socket failed !!!\n");
return -1;
}
// 2.连接服务器
struct sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons(8080);
target.sin_addr.s_addr = inet_addr("127.0.0.1");
if (-1 == connect(client_socket, (struct sockaddr*)&target, sizeof(target)))
{
printf("connect server failed !!!\n");
closesocket(client_socket);
return -1;
}
// 3.开始通讯
while (1)
{
char buff[1024] = { 0 };
scanf("%s", buff);
send(client_socket, buff, sizeof(buff), 0);
char recv_buff[1024] = { 0 };
if (0 >= recv(client_socket, recv_buff, 20, 0))
break;
printf("接收到数据%s\n", recv_buff);
}
// 4.关闭连接
closesocket(client_socket);
}