在前面的文章中有一篇讲到了命名管道通信,它是创建一根管道来进行进程之间或网络之间通信的。但是它有些缺陷,比如说效率较低等。而从这篇文章开始将介绍socket编程。socket是通过TCP,UDP,IP协议来进行通信,效率较高。本文先介绍TCP的服务端和客户端。

TCP是面向连接的,它在进行通信之前呢,需要双方先进行沟通,然后才能进行通信。而且TCP是以数据流的方式进行数据传递,会自动的进行拆包和组包的过程。所以呢,TCP的连接是比较可靠的,但是它的传输速度也因此相对较慢。接下来看下如何在代码中实现TCP的通信。

在windows中,要想进行socket网络操作,必须包含一个名叫做WinSock2.h(或者WinSock.h),如果包含的是WinSock2.h则必须在windows.h之前,否则会产生一些重定义的编译错误。包含完头文件之后,还要链接一个库文件ws2_32.lib,完成之后,我们就可以开始进行TCP服务端和客户端的编写了。

先来看服务端。首先,要进行网络操作,我们先要进行一下网络环境的初始化。WSAStartup函数就是用来初始化网络环境的。其声明如下:

int WSAStartup(  WORD wVersionRequested,       //版本号,一般使用2.2版本LPWSADATA lpWSAData  <span style="white-space:pre">        </span>//WSAData地址
);

上面函数的第二个参数,接收一个WSAData结构的指针,该结构呢,里边包含了版本号,我们传递的版本号会对该结构里边的版本号进行初始化。

初始化完成之后,我们需要创建一个socket(套接字),这个套接字相当于我们前面讲到的管道,用于客户端和服务端的连接。调用socket函数我们可以创建一个套接字,声明如下:

SOCKET socket(  int af,         //IP协议簇int type,       //套接字类型,TCP应该用SOCK_STREAMint protocol<span style="white-space:pre">   </span>  //协议
);

其实,socket也是一个内核对象,但是它没有内核对象所拥有的明显标志,安全属性。

创建好套接字后呢,我们需要告诉操作系统需要在哪个地址和端口上进行网络操作,相当于管道通信中绑定到标准输入输出口上。绑定的时候,需要有一个SOCKADDR_IN这个结构体,声明如下:

struct sockaddr_in{short              sin_family;<span style="white-space:pre"> </span>//协议簇unsigned short     sin_port;<span style="white-space:pre">   </span>//端口struct   in_addr   sin_addr;<span style="white-space:pre">    </span>//ip地址char               sin_zero[8];<span style="white-space:pre">   </span>//为了设置和SOCKADDR结构等长的补充字节
};

还有一个SOCKADDR结构和上面这个的功能完全一样,但是SOCKADDR这个结构里边只有两个成员,一个是协议簇,一个是14个字节的char数组,为了让我们更好的编写代码,于是将char数组拆解成SOCKADDR_IN 中后三个成员。初始化完端口,地址等信息后,需要调用bind函数,来完成绑定操作,声明如下:

int bind(  SOCKET s,                          //我们创建的那个socketconst struct sockaddr FAR *name,   //sockaddr结构指针int namelen                        //sockaddr长度
);

绑定之后,我们还需要调用listen函数来进行监听操作,这个操作呢,就相当于门卫一样了,如果有人来,就告诉你一声,这就是监听。该函数声明如下:

int listen(  SOCKET s,      //我们创建的socketint backlog  <span style="white-space:pre">    </span> //最大连接的队列长度
);

第二个参数backlog呢,我们一般不要给的太大,这就好比你去交电费,还要进行排队等候,如果排队的人多了,这就会给你留下不好的体验,因此随便给个10,100的就行了。
监听完成之后,我们就可以进行接收客户端的连接了,我们需要调用accept这个函数来进行接客。声明如下:

SOCKET accept(  SOCKET s,   <span style="white-space:pre">          </span>//我们监听的那个socketstruct sockaddr FAR *addr,    //我们需要传递一个sockaddr的地址,用于保存客户端的地址int FAR *addrlen  <span style="white-space:pre">      </span>//sockaddr的长度指针
);

接完客之后,我们就可以进行通信了,需要调用recv和send两个函数来进行收发数据,它们的声明如下:

int recv(  SOCKET s,         <span style="white-space:pre"> </span>//客户端的socketchar FAR *buf,    <span style="white-space:pre">  </span>//接收的缓冲区int len,        <span style="white-space:pre">    </span>//缓冲区的大小int flags       <span style="white-space:pre">    </span>//标志位,一般为0
);int send(  SOCKET s,             //客户端的socketconst char FAR *buf,  //发送数据的缓冲区int len,              //缓冲区的大小int flags             //标志位,一般为0
);

当我们传输完数据后,应该调用WSACleanup和closesocket来进行关闭网络环境和套接字。声明如下:

int  WSACleanup (void);int closesocket(  SOCKET s  <span style="white-space:pre">       </span>//要关闭的套接字
);

下面附上服务端的示例代码:

#include <stdio.h>
#include <winsock2.h> // 必须包含windwos.h之前
#include <Windows.h>#pragma comment(lib,"ws2_32.lib") #define  PORT 6000
DWORD WINAPI clientProc(LPARAM lparam)
{   SOCKET sockClient = (SOCKET)lparam;char buf[1024];while (TRUE){memset(buf, 0, sizeof(buf));// 接收客户端的一条数据 int ret = recv(sockClient, buf, sizeof(buf), 0);//检查是否接收失败if (SOCKET_ERROR == ret) {printf("socket recv failed\n");closesocket(sockClient);return -1;}// 0 代表客户端主动断开连接if (ret == 0) {printf("client close connection\n");closesocket(sockClient);return -1;}     // 发送数据ret = send(sockClient, buf, strlen(buf), 0);//检查是否发送失败if (SOCKET_ERROR == ret){printf("socket send failed\n");closesocket(sockClient);return -1;}}closesocket(sockClient);return 0;
}
bool InitNetEnv()
{// 进行网络环境的初始化操作WSADATA wsa;if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {printf("WSAStartup failed\n");return false;}return true;
}
int main(int argc, char * argv[])
{if (!InitNetEnv()){return -1;}// 初始化完成,创建一个TCP的socketSOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);//检查是否创建失败if (sServer == INVALID_SOCKET){printf("socket failed\n");return -1;}printf("Create socket OK\n");//进行绑定操作SOCKADDR_IN addrServ;addrServ.sin_family = AF_INET; // 协议簇为IPV4的addrServ.sin_port = htons(PORT); // 端口  因为本机是小端模式,网络是大端模式,调用htons把本机字节序转为网络字节序addrServ.sin_addr.S_un.S_addr = INADDR_ANY; // ip地址,INADDR_ANY表示绑定电脑上所有网卡IP//完成绑定操作int ret = bind(sServer, (sockaddr *)&addrServ, sizeof(sockaddr));//检查绑定是否成功if (SOCKET_ERROR == ret){printf("socket bind failed\n");WSACleanup(); // 释放网络环境closesocket(sServer); // 关闭网络连接return -1;}printf("socket bind OK\n");// 绑定成功,进行监听ret = listen(sServer, 10);  //检查是否监听成功if (SOCKET_ERROR == ret){printf("socket listen failed\n");WSACleanup();closesocket(sServer);return -1;}printf("socket listen OK\n");// 监听成功sockaddr_in addrClient; // 用于保存客户端的网络节点的信息int addrClientLen = sizeof(sockaddr_in);while (TRUE){//新建一个socket,用于客户端SOCKET *sClient = new SOCKET; //等待客户端的连接*sClient= accept(sServer, (sockaddr*)&addrClient, &addrClientLen);if (INVALID_SOCKET == *sClient){printf("socket accept failed\n");WSACleanup();closesocket(sServer);delete sClient;return -1;}//创建线程为客户端做数据收发CreateThread(0, 0, (LPTHREAD_START_ROUTINE)clientProc, (LPVOID)*sClient, 0, 0);}closesocket(sServer);WSACleanup();return 0;
}

接下来,看下客户端。

客户端比较简单,前面的部分和服务端都基本相同,在绑定操作上会有所差别。服务端绑定的IP地址是本机所有网卡的IP,而客户端只需要绑定一个即可,因为对客户端来说,我们只需连接指定的服务器。赋值完SOCKADDR_IN结构之后,服务端会调用bind函数,而客户端呢,需要调用connect函数,其声明如下:

int connect(  SOCKET s,                          //要进行连接的socketconst struct sockaddr FAR *name,   //SOCKADDR结构地址int namelen                        //SOKADDR大小
);

连接成功后,就可以和服务端进行通信了,调用recv和send来进行收发数据。需要注意的是,如果服务端程序先进行recv操作,则我们应该在客户端先进行send操作,若两个同时进行相同的操作的话,则会卡在当前的位置,因为recv和send都是阻塞型的函数。

当通信完之后,就可以关闭连接了。文章开头讲过,当客户端和服务端刚开始连接的时候呢,两者会先进行沟通,这个沟通需要3个步骤来完成,我们称之为3次握手,同样的关闭连接的时候,需要进行4个步骤来完成,我们称之为4次握手。如果你是粗暴型的,直接拔网线呢,它也会完成其中的两次步骤,作为应用层开发,并不需要深究其中的原理,若感兴趣,可自行查找资料。
下面附上客户端的示例代码:

#include <stdio.h>
#include <winsock2.h>
#include <Windows.h>#pragma comment(lib,"ws2_32.lib")
#define  PORT 6000int main(int argc, char * argv[])
{//初始化网络环境WSADATA wsa;if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0){printf("WSAStartup failed\n");return -1;}// 初始化完成,创建一个TCP的socketSOCKET sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (sServer == INVALID_SOCKET){printf("socket failed\n");return -1;}//指定连接的服务端信息SOCKADDR_IN addrServ;addrServ.sin_family = AF_INET;addrServ.sin_port = htons(PORT);//客户端只需要连接指定的服务器地址,127.0.0.1是本机的回环地址addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 服务器Bind 客户端是进行连接int ret = connect(sServer,(SOCKADDR*)&addrServ,sizeof(SOCKADDR));//开始连接if (SOCKET_ERROR == ret){printf("socket connect failed\n");WSACleanup();closesocket(sServer);return -1;}//连接成功后,就可以进行通信了char szBuf[1024];memset(szBuf,0,sizeof(szBuf));sprintf_s(szBuf,sizeof(szBuf),"Hello server");//当服务端是recv的时候,客户端就需要send,若两端同时进行收发则会卡在这里,因为recv和send是阻塞的ret = send(sServer, szBuf, strlen(szBuf), 0);if (SOCKET_ERROR == ret){printf("socket send failed\n");closesocket(sServer);return -1;}ret = recv(sServer, szBuf, sizeof(szBuf), 0);if (SOCKET_ERROR == ret){printf("socket recv failed\n");closesocket(sServer);return -1;}printf("%s\n",szBuf);
<span style="white-space:pre"> </span>//关闭连接closesocket(sServer);WSACleanup();return 0;
}

windows Socket编程之TCP服务端与客户端相关推荐

  1. 网络编程之TCP服务端程序开发

    TCP服务端程序开发 学习目标 能够写出TCP服务端应用程序接收和发送消息 1. 开发 TCP 服务端程序开发步骤回顾 创建服务端端套接字对象 绑定端口号 设置监听 等待接受客户端的连接请求 接收数据 ...

  2. Windows Socket编程之TCP实现大文件的传输

    前言: UDP版本可参考被人博客: http://blog.csdn.net/luchengtao11/article/details/71016222 本文所述代码工程托管在Github: http ...

  3. Linux下Socket编程之TCP Server端

    一.建模 绝大部分关于socket编程的教程总是从socket的概念开始讲起的.要知道,socket的初衷是个庞大的体系,TCP/IP只是这个庞大体系下一个很小的子集,而我们真正能用上的更是这个子集中 ...

  4. Linux下Socket编程之TCP应用

    现在,我们用前面所构建的socket类,重新设计<Linux下Socket编程之TCP Server端>中echo的服务器,然后设计客户端程序. echo服务器的工作原理很简单: 1.接收 ...

  5. 基于Linux的Socket编程之TCP全双工Server-Client聊天程序

    转载:http://blog.csdn.net/apollon_krj/article/details/53437764#0-tsina-1-58570-397232819ff9a47a7b7e80a ...

  6. 2-3 建立简易TCP服务端、客户端【socket server/client】【socket、bind、listen、accept、send、closesocket】【conect、recv】

    2-3 建立简易TCP服务端.客户端 文章目录 2-3 建立简易TCP服务端.客户端 0-前言 1-服务端简易功能 2-客户端简易功能 3-代码逻辑 4-服务端 4-1 建立socket 4-2 绑定 ...

  7. Python基于socket实现的TCP服务端

    ''' 基于socket实现的TCP服务端 '''import socket # 建立socket对象 server_socket=socket.socket(socket.AF_INET,socke ...

  8. Qt:Qt实现Winsock网络编程—TCP服务端和客户端通信(多线程)

    Qt实现Winsock网络编程-TCP服务端和客户端通信(多线程) 前言 感觉Winsock网络编程的api其实和Linux下网络编程的api非常像,其实和其他编程语言的网络编程都差不太多.博主用Qt ...

  9. Java中利用socket实现简单的服务端与客户端的通信(中级)——实现任意双向通信

    本文计划采用socket实现客户端和服务端的任意双向通信,即客户端可以随时给服务端发消息,服务端也可以随时给客户端发消息,最终结果就是一个类似与QQ的聊天软件的功能. 以下代码可以直接拷贝到Eclip ...

最新文章

  1. python实现nginx图形界面管理
  2. 关于组织参加2020年全国大学生智能汽车竞赛山东赛区比赛的通知
  3. SAP MM 库存初始化和批量扩充物料仓位
  4. java变量不声明可以直接使用吗_我们可以在不使用Java进行初始化的情况下声明最终变量吗?...
  5. 网络虚拟化有几种实现方式_停车场管理系统的防砸车功能有几种方式?如何实现?...
  6. 只要一点点力气就可以撬起重物?
  7. Servlet案例6:显示用户的上次访问时间
  8. python自动化办公入门书籍-用Python自动办公,做职场高手(完结)
  9. 解决fullgc_CMS发生FullGc分析
  10. redux源码分析之二:combineReducers.js
  11. 深测「小度人工智能音箱1S」:加冕的红外控制术,和掀起的一场智能大进化...
  12. 利用Windows自带的Certutil查看文件MD5
  13. Geforce GTX 1660Ti + Ubuntu18.04 LTS + Nvidia显卡驱动 +CUDA10 配置安装
  14. 如何在word中批量编辑所有图片的大小?
  15. Leetcode 460. LFU 缓存
  16. 2023年湖南建筑八大员(材料员)模拟真题及答案
  17. CRM系统的销售管理
  18. git删除本地代码库文件后同步到远程仓库同时删除远程仓库文件
  19. 修改内存地址 windows程序设计,王艳平
  20. 电大计算机网络形成性考核册,国家开放大学学习网电大Internet和Intranet应用形成性考核一答案...

热门文章

  1. JavaScript 懒癌患者福利
  2. python蚁群算法路径规划_使用python实现蚁群算法
  3. 科学的实践时代---2. 科学研究的目的
  4. 2021地理设计组二等奖:阜阳市区火灾消防时空格局分析及站点优化选址
  5. PPT 2007幻灯片中嵌入.swf 格式Flash 动画
  6. N76E003的IAPfor flash
  7. 首播:加入24小时倒计时,抢先播种!
  8. 百度地图api 密钥获取
  9. 深度学习基础09---回归中的相关度和决定系数
  10. php getrealpath,weblogic环境下getRealPath() called with unsafe问题的处理