Socket编程基础----TCP C/S模型

1. 客户端需要执行的步骤

在这里插入图片描述

1.1 打开网络库并且校验版本

  在Windows平台使用Visual studio进行网络通信相关开发的时候,首先需要导入头文件和静态库。

#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

  导入头文件后,就可以使用网络库中的函数。首先需要打开网络库,根据指定的版本打开一个网络库。使用到的函数是WSAStartup()。

参数&返回值作用
wVersionRequired需要的网络库版本
lpWSAData返回的网络库版本的信息
int WSAStartup(
  WORD      wVersionRequired,
  LPWSADATA lpWSAData
);

示例:

// 1. 打开网络库
	WORD wVersion = MAKEWORD(2, 2);// 2.2的网络库版本
	WSADATA dwVersionInfo;
	int status = WSAStartup(wVersion, &dwVersionInfo);
	if (status != 0)
	{
		printf("Open Lib failed \n");
		WSACleanup();
		return -1;
	}
	// 版本校验
	if (HIBYTE(dwVersionInfo.wVersion)!= 2 || LOBYTE(dwVersionInfo.wVersion) != 2)
	{
		printf("Open Version is not match\n");
		WSACleanup();
		return -1;
	}

1.2 创建一个服务器socket

  在操作系统中进行网络开发的时候,系统底层将复杂的协议栈进行封装,封装完成后的结果就是一个socket。在进行网络通信的时候,就是调用这些创建好的socket。
  创建socket的函数如下。

参数&返回值作用
af地址类型 : 常用的地址有IPV4 IPV6等地址,在一般情况下
为TCP地址
type套接字类型, 常用的套接字类型有基于流和数据报类型的
TCP 协议一般为SOCK_STREAM, UDP一般为SOCK_DGRAM
protocol协议类型: TCP协议为 IPPROTO_TCP , UDP协议为IPPROTO_UDP 就是各种类型的数据包
socket s若创建成功则会返回一个创建好的socket, 否则会返回SOCK_ERROR
SOCKET  socket(
  int af,
  int type,
  int protocol
);

示例:

// 创建一个Socket
// 网络通信的时候,需要使用到SOCKET,其中一个作为服务端,一个作为客户端
// 参数: 1. 地址类型 IPV4类型
//		  2. 套接字类型 数据流类型
//		  3. 协议的类型 TCP协议
SOCKET sockServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockServer == INVALID_SOCKET)
{
	printf("SOCKET 错误\n");
	WSACleanup();
	return -1;
}

1.3 绑定地址与端口

  对于一个TCP的服务端,需要将SOCKET其与对应的IP地址和端口号进行绑定。

参数&返回值作用
SOCKET s创建好的服务器socket
const sockaddr *addr服务器的IP地址和端口号等
int namelen参数二的长度
return绑定成功会返回0, 绑定失败会返回SOCK_ERROR

  在进行IP地址和端口号的绑定的时候,首先需要指定服务端的IP地址和端口号。从参数中可以看到所需要的数据第二个参数就是sockaddr这个类型的指针。在实际使用中,一般会先使用sockaddr_in这个类型的结构体对IP地址进行赋值,然后再进行转换。

int bind(
  SOCKET           s,
  const sockaddr *addr,
  int             namelen
);

实列:

// 绑定地址与端口,
// 地址就是IP地址,端口号就是具体的应用程序
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(0x8080);
serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
// 进行地址绑定的时候, SOCKADDR_IN和SOCKADDR 其实是一样的
// 使用SOCKADDR 的时候会比较简单,因此可以使用SOCKADDR_IN的类型的数据进行赋值,
// 然后将其强转为SOCKADDR类型
status = bind(sockServer, (sockaddr *) &serverAddr, sizeof(serverAddr));
if (status != 0)
{
	printf("绑定错误\n");
	// 关闭具体的socket
	closesocket(sockServer);
	WSACleanup();
	return -1;
}

1.4 启动监听

  监听的作用就是,将套接字置于正在侦听传入连接的状态,只有启动监听了,服务端才能开始去与客户端建立连接。

参数&返回值作用
SOCKET s创建好的服务器socket
int backlog挂起连接队列的最大长度 一般填 SOMAXCONN 让系统自己判断需要连接的个数
return绑定成功会返回0, 绑定失败会返回SOCK_ERROR
int WSAAPI listen(
  SOCKET s,
  int    backlog
);

示例:

// 开始监听函数
// 将套接字置于侦听正在传入链接的状态,socket 就开始工作了
status = listen(sockServer, 2);
if (status != 0)
{
	printf("listen failed \n");
	closesocket(sockServer);
	WSACleanup();
	return -1;
}

1.5 接收客户端传来的连接

  listen监听客户端来的链接,accept将客户端的信息绑定到一个socket上,也就是给客户端创建一个socket,通过返回值返回客户端的socket。

参数&返回值作用
SOCKET s创建好的服务器socket
sockaddr *addr客户端的IP地址和端口信息的结构体
int *addrlen参数二的长度
return若成功返回,则返回的就是客户端的socket, 若返回错误,就会返回INVALID_SOCKET
SOCKET WSAAPI accept(
  SOCKET   s,
  sockaddr *addr,
  int      *addrlen
);

示例:

// accept 函数,用于将客户端的链接进行接受,能够为系统提供客户端的
// socket 和其socket信息. 监听的作用listen监听客户端来的链接,accept
// 将客户端的信息绑定到一个socket上,也就是给客户端创建一个socket,
// 通过返回值返回给我们客户端的socket
sockaddr_in clientAddr;
int clientAddrLen;
// accept 函数是阻塞的,会一直等到客户端链接才会继续执行
SOCKET sockClient =  accept(sockServer, NULL, NULL);
if (sockClient == INVALID_SOCKET)
{
	printf("SOCKET 接受错误\n");
	closesocket(sockServer);
}

1.6 发送数据和接收数据

1.6.1 接收函数

  数据的接收都是由协议本身做的,也就是socket的底层做的,系统会有一段缓冲区,存储着接收到的数据。接收函数的本质就是将协议FIFO当中的数据读取到应用程序当中。

参数&返回值作用
SOCKET s创建好的服务器socket
char *buf用于接收数据的字符数组
int len,想要接收的长度
flags一般填零就可以
return执行成功会返回接收到的数据个数,
返回值为0 说明连接中断或者客户端下线,
执行失败会返回SOCK_ERROR

  recv函数是一个阻塞的函数,也就是说若要接收的数据长度比协议缓冲区中的数据要多的话,它会一直等到协议缓冲区中有足够的数据。

int recv(
  SOCKET s,
  char   *buf,
  int    len,
  int    flags
);
// 接受数据函数recv
char buf[4096];
status = recv(sockClient, buf, 4096, 0);
if (status == 0)
{
	printf("链接中断、 客户端下线\n");
}
else if (status == SOCKET_ERROR)
{
	printf("接受错误\n");
}
else
{
	printf("receive bytes %d\n", status);
}

1.6.2 发送函数

参数&返回值作用
SOCKET s要发送的客户端socket
char *buf用于发送数据的字符数组
int len,想要发送的长度
flags一般填零就可以
return执行成功会返回接收到的数据个数,
执行失败会返回SOCK_ERROR
int WSAAPI send(
  SOCKET     s,
  const char *buf,
  int        len,
  int        flags
);
// 发送函数send
status = send(sockClient, buf, 4096, 0);
if (status == SOCKET_ERROR)
{
	printf("发送错误\n");
}
else
{
	printf("send bytes %d\n", status);
}

2. TCP 客户端

   TCP 客户端实现的功能相较于服务端要简单很多,唯一的不同就是客户端只需要去连接服务端的socket就可以了。在客户端连接到服务端的时候,连接的socket需要连接服务端的socket。
在这里插入图片描述

connect函数

参数&返回值作用
SOCKET s服务器的socket
const sockaddr *name服务端的IP地址和端口号的结构体
int namelen,参数二的长度
return连接成功会返回0,失败会返回SOCK_ERROR
int WSAAPI connect
(
  SOCKET         s,
  const sockaddr *name,
  int            namelen
);

示例:

sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(0x8080);
serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
status = connect(sockServer, (sockaddr *)&serverAddr, sizeof(serverAddr));
if (status == SOCKET_ERROR)
{
	printf("链接错误\n");
	closesocket(sockServer);
	return -1;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值