WinSocket网络编程记录

记录下学习网络通信编程的知识。

一.概念理解

1.Socket通信模型

在这里插入图片描述
网络通信就好比去邮局寄信这个过程。Socket就相当于寄信这个过程中,邮局门口的邮筒,所以Socket也称套接字。
在这里插入图片描述

2.Socket通信流程

在这里插入图片描述

3.关于IP地址

①查询自己电脑的IP地址
在这里插入图片描述
②nslookup
在这里插入图片描述
nslookup的作用:将www.baidu.com这个域名解析为IP地址
即也可以通过输入http://180.101.49.11(180.101.49.11)也能访问到百度网页。

二.Socket通信函数

WSAStartup()

int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData)

使用Socket之前必须调用此函数

第一个参数:Socket版本
第二个参数:操作系统通过它返回请求Socket版本信息

WSACleanup()

int WSACleanup(void)
应用程序在完成对请求的Socket库使用之后,要调用此函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。

socket()

SOCKET socket(int af,int type,int protocol)

应用程序调用socket函数来创建网络通信的套接字。

参数介绍:
-af:一个地址描述。目前仅支持AF_INET格式,也就是ARPA Internet地址格式。
-type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)-protocol:指定协议,套接口所用的协议。如调用者不想指定,可用0。常用的协议有IPPROTO_TCP、IPPROTO_UDP等。

closesocket()

int closesocket(SOCKET s)

关闭一个描述符为s的套接字,执行成功返回0,否则返回SOCKET_ERROR。

send()

int send(SOCKET s,const char FAR* buf,int len,int flags)

不管是客户端还是服务端应用程序,都用此函数向TCP连接的另一端发送数据。

向一个已连接的套接口发送数据。

-s:一个用于标识已连接套接口的描述字,是目的地的套接口。
-buf:包含待发送数据的缓冲区。
-len:缓冲区中数据的长度。
-flags:调用执行方式。

第一个:SOCKET的描述符
第二个:存放应用程序要发送数据的缓冲区
第三个:指明实际要发送的数据的字节数
第四个:一般设置为0

recv()

int recv(SOCKET s,char FAR *buf,int len,int flags)

第三个参数:指明buf的长度。
第四个参数:一般设置为0

bind()

int bind(SOCKET s,const struct sockadrr FAR* name,int namelen)

创建一个socket之后,套接字数据结构中有一个默认的IP地址和默认端口,一个服务程序必须调用bind给其绑定一个IP和端口,客户端程序不需要绑定。

第一个:指定绑定的Socket描述符
第二个:指定Socket结构
第三个:指定名称长度

如无错误发生,则bind()返回0,否则的话,将返回-1,应用程序可通过WSAGetLastError()获取相应的错误代码。

listen()

int listen(SOCKET s,int backlog)
服务程序可以调用listen函数使流式套接字s处于监听状态。执行成功返回0,否则返回SOCKET_ERROR。

-s:用于标识一个已捆绑未连接套接口的描述字。
-backlog:等待连接队列的最大长度。

功能:监听通过socket发起的连接请求,将其加入等待队列,等待accept函数的接收。

accept()

SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen)

服务端程序调用,从处于监听状态的流式套接字s的客户连接请求队列中取出排在最前面的客户请求。

本函数从s的等待连接队列中抽取第一个连接,创建一个与s同类的新的套接口并返回句柄。

-s:套接字描述符,该套接口在listen()后监听连接。
-addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
-addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。

connect()

int connect(SOCKET s,const struct sockaddr FAR* name,int namelen)

-s:标识一个未连接socket
-name:指向要连接套接字的sockaddr结构体的指针
-namelen:sockaddr结构体的字节长度

客户程序调用,使客户的socket s与监听于name所指定的计算机特定端口上的服务socket进行连接,连接成功则返回0,否则返回SOCKET_ERROR。

本函数用于创建与指定外部端口的连接。s参数指定一个未连接的数据报或流类套接口。

如套接口未被捆绑,则系统赋给本地关联一个唯一的值,且设置套接口为已捆绑。请注意若名字结构中的地址域为全零的话,则connect()将返回WSAEADDRNOTAVAIL错误。

对于流类套接口(SOCK_STREAM类型),利用名字来与一个远程主机建立连接,一旦套接口调用成功返回,它就能收发数据了。对于数据报类套接口(SOCK_DGRAM类型),则设置成一个缺省的目的地址,并用它来进行后续的send()与recv()调用。

ioctlsocket()

int ioctlsocket(SOCKET s,long cmd,u_long FAR *argp)

ioctlsocket()是一个计算机函数,功能是控制套接口的模式。可用于任一状态的任一套接口。它用于获取与套接口相关的操作参数,而与具体协议或通讯子系统无关。

-s:一个标识套接口的描述字。
*cmd:对套接口s的操作命令。
-argp:指向cmd命令所带参数的指针。

三.IP处理函数

inet_aton()

int inet_aton(const char *string, struct in_addr*addr)

参数描述:
1 输入参数string包含ASCII表示的IP地址。
2 输出参数addr是将要用新的IP地址更新的结构。

返回值:
如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以它的值会被忽略。

inet_addr()

in_addr_t inet_addr(const char *cp)

inet_addr函数转换“网络主机地址”(如192.168.1.10)为“网络字节序二进制值”,如果参数char *cp无效,
函数返回-1(INADDR_NONE),这个函数在处理地址为255.255.255.255时也返回-1255.255.255.255是一个有效的地址,不过inet_addr无法处理。

inet_nota()

char *inet_ntoa(struct in_addr in)

inet_nota函数转换“网络字节排序的地址”为“标准的ASCII以点分开的地址”,该函数返回指向点分开的字符串地址(如192.168.1.10)的指针,
该字符串的空间为静态分配的,这意味着在第二次调用该函数时,上一次调用将会被覆盖,所以如果需要保存该字符串需要最后复制出来自己管理。

四.小案例:一个服务端和一个客户端通信

①在vs中建立两个项目,一个是服务端,一个是客户端
②将两个项目的属性设置如下
在这里插入图片描述
服务端.cpp

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <WinSock2.h> //Windows Socket编程头文件
#include <cstring>

#pragma comment(lib,"ws2_32.lib") //链接ws2_32.lib库文件到此项目中
using namespace std;

//================================全局常量================================
const int BUF_SIZE = 2048; //缓冲区大小

//================================全局变量================================
SOCKET sockSer,sockCli; //服务端、客户端套接口
SOCKADDR_IN addrSer, addrCli; //服务端、客户端的地址
int naddr = sizeof(SOCKADDR_IN);
char sendbuf[BUF_SIZE]; // 发送缓冲区
char inputbuf[BUF_SIZE]; //输入缓冲区
char recvbuf[BUF_SIZE]; //接受缓冲区

int main()
{
    //载入socket库
    WSADATA wsadata;
    if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
    {
        //输出出错信息
        cout << "载入socket库失败" << endl;

        system("pause");
        return 0;
    }

    //创建socket
    sockSer = socket(AF_INET, SOCK_STREAM, 0);
    //初始化服务器地址相关信息
    addrSer.sin_addr.s_addr = inet_addr("192.168.101.127"); //填自己电脑的IP地址
    addrSer.sin_family = AF_INET;
    addrSer.sin_port = htons(12248); //端口号随便写,最大65535

    //绑定socket和本地地址
    bind(sockSer, (SOCKADDR*)&addrSer, sizeof(SOCKADDR));

    while (1)  //不断的监听是否有连接请求
    {
        //监听socket
        listen(sockSer, 5);

        //接受连接请求
        sockCli = accept(sockSer, (SOCKADDR*)&addrCli, &naddr);
        if (sockCli != INVALID_SOCKET) //连接成功
        {
            cout << "连接成功!" << endl;
            strcpy(sendbuf, "欢迎!");

            send(sockCli, sendbuf, sizeof(sendbuf), 0);

        }

    }
    closesocket(sockSer);
    closesocket(sockCli);
    
    WSACleanup();
    
    return 0;
}

客户端.cpp

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <WinSock2.h> //Windows Socket编程头文件
#include <cstring>

#pragma comment(lib,"ws2_32.lib") //链接ws2_32.lib库文件到此项目中
using namespace std;

//================================全局常量================================
const int BUF_SIZE = 2048; //缓冲区大小

//================================全局变量================================
SOCKET sockSer, sockCli; //服务端、客户端套接口
SOCKADDR_IN addrSer, addrCli; //服务端、客户端的地址
int naddr = sizeof(SOCKADDR_IN);
char sendbuf[BUF_SIZE]; // 发送缓冲区
char inputbuf[BUF_SIZE]; //输入缓冲区
char recvbuf[BUF_SIZE]; //接受缓冲区

int main()
{
    //载入socket库
    WSADATA wsadata;
    if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
    {
        //输出出错信息
        cout << "载入socket库失败" << endl;

        system("pause");
        return 0;
    }

    //创建socket
    sockCli = socket(AF_INET, SOCK_STREAM, 0);
    //初始化客户端地址相关信息
    addrCli.sin_addr.s_addr = inet_addr("127.0.0.1"); //这个IP地址是用来测试的
    addrCli.sin_family = AF_INET;
    addrCli.sin_port = htons(12248); 

    //初始化服务器地址相关信息
    addrSer.sin_addr.s_addr = inet_addr("192.168.101.127"); //填自己电脑的IP地址
    addrSer.sin_family = AF_INET;
    addrSer.sin_port = htons(12248); 

    while (1)
    {
        if (connect(sockCli, (SOCKADDR*)&addrSer, sizeof(addrSer)) != SOCKET_ERROR)
        {
            recv(sockCli, recvbuf, sizeof(recvbuf), 0);
            cout << recvbuf << endl;
        }
    }

    closesocket(sockSer);
    closesocket(sockCli);

    WSACleanup();

	return 0;
}

效果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

boss-dog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值