socket网络编程入门

网络编程

实现两台或多台已经联网的计算机互相交换数据的行为,就是网络编程.
我们日常使用的操作系统已经为我们提供了socket, 不需要熟悉网络数据传输的原理,
也能掌握网络编程.


windows中的socket与Linux中的有何区别?

  • 相同
  • Linux通常会用文件描述符来表示或区分已经打开了的文件;
  • windows通过文件句柄的方式来表示,和上述的Linux文件描述符是类似的概念;
  • 不同
  • Linux的一切都是文件,所以网路连接也是一个文件.
  • Windows则会将socket和文件区分开来,因此在Windowssocket有针对性设计的数据传输函数.

socket的类型

  • SOCK_STREAM

面向连接的套接字(Stream Sockets),是对SOCK_STREAM的说明.
使用了TCP协议,有自己的纠错机制.
浏览器使用的http协议就是基于面向连接的套接字

  • SOCK_DGRAM

无连接的套接字(Datagram Sockets),也叫数据报格式套接字.
它的传输效率相对于SOCK_STREAM要高,但对数据的校验工作较少.
视频聊天和语音视频大多是采用无连接套接字来传输数据的.

  • SOCK_RAW

原始套接字(raw-protocol interface).
保存了数据包的完整IP头,可以通过它来对数据进行分析.
网络安全产品通常使用此类型,如MAC地址扫描器,网络嗅探器等产品.


TCP套接字

  • 服务端
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;
int main()
{
    //初始化套接字库
    WORD wVersion;
    WSADATA wsaData;
    int err;
    wVersion = MAKEWORD(1, 1);
    err = WSAStartup(wVersion, &wsaData);
    //检查
    if (err != 0)
    {
        return err;
    }
    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
    {
        //清理套接字库
        WSACleanup();
        return -1;
    }
    //创建套接字
    SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);

    //准备绑定的信息
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(6000);

    //绑定到本机
    bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
    //监听
    listen(sockSrv, 10);

    cout << "server start at prot: 6000" << endl;
    SOCKADDR addrCli;
    int len = sizeof(SOCKADDR);
    char recvBuf[100];
    char sendBuf[100];
    while (true)
    {
        //接受连接请求
        SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrCli, &len);

        sprintf_s(sendBuf,100, "hello");

        send(sockConn, sendBuf, strlen(sendBuf) + 1, 0);
        //接受或发送数据
        recv(sockConn, recvBuf, 100, 0);

        std::cout << recvBuf << std::endl;

        //关闭套接字
        closesocket(sockConn);
    }

    //关闭套接字
    closesocket(sockSrv);
    //清理套接字库
    WSACleanup();

    system("pause");
    return 0;
}
  • 客户端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
    //初始化套接字库
    WORD wVersion;
    WSADATA wsaData;
    int err;
    wVersion = MAKEWORD(1, 1);
    err = WSAStartup(wVersion, &wsaData);
    //检查
    if (err != 0)
    {
        return err;
    }
    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
    {
        //清理套接字库
        WSACleanup();
        return -1;
    }

    //创建tcp套接字
    SOCKET sockCli = socket(AF_INET, SOCK_STREAM, 0);
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.0.131");
    //addrSrv.sin_addr.S_un.S_addr = htons(6000);//大失误
    addrSrv.sin_port = htons(6000);
    addrSrv.sin_family = AF_INET;

    //连接服务器
    connect(sockCli, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

    char sendBuf[] = "world";
    char recvBuf[100];

    //发送数据到服务器
    send(sockCli, sendBuf, strlen(sendBuf) + 1, 0);

    //接收服务器发送的数据
    recv(sockCli, recvBuf, sizeof(recvBuf), 0);

    cout << recvBuf << endl;

    closesocket(sockCli);

    WSACleanup();

	system("pause");
	return 0;
}

UDP套接字

  • 服务端
#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
    //初始化套接字库
    WORD wVersion;
    WSADATA wsaData;
    wVersion = MAKEWORD(1, 1);
    int err = WSAStartup(wVersion, &wsaData);
    //检查
    if (err != 0)
    {
        cout << WSAGetLastError() << endl;
        return err;
    }
    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
    {
        cout << WSAGetLastError() << endl;
        WSACleanup();
        return -1;
    }

    //创建套接字
    SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    addrSrv.sin_port = htons(6002);//主机字节序转换
    addrSrv.sin_family = AF_INET;

    //绑定到本机6002端口
    bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

    //接受请求,处理请求
    SOCKADDR_IN addrCli;
    int len = sizeof(SOCKADDR);

    char sendBuf[] = "UDP Server ...";
    char recvBuf[100];

    cout << "Start UDP Server with port 6002 " << endl;;
    while (true)
    {
        //接收数据
        recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
        cout << "Recv: " << recvBuf << endl;

        //发送数据
        sendto(sockSrv, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrCli, len);
        cout << "Send: " << sendBuf << endl;
    }

    //关闭套接字并清除套接字库
    closesocket(sockSrv);
    WSACleanup();

    system("pause");
    return 0;
}
  • 客户端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
    //初始化套接字库
    WORD wVersion;
    WSADATA wsaData;
    wVersion = MAKEWORD(1, 1);
    int err = WSAStartup(wVersion, &wsaData);
    //检查
    if (err != 0)
    {
        return err;
    }
    if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
    {
        WSACleanup();
        return -1;
    }

    //创建UDP套接字
    SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    addrSrv.sin_port = htons(6002);
    addrSrv.sin_family = AF_INET;

    SOCKADDR_IN addrCli;
    int len = sizeof(SOCKADDR);

    char sendBuf[] = "send from UDP client ...";
    char recvBuf[100];

    //发送数据到服务端并打印
    cout << "send to server: " << sendBuf << endl;
    sendto(sockCli, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrSrv, len);

    //接收服务端数据并打印
    recvfrom(sockCli, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
    cout << "receve from: " << recvBuf << endl;

    //关闭套接字并清除套接字库
    closesocket(sockCli);
    WSACleanup();

    system("pause");
    return 0;
}

常用函数分析

  • WSAStartup()函数

Windows操作系统特有的函数.
Windows系统中使用网络编程需要加载ws2_32.dll动态链接库.
使用这个dll之前需要调用WSAStartup()函数初始化动态库.

函数原型:

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
  • socket()函数

程序使用socket函数来创建套接字,不管是Linux还是Windows都一样,区别是返回值不同.
Linuxsocket函数的返回值是int,Windows里的返回值是SOCKET.
我这里练习的环境是windows,所以这里的代码例子都以Windows为准.

函数原型:

SOCKET socket(int af, int type, int protocol);
  • bind()函数

创建完套接字之后需要确定套接字的各种属性,比如IP地址,端口等信息.
这些信息用一个sockaddr结构体变量存放.

函数原型:

int bind(SOCKET sock, const struct sockaddr *addr, int addrlen); 
  • connect()函数

是客户端在创建完套接字后,用来建立连接的.
它的参数和bind相同.

函数原型:

int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);
  • listen()函数

在服务端绑定完套接字之后,还使用listen函数让套接字进入被动监听状态.
第二个参数是请求队列的最大长度.

函数原型:

int listen(SOCKET sock, int backlog);
  • accept()函数

套接字通过listen函数进入被动监听状态之后,通过accept函数接收客户端请求.
它的参数跟listenconnect函数相同.

函数原型:

SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);
  • send()函数

经过上面一系列的连接的目的,自然是为了实现服务端与客户端之间的发送和接收数据.
send函数是从服务端发送数据,第四个参数可以参考send recv函数中的flags参数

函数原型:

int send(SOCKET sock, const char *buf, int len, int flags);
  • recv()函数

recv函数的功能是从服务端或客户端接收数据.
它和send函数的参数是一样的.

函数原型:

int recv(SOCKET sock, char *buf, int len, int flags);

总结

这篇博客内容只是简单的涵盖了socket编程需要用到的函数,
以及给出了简单的示例代码.对于socket编程入门还是有帮助的.\

Windows操作系统拓展的套接字函数一般以WSA开头,它的意思是Windows socket API.常用的比如:
WSASend,WSARecv以及前面提到的WSACleanup等.

猜你喜欢

转载自blog.csdn.net/Stephen8848/article/details/124773426
今日推荐