TCP IP协议学习

目录

1.服务端

1.1使用 WinSock2 编写 TCP 服务端程序

1.2环境准备

1.3步骤解析

1. 初始化 WinSock 库

2. 创建套接字

3. 绑定端口

4. 开始监听连接

5. 接受客户端连接

6. 数据接收与发送

7. 关闭套接字

1.4完整代码

2.客户端

2.1使用 WinSock2 编写 TCP 客户端程序

2.2环境准备

2.3步骤解析

1. 初始化 WinSock 库

2. 创建客户端套接字

3. 连接服务器

4. 数据发送和接收

5. 关闭连接

2.4完整代码

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);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值