WinSock TCP 编程流程

  TCP通信,就像是固定电话,首先是要安装基站,然后是将电话号绑定到电话,然后拨号,接通之后说事,完事之后还要挂电话(甭管谁先挂)。

1、初始化环境

  使用函数 int WSAstartup(WORD Version,LPWSAData dtPtr);

  参数 Version 为套接字版本,dtPtr 为一个WSAData 结构的 指针,表示获取到的 套接字的详细信息。

  typedef srcut WSAData{

    WORD Version ;        //当前版本

    WORD HightestVersion;    //当前库支持的最高版本

    char szDescription[WSADESCRIPTION_LEN + 1];      //库文件描述字符串

    char szSystemStatus[WASYS_STATUS_LEN + 1];     //系统状态字符串

    unsigned short maxSockets;   //同时支持的最大套接字数目

    unsigned short maxUdpDlg;   //已弃用(目的是什么都不知道就已经弃用了……)

    char FAR * VersionIndo;     // 也是在目的都不知道的情况被弃用了……
  }

  在使用WinSockt 前必须首先对库进行初始化

  DWORD wrd = MAKEWORD(2,0);//假设版本为2.0

  WSAData data;

  int res = WSAstartup(wrd,&data);

  函数返回一个int类型的值,为0则成功,否则失败;

  if(res != 0 )

  {

    MessageBox("初始化失败!");

  }

  注:1)初始化过程可放在程序启动时实现;2)在使用结束后(一般是在程序结束时,需要使用 WSAcleanup()释放资源)

2、创建套接字

  在初始化库之后,就可以创建套接字了,WinSockt 中创建套接字使用 socket(int af,int type,int protocol);

  参数 af:地址格式:可以是 AF_INET,表示IPV4;或 AF_INET6,表示IPV6等

  参数 type:套接字类型 ,可以是 TCP :SOCK_STREAM;或 UDP:SOCK_DGRAM

  参数  protocol:如果type设定为 TCP或UDP,则设定为0

  那么创建一个基于TCP 的套接字可以写为

  SOCKET Socket = ::socket(AF_INET,SOCK_STREAM,0):

3、绑定地址信息

  创建套接字后,需要绑定到特定的地址之后,才能使用

  了么首先需要定义一个 sockaddr_in 地址结构

  sockaddr_in addr;

  ......        //省略地址结构的初始化

  使用bind函数将套接字绑定到特定的地址

  int bres = ::bind(Socket, (sockaddr*)&addr, sizeof(addr));

  同样,成功返回 0,否则绑定失败。

4、开始监听或链接

  对于服务端来说,在绑定成功后,需要监听;对于客户端来说,就可以连接到特定的服务器了。

  服务端监听:listen(SOCKET s,int flags)

  参数 s:执行监听的 套接字

  参数 flags:最多同时处理连接数,取值 1~5,默认 5

  则监听可写为

  ::listen(Socket,5);

  客户端链接:connnect(SOCKET s, sockaddr* lpAddr,int addrlen);

  参数 s:执行链接的套接字对象

  参数 lpaddr:要链接到的地址结构对象

  参数 addrlen:地址结构长度

  则链接可以写为:

  ::connnect(Socket,(sockaddr*)&addr,sizeof(addr));

5、服务端接受链接

  对于服务器来说,因为客户端需要链接到她,所以它较客户端多一步,即接收(accept)来自客户端的链接请求。

  使用函数 SOCKET s :: accept(SOCKET Lsocket ,(sokaddr*) addr,int* lpLen) 接收来自客户端的请求

  参数 Lsocket :服务端负责监听的套接字

  参数 addr:绑定到的地址结构

  参数 lpLen:地址长度指针

  返回值 s:一个新的套接字,该套接字才是真正与客户端进行链接的套接字,使用该套接字实现与客户端的通信,因为Lsocket 正忙着监听其他的链接呢,没空。

  那么接受过程可以写为:

  while (true)
    {
        int len = sizeof(addr);
        SOCKET linkSock = ::accept(Socket, (sockaddr*)&addr, &len);
    }

  为什么使用无线循环呢,这样可以保证在接受到一个链接后,还可以继续接收其他的链接请求,不然在接收到一个链接请求后,程序向下执行,而其他客户端又在请求链接,但是负责开门的人已经走了,于是乎客户端就只能在门外苦苦的等待,直到死亡……

  注:accept函数也是一个比较傻的函数,在等待客户端链接过程,如果没有客户端请求,则会一直等待下去,程序将会阻塞在 accept处不再向下执行,直到接收到一个链接请求为止。所以建议将accept放到一个单独的线程执行,否则程序将出现“假死”状态。

6、数据收发

  数据接收

  在套接字之间,使用 int ::recv(SOCKET s,char* buffer,int len ,int flags)进行接收数据

  参数 s:已经建立链接的套接字对象

  参数 buffer:接收到的数据缓存

  参数 len:欲接收的数据长度(最好和缓存区长度一致)

  参数 flags: 通常设为0

  返回值 int :表示实际接收到的字节数

  那么从对面接收数据可写为

  char buffer[512];
      int cnt = ::recv(linkSock, buffer, 512,0);

  数据发送

  套接字之间使用 int ::send(SOCKET s,char* buffer,int len,int flags)发送数据

  各参数与 recv 相同。

  发送数据可写为

  char* datas = "Hello world!\r\n";
      int scnt = ::send(linkSock, datas, sizeof(datas), 0);

  注:

    1)在服务端,负责与客户端通信的是与客户端建立链接的套接字对象,而不是负责监听的套接字。

    2)数据接收函数(recv)也是一个阻塞函数,因此建议在单独线程处理数据的收发。

    3)套接字TCP通信需要遵守“三次握手”原则,不然要出大问题的。

    4)每发送一条数据,在最后必须加上结束标识符(\r\n),否则在接收端接收的数据会出人意料的哦……

7、关闭套接字

  在数据发送完毕,不再需要链接时,需要关闭套接字。

  ::closesocket(SOCKET s);

至此,基于WinSock的TCP网络编程编程告一段落。至于在数据收发过程,根据不同的场景不同的需求可能需要遵守或制定特定的协议,但是“三次握手”是永恒不变的。

C++ Socket学习记录 -2的更多相关文章

  1. C++ Socket学习记录 -1

    1.IP的转换 1)正转换 结构 sockaddr_in 在C++ 中表明一个IP地址结构,包含地址家,端口以及IP地址等信息 如: sockaddr_in addr; addr.sin_family ...

  2. C++ Socket学习记录 -3

    WinSocket套接字之间的数据传输 在客户端和服务端建立链接后,接着就是在套接字之间传输数据. 很简单的使用 recv 接收数据,用send发送数据,单但是出现一个问题,不管接收还是发送,数据都是 ...

  3. [Python 学习]2.5版yield之学习心得 - limodou的学习记录 - limodou是一个程序员,他关心的焦点是Python, DocBook, Open Source …

    [Python 学习]2.5版yield之学习心得 - limodou的学习记录 - limodou是一个程序员,他关心的焦点是Python, DocBook, Open Source - [Pyth ...

  4. zeromq学习记录(一)最初的简单示例使用ZMQ_REQ ZMQ_REP

    阅读zeromq guide的一些学习记录 zeromq官方例子 在VC下运行会有些跨平台的错误 我这里有做修改 稍后会发布出来 相关的代码与库  http://download.zeromq.org ...

  5. Java 8 学习记录

    Java 8 学习记录 官方文档 https://docs.oracle.com/javase/8/ https://docs.oracle.com/javase/8/docs/index.html ...

  6. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  7. Java 静态内部类与非静态内部类 学习记录.

    目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...

  8. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  9. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

随机推荐

  1. accp8.0转换教材第2章初识MySQL

    首先安装MySQL: 一.单词部分: ①networking网络②option选择③port端口④firewall防火墙⑤engine引擎 ⑥standard标准⑦character字符⑧collat ...

  2. 如何判断浏览器为ie10以上

    如果针对ie10 以上单独写css样式的话,ie10以上已经不提供 <!--[if ...]><![endif]--> 这种方法去操作了,所以可以用css媒体查询的方法@med ...

  3. tcp/ip通信传输流

    利用TCP/IP协议族进行网络通信时,会通过分层顺序与对方进行通信,发送端从应用层往下走,接收端则往应用层方向走. 我们用HTTP进行举例 客户端在应用层发出想要看到某个web页面的http请求.HT ...

  4. 历年NOIP中的搜索题

    什么题目都不会做于是开始做搜索题. 然而我搜索题也不会做了. 铁定没戏的蒟蒻. 1.NOIP2004 虫食算 “对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立.输入数据 ...

  5. nodejs 构建本地web测试服务器 以及 解决访问静态资源的问题!

    直接打开html文件,是以file:///方式打开的,这种方式很多时候会遇到跨域的问题,因此我们一般会搭建一个简易的本地服务器,来运行测试页面. 一.构建静态服务器 1.使用express模块 建立个 ...

  6. JavaScript中闭包实现的私有属性的getter()和setter()方法

    注意: 以下的输出都在浏览器的控制台中 <!DOCTYPE html> <html> <head> <meta charset="utf-8&quo ...

  7. Java8 in action(1) 通过行为参数化传递代码--lambda代替策略模式

    [TOC] 猪脚:以下内容参考<Java 8 in Action> 需求 果农需要筛选苹果,可能想要绿色的,也可能想要红色的,可能想要大苹果(>150g),也可能需要红的大苹果.基于 ...

  8. for循环问题

    印象中的for语句是这样的,语法:  for (语句 1; 语句 2; 语句 3) { 被执行的代码块 }  语句 1 (代码块)开始前执行 starts. 语句 2 定义运行循环(代码块)的条件 语 ...

  9. [luogu P3787][新创无际夏日公开赛] 冰精冻西瓜 [树状数组][dfs序]

    题目背景 盛夏,冰之妖精琪露诺发现了一大片西瓜地,终于可以吃到美味的冻西瓜啦. 题目描述 琪露诺是拥有操纵冷气程度的能力的妖精,一天她发现了一片西瓜地.这里有n个西瓜,由n-1条西瓜蔓连接,形成一个有 ...

  10. luogu P3398 仓鼠找sugar [LCA]

    题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c) ...