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. 初试spring-session

    一.简介 spring-session提供了用户会话信息管理的API和实现. 它将取代容器中的HttpSession.在没有容器会话集群方案的情况下,使得支持会话集群微不足道. 它支持在一个浏览器实例 ...

  2. usaco training 4.1.1 麦香牛块 题解

    Beef McNuggets题解 Hubert Chen Farmer Brown's cows are up in arms, having heard that McDonalds is cons ...

  3. AsyncTask onPreExecute方法用于在执行后台任务前做一些UI操作

    1.实例化 TableListsTask task = new TableListsTask(ServerIP,"ALL", MenuActivity.this);   //第三参 ...

  4. 二叉树 java实现

    class Node { private int data; // 其他数据 private int otherData; private Node left; private Node right; ...

  5. rsync随机启动脚本

    服务端 #!/bin/sh # chkconfig: # description: Saves and restores system entropy pool for \ #create by xi ...

  6. 发博客用的一些HTML

    这个世界,在发生什么? 移动光标 <p style="background: #999999; padding: 5px; font-size: 22px;">< ...

  7. nyoj_68:三点顺序(计算几何)

    题目链接 根据 AB*AC的值进行判断即可(ps,结果为0时不构成三角形) #include<iostream> #include<cstdio> #include<cs ...

  8. mybaits错误解决:There is no getter for property named 'parentId ' in class 'java.lang.String'

    在使用mybaitis传参数的时候,如果仅传入一个类型为String的参数,那么在 xml文件中应该使用_parameter来代替参数名. 比如mapper中如下方法,只有一个String值 publ ...

  9. Objective-C AVPlayer播放视频的使用与封装

    大致效果 不要介意.界面有点丑... 界面搭建 看下成员变量就知道我怎么搭建的了,这里我将video播放层的size作为参照量,对所有控件的size按照其video的size宽高进行比例缩放 @int ...

  10. iOS TextView输入长度限制 设置placeholder

    textView在使用中通常会有2个功能是最常用的 设置placeholder 限制输入长度 TYLimitedTextView刚好是为了解决这个2个问题而诞生的,下面讲解TYLimitedTextV ...