C++ Socket

Socket网络编程实现过程简单总结

一、   服务器端

1、    加载及释放套接字库

a)       使用函数WSAStartup()绑定相应的套接字库:

    当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。

Ex:

        wVersionRequested = MAKEWORD( 2, 1 );//指定加载2.1版本

        err =WSAStartup( wVersionRequested, &wsaData );

关于Socket版本:不同版本是有区别的,例如1.1版只支持TCP/IP协议,而2.0版可以支持多协议。2.0版有良好的向后兼容性,任何使用1.1版的源代码、二进制文件、应用程序都可以不修改地在2.0规范下使用。此外winsock 2.0支持异步 1.1不支持异步.

b)   使用函数WSACleanup ()绑定相应的套接字库:

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

c)    以上二者需要库Ws2_32.lib和头文件winsock2.h

#include"winsock2.h"

#pragma comment(lib,”ws2_32.lib”)

2、   创建套接字

socket(domain=AF_INET,type=SOCK_STREAM,proto=IPPROTO_TCP)

三个参数分别是:地址系列,套接字类型,协议号

1)、Domain : Domain参数指定了通信的”域”

AF_UNIX :AF_LOCAL本地通信

AF_INET:IPv4网络通信

AF_INET6:IPv6网络通信

AF_PACKET:链路层通信

2)、Type: Type就是socket的类型,对于AF_INET协议族而言有流套接字(SOCK_STREAM)、数据包套接字(SOCK_DGRAM)、原始套接字(SOCK_RAW)。

        SOCK_STREAM:流套接字, 提供顺序,可靠,双向,基于连接的字节流。 可以支持带外数据传输机制。例

            如:TCP协议、FTP协议

        SOCK_DGRAM:数据包套接字, 支持数据报(无连接,不可靠的固定最大长度的消息)例如:UDP协议

        SOCK_RAW:原始套接字, 使用原始套接字时候调用,原始套接字也就是链路层协议

        SOCK_SEQPACKET:有序分组套接字,为固定最大长度的数据报提供有序,可靠,双向连接的数据传输路径; 消费者需要利用每个输入系统调用读取整个分组

3)、Protocol:支持的协议

参数解析参考:https://blog.csdn.net/liuxingen/article/details/44995467

3、  绑定服务器套接字地址

int bind(intsockfd, const struct sockaddr *addr,socklen_t addrlen);

服务端套接字绑定自己的IP地址与端口号,客户端那边可以不写,内核会给它分配一个临时的端口。

参数:

1)、sockfd: 服务器或者客户端自己创建的socket

2)、sockaddr: 服务器或者客户端自己的地址信息(协议族、IP、端口号)

sockaddr 与sockaddr_in:

二者的占用的内存大小是一致的,因此可以互相转化,从这个意义上说,他们并无区别。

sockaddr常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息。是一种通用的套接字地址。而sockaddr_in 是internet环境下套接字的地址形式。所以在网络编程中我们会对sockaddr_in结构体进行操作。使用sockaddr_in来建立所需的信息,最后使用类型转化就可以了。

3)、socklen_t: 服务器或者客户端自己的地址信息的长度

EXP:

seraddr.sin_family = AF_INET; // 设置地址族为IPv4

seraddr.sin_port = htons(SERPORT);    //设置地址的端口号信息

seraddr.sin_addr.s_addr = inet_addr(SERADDR);   // 设置IP地址

ret= bind(sockfd, (const struct sockaddr *)&seraddr, sizeof(seraddr));

参考:https://blog.csdn.net/zz709196484/article/details/54864770

4、    将套接字设置为监听模式等待连接请求

intlisten(int  sockfd, int  backlog);

用法:函数应该在调用socket和bind这两个函数之后,accept函数之前调用。

作用:让服务器套接字sockfd进入监听状态。

参数:sockfd:套接字,成功返回后进入监听模式,当有新连接并accept后会再建立一个套接字保存新的连接;

  backlog:并发连接数,下面详细介绍此参数:

        1)  当TCP接收一个连接后(三次握手通过)会将此连接存在连接请求队列里面,并对队列个数+1,而backlog为此队列允许的最大个数,超过此值,则直接将新的连接删除,即不在接收新的连接。将这些处于请求队列里面的连接暂记为后备连接,这些都在底层自动完成,底层将连接添加到队列后等待上层来处理(一般是调用accept函数接收连接);

        2)  当上层调用accept函数接收一个连接(处于请求队列里面的后备连接),队列个数会-1;

        3)  那么这样一个加一个减,只要底层提交的速度小于上层接收的速度(一般是这样),很明显backlog就不能限制连接的个数,只能限制后备连接的个数。那为啥要用这个backlog呢?主要用于并发处理,当上层没来的及接收时,底层可以提交多个连接;

        4) backlog的取值范围 ,一般为0-5。

5、   接受连接请求,返回一个新的对应于此次连接的套接字

        accept(intsocket, sockaddr *name, int *addrlen)

        参数socket: 是一个已设为监听模式的服务器端socket的描述符。

        参数sockaddr: 是一个返回值,它指向一个struct sockaddr类型的结构体的变量,保存了发起连接的客户端得IP地址信息和端口信息。

        参数addrlen: 也是一个返回值,指向整型的变量,保存了返回的地址信息的长度。

       accept函数返回值是一个客户端和服务器连接的SOCKET类型的描述符,在服务器端标识着这个客户端。

6、   用5返回的套接字和客户端进行通信(send()/recv());

send(sockets, char * str, int len, int flag)

        第一个参数:本机创建的套接字

        第二个参数:要发送的字符串

        第三个参数:发送字符串长度

        第四个参数:会对函数行为产生影响,一般设置为0

    recv(socket s, char * buf, int len,intflag)

        参数socket:创建的可以传输消息过来的套接字

        参数buf:接受消息的字符串缓存

        参数len:允许接收字符串的缓存的最大长度

        第四个参数:会对函数行为产生影响,一般设置为0

send和recv实际上分别是write和read函数的基础上扩展了第四个参数:

1)、recv对应的flags有3个选项:

    MSG_PEEK:查看数据,并不从系统缓冲区移走数据

    MSG_WAITALL:等待所有数据,等到所有的信息到达时才返回,使用它时,recv返回一直阻塞,直到指定的条件满足时,或者发生错误

     MSG_OOB:接受或者发送带外数据

2)、send第四个参数flags,有2个选项:

    MSG_DONTROUTE:不查找表,它告诉ip,目的主机在本地网络上,没必要查找表。(一般用在网络诊断和路由程序里面)

    MSG_OOB:接受或者发送带外数据 

功能上是分别将SendBuff拷贝到发送缓冲区中和将接受缓冲区的数据拷贝到ReciveBuf中;

https://blog.csdn.net/tingyun_say/article/details/51907687

https://blog.csdn.net/rankun1/article/details/50488989

二、  客户端

1、加载套接字库,创建套接字(WSAStartup()/socket());

2、向服务器发出连接请求(connect());

3、和服务器进行通信(send()/recv());

4、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup());

三、  代码实现

环境:window7、vs2015

服务器端:

#include<iostream>

#include "winsock2.h"

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()

{

    int RetVal;

    WORD SocketVersion=MAKEWORD(2, 2);

    WSADATA wsd;

    if (WSAStartup(SocketVersion, &wsd) != 0)

    {

        cout << "绑定Socket库失败" << endl;

    }

    SOCKET ServerSocket;

    ServerSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if (ServerSocket == INVALID_SOCKET)

    {

        cout << "创建服务器套接字失败" << endl;

        WSACleanup();

        return -1;

}

SOCKADDR_IN ServerAddr;

ServerAddr.sin_family = AF_INET;

ServerAddr.sin_port = htons(2345);

ServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;

RetVal = bind(ServerSocket, (SOCKADDR *)&ServerAddr, sizeof(SOCKADDR_IN));

if (RetVal == SOCKET_ERROR)

{

    cout << "套接字绑定失败" << endl;

    closesocket(ServerSocket);

    WSACleanup();

    return -1;

}

RetVal = listen(ServerSocket,2);

if (RetVal == SOCKET_ERROR)

{

    cout << "套接字监听失败" << endl;

    closesocket(ServerSocket);

    WSACleanup();

    return -1;

}

SOCKET ClientSocket;

SOCKADDR_IN ClientAddr;

int ClientAddrLen = sizeof(ClientAddr);

ClientSocket = accept(ServerSocket, (SOCKADDR*)&ClientAddr, &ClientAddrLen);

if (ClientSocket == INVALID_SOCKET)

{

    cout << "接收客户端请求失败" << endl;

    closesocket(ServerSocket);

    WSACleanup();

    return -1;

}

char ReceiveBuff[BUFSIZ];

char SendBuff[BUFSIZ];

while (true)

{

    ZeroMemory(ReceiveBuff, BUFSIZ);

    RetVal = recv(ClientSocket, ReceiveBuff, BUFSIZ, 0);

    if (RetVal == SOCKET_ERROR)

    {

        cout << "接收数据失败" << endl;

        closesocket(ServerSocket);

        closesocket(ClientSocket);

       WSACleanup();

        return  -1;

    }

cout << "接收自客户端数据:" << ReceiveBuff << endl;

cout << "向客户端发送数据:";

cin >> SendBuff;

send(ClientSocket, SendBuff, strlen(SendBuff), 0);

}

closesocket(ServerSocket);

closesocket(ClientSocket);

WSACleanup();

return 0;

}

客户端:

#include<iostream>

#include "winsock2.h"

#pragma comment(lib,"ws2_32.lib")

using namespace std;

int main()

{

    const int BUF_SIZE = 64;

    int RetVal;

    WSADATA Wsd;

    if (WSAStartup(MAKEWORD(2, 2), &Wsd) != 0)

    {

        cout << "初始化套接字动态库失败" << endl;

        return -1;

    }

    SOCKET ServerScoket;

    ServerScoket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

    if (ServerScoket == INVALID_SOCKET)

    {

        cout << "创建套接字失败" << endl;

        WSACleanup();

        return -1;

    }

    SOCKADDR_IN ServerAddr;

    ServerAddr.sin_family = AF_INET;

    ServerAddr.sin_port = htons(2345);

    ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    RetVal = connect(ServerScoket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr));

    if (RetVal == SOCKET_ERROR)

    {

        cout << "链接服务器失败" << endl;

        closesocket(ServerScoket);

        WSACleanup();

        return -1;

    }

    char SendBuff[BUF_SIZE];

    char RECVBuff[BUF_SIZE];

    while (true)

    {

        ZeroMemory(SendBuff, BUF_SIZE);

        cout << "向服务器发送数据" << endl;

        cin >> SendBuff;

        RetVal = send(ServerScoket, SendBuff, strlen(SendBuff),0);

        if (RetVal == SOCKET_ERROR)

        {

            cout << "发送数据失败" << endl;

            closesocket(ServerScoket);

            WSACleanup();

            return -1;

        }

    ZeroMemory(RECVBuff, BUF_SIZE);

    recv(ServerScoket, RECVBuff, BUF_SIZE, 0);

    cout << endl << "从服务器接收数据:" << RECVBuff << endl;

    }

    closesocket(ServerScoket);

    WSACleanup();

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • socket通信原理 socket又被叫做套接字,它就像连接到两端的插座孔一样,通过建立管道,将两个不同的进程之间...
    jiodg45阅读 1,111评论 0 1
  • IP地址操作类 IPAddress类在该类中有一个Parse()方法,可以把点分的十进制IP转化成IPAddres...
    Lv1_Sans阅读 4,449评论 0 3
  • 1三个相关数据结构. 关于socket的创建,首先需要分析socket这个结构体,这是整个的核心。 104 str...
    ice_camel阅读 2,800评论 1 8
  • 刺眼的阳光,喧闹的茶座,暑假开始一半的七月要结束了。许久未见的闺蜜刚刚走,独自坐在长椅上的我便有些怅然若失。其实我...
    幕夏景凉阅读 260评论 0 0
  • 【导读】过年回去去了别人家,感觉与自己家有很大不同,这就好像去了不同的城市的那种感受,不知道你是不是也有这种感觉。...
    春未央阅读 457评论 0 2