基于TCP的socket通信

2023-11-17


 

本文转载自http://blog.csdn.net/chocolate001/article/details/6612201

基于TCP(面向连接)的socket编程,分为客户端和服务器端。

客户端的流程如下:

(1)创建套接字(socket)

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

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

(4)关闭套接字

服务器端的流程如下:

(1)创建套接字(socket)

(2)将套接字绑定到一个本地地址和端口上(bind)

(3)将套接字设为监听模式,准备接收客户端请求(listen)

(4)等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)

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

(6)返回,等待另一个客户请求。

(7)关闭套接字。

 

下面通过一个具体例子讲解一下具体的过程和相关的函数。

客户端代码,运行于vs2008

// ClientTest.cpp : 定义控制台应用程序的入口点。 
//

#include "stdafx.h" #include <stdio.h> #include <stdlib.h> #include <winsock2.h> #define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; int ret; SOCKET sClient; //连接套接字 struct sockaddr_in saServer; //服务器地址信息 char *ptr; BOOL fSuccess = TRUE; //WinSock初始化 wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本 ret = WSAStartup(wVersionRequested, &wsaData); //加载套接字库 if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //确认WinSock DLL支持版本2.2 if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2) { WSACleanup(); //释放为该程序分配的资源,终止对winsock动态库的使用 printf("Invalid WinSock version!\n"); //return 0; } //创建Socket,使用TCP协议 sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sClient == INVALID_SOCKET) { WSACleanup(); printf("socket() failed!\n"); //return 0; } //构建服务器地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序 saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //连接服务器 ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("connect() failed!\n"); closesocket(sClient); //关闭套接字 WSACleanup(); //return 0; } char sendMessage[]="ZhongXingPengYue"; ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0); if (ret == SOCKET_ERROR) { printf("send() failed!\n"); } else printf("client info has been sent!"); char recvBuf[100]; recv(sClient,recvBuf,100,0); printf("%s\n",recvBuf); closesocket(sClient); //关闭套接字 WSACleanup(); getchar(); //return 0; }
 

        第一步,加载套接字。使用WSAStartup 函数,如:ret = WSAStartup(wVersionRequested, &wsaData)。WSAStartup函数的原型为

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData)

第一参数wVersionRequested,用来指定准备加载的winsock库的版本。利用MAKEWORD(x,y)宏来赋值。x是高位字节,表示副版本号;y是低位字节,表示主版本号。MAKEWORD(2, 2)表示版本号为2.2。

第二个参数是指向WSADATA结构的指针,是一个返回值,保存了库版本的有关信息。

 

       第二步,创建套接字。使用socket函数,如:sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)。socket函数的原型为:

SOCKET socket(int af, int type, int protocol );

第一个参数,指定地址族,对于TCP/IP协议的套接字,只能为AF_INET;

第二个参数,指定socket类型,SOCK_STREAM指产生流式套接字,SOCK_DGRAM指产生数据报套接字,TCP/IP协议使用SOCK_STREAM。

第三个参数,与特定的地址家族相关的协议,TCP协议一般为IPPROTO_TCP。也可以写0,那么系统会根据地址格式和套接字类别,自动选择一个适合的协议。

如果socket创建成功,则返回一个新的SOCKET数据类型的套接字描述符;若失败,则返回INVALID_SOCKET,由此可以判断是否创建成功。

 

        第三步,连接服务器。使用connect函数,如:ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer))。connect函数函数原型为

int connect(SOCKET s, const struct sockaddr FAR* name, int namelen);

第一个参数是将在上面建立连接的那个套接字的描述符,即之前创建socket的返回值sClient。

第二个参数是要连接的服务器端的地址信息。它是一个结构体类型struct sockaddr_in ,需要在调用connect函数之前构建服务器地址信息。sockaddr_in的定义如下:

struct sockaddr_in{

short sin_family;

unsigned short sin_port;

struct in_addr sin_addr;

char sin_zero[8]

};


设置服务器端口时,用到htons函数,该函数把一个u_short类型的值从主机字节顺序转换为TCP/IP网络字节顺序,因为不同的计算机存放多字节的顺序不同(基于Intel CPU是高字节存放在低地址,低字节存放在高地址),所以网络中不同主机间通信时,要统一采用网络字节顺序。设置服务器IP地址时,使用到inet_addr函数,它是将点分十进制的IP地址的字符串转换成unsigned long型。inet_ntoa函数做相反的转换。

第三个参数是服务器端地址结构体的大小。

 

        第四步,发送。使用send函数向服务器发送数据,如:ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0)。send函数的原型为

int send(SOCKET s, const char FAR* buf, int len, int flags);

第一个参数,是一个与服务器已经建立连接的套接字。

第二个参数,指向包含要发送的数据的缓冲区的指针。

第三个参数,是所指向的缓冲区的长度。准确的说,应该是所要发送的数据的长度,因为不是缓冲区的所有数据都要同时发送。

第四个参数,它设定的值将影响函数的行为,一般将其设置为0即可。

如果发送失败,send会返回SOCKET_ERROR,由此可以判断发送是否成功。

 

        第五步,接收。使用recv函数接收服务器发过来的数据,如recv(sClient,recvBuf,100,0)。recv函数的原型为

int recv(SOCKET s, const char FAR* buf, int len, int flags);

recv函数的参数的含义和send函数参数含义差不多,只是第二个参数是指向用来保存接收数据的缓冲区的指针。recv函数的返回值应该是所接收的数据的长度,如果返回SOCKET_ERROR表示接收失败;返回0表示服务器端关闭连接。

 

       第六步,关闭socket,释放资源。使用closesocket函数关闭套接字,如closesocket(sClient);使用WSACleanup函数释放为该程序分配的资源,终止对winsock动态库的使用,如WSACleanup();
 

服务器端代码,运行于vs2008

// ServerTest.cpp : 定义控制台应用程序的入口点。
//





#include "stdafx.h" #include <stdio.h> #include <winsock2.h> #define SERVER_PORT 5208 //侦听端口 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; int ret, nLeft, length; SOCKET sListen, sServer; //侦听套接字,连接套接字 struct sockaddr_in saServer, saClient; //地址信息 char *ptr;//用于遍历信息的指针 //WinSock初始化 wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本 ret=WSAStartup(wVersionRequested, &wsaData); if(ret!=0) { printf("WSAStartup() failed!\n"); //return 0; } //创建Socket,使用TCP协议 sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (sListen == INVALID_SOCKET) { WSACleanup(); printf("socket() faild!\n"); //return 0; } //构建本地地址信息 saServer.sin_family = AF_INET; //地址家族 saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序 saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址 //绑定 ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer)); if (ret == SOCKET_ERROR) { printf("bind() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 WSACleanup(); //return 0; } //侦听连接请求 ret = listen(sListen, 5); if (ret == SOCKET_ERROR) { printf("listen() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 //return 0; } printf("Waiting for client connecting!\n"); printf("Tips: Ctrl+c to quit!\n"); //阻塞等待接受客户端连接 while(1)//循环监听客户端,永远不停止,所以,在本项目中,我们没有心跳包。 { length = sizeof(saClient); sServer = accept(sListen, (struct sockaddr *)&saClient, &length); if (sServer == INVALID_SOCKET) { printf("accept() faild! code:%d\n", WSAGetLastError()); closesocket(sListen); //关闭套接字 WSACleanup(); return 0; } char sendMessage[]="hello client"; //发送信息给客户端 send(sServer,sendMessage,strlen(sendMessage)+1,0); char receiveMessage[5000]; nLeft = sizeof(receiveMessage); ptr = (char *)&receiveMessage; while(nLeft>0) { //接收数据 ret = recv(sServer, ptr, 5000, 0); if (ret == SOCKET_ERROR) { printf("recv() failed!\n"); return 0; } if (ret == 0) //客户端已经关闭连接 { printf("Client has closed the connection\n"); break; } nLeft -= ret; ptr += ret; } printf("receive message:%s\n", receiveMessage);//打印我们接收到的消息。 } // closesocket(sListen); // closesocket(sServer); // WSACleanup(); return 0; }


 

        第一步,加载套接字库,和客户端得加载套接字一样。

 

        第二步,创建监听套接字,sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);仍然使用的是socket函数。

 

        第三步,绑定。使用bind函数,该函数的作用是将一个创建好的套接字绑定到本地的某个地址和端口上,该函数的原型为:

int bind(SOCKET s, const struct sockaddr FAR* name, int namelen);

第一个参数,指定要绑定的套接字;

第二个参数,指定该套接字的地址信息,这里即服务器的地址信息,它仍是指向struct sockaddr_in类型的结构体的指针。这个结构体和客户端调用connect函数之前构建服务器地址信息的一样。其中INADDR_ANY 是指示任意地址,因为服务器含有可能多个网卡,可能有多个IP地址,这边指选择一个任意可用的地址。

第三个参数,地址的信息的长度。

 

        第四步,监听连接。使用listen函数,该函数是将指定的套接字设置为监听模式,如ret = listen(sListen, 5)。函数原型为

int listen(SOCKET s, int backlog);


第一个参数,是要设置为监听的套接字描述符。

第二个参数,是等待连接队列的最大的长度。注意了,设置这个值是为了设置等待连接队列的最大长度,而不是在一个端口上可以连接的最大数目。例如,设置为5,当有6个连接请求同时到达,前面5个连接请求会被放到等待请求连接队列中,然后服务器依次处理这些请求服务,但是第6个连接请求会被拒绝。

 

       第五步,接受客户端的连接请求。使用accept函数接受客户端发送的连接请求,如sServer = accept(sListen, (struct sockaddr *)&saClient, &length);该函数的原型为

SOCKET accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);


第一个参数,是一个已设为监听模式的socket的描述符。

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

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

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

      

       第六、七步是发送和接收,分别使用send和recv函数,这里和客户端的一样,不再重复。

       accept函数是放在一个死循环中的,一直监听客户的请求。当服务器关闭时要关闭所有的套接字,和释放资源。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于TCP的socket通信 的相关文章

  • 关闭子进程中打开的套接字

    我有一个 SIP 服务器 守护进程 它正在侦听 tcp 套接字 5060 现在 在这个父进程中 我创建一个子进程并在子进程中执行一些操作 现在 当我在父进程中关闭此 tcp 套接字并尝试再次创建 假设我在此服务器上禁用和启用 SIP 时 会
  • 使用 OTP 原理的非阻塞 TCP 服务器

    我开始学习 Erlang 所以我尝试写 hello world 并发编程 IRC 机器人 我已经使用 Erlang 编写了一个 没有任何 OTP 细节 管理程序 应用程序等行为 我希望使用 OTP 原则重写它 但不幸的是我无法找出使用 OT
  • 被释放的指针未分配[Swift]

    我正在尝试读取 TCP 套接字连接中的长字符串 对于读取短长度字符串 它工作正常 但是当我尝试发送长长度的 base64 编码图像时 它崩溃了 我尝试增加到maxReadLength 10000 但仍然不起作用 读取传入消息 private
  • 文件上传流从哪里获取内容?

    我有一个关于文件上传的问题 这与它的工作原理更相关 而不是代码问题 我在互联网上查看 但找不到正确的答案 我有一个在 tomcat 上运行的 Web 应用程序 它处理文件上传 通过 servlet 假设我现在想要上传大文件 gt 1 GB
  • 将进程附加到远程 PC 上正在运行的进程

    我正在开发一个 C 应用程序 该应用程序在远程 PC 上运行 我有 VPN 连接并使用 RDP 会话 我可以登录到该计算机 在那台计算机上 在正在运行的进程旁边 还有两个 msvsmon exe 进程 在任务管理器中具有以下 命令行 条目
  • 如何在.NET中创建HTTP请求侦听器Windows服务

    我想创建充当 HTTP 侦听器并可以处理大约 500 个客户端的 Windows 服务 这种服务有什么特别的考虑吗 我对 HTTPListener 类和 TCPListener 类有点困惑 将哪一个用于 Windows 服务将 接受客户端连
  • Indy TCP - 循环读取数据

    TCP 服务器每 8 毫秒连续发送一次数据帧 我想编写一个能够接收这些数据帧的客户端 Indy 9 中是否有任何程序可以知道缓冲区中是否有可用数据 我当前的程序如下 我正在使用线程 procedure TThreadRead Execute
  • WebSocket 和纯 TCP 之间的根本区别是什么?

    我读过关于WebSockets http en wikipedia org wiki Web Sockets我想知道为什么浏览器不能像任何其他桌面应用程序一样简单地打开简单的 TCP 连接并与服务器通信 为什么可以通过 websocket
  • WCF TCP 客户端 - 如何使用它们的基本指南?

    我有一个 WCF 服务并希望使用 TCP 绑定连接到它 这一切都很好 但是你应该如何处理客户呢 我注意到 如果您为每个调用创建一个新客户端 它不会重新使用该通道 并会留下一堆 TCP 连接 直到超时 创建客户端 调用其方法 然后关闭它是正常
  • 如何识别用户空间和内核空间之间的特定套接字?

    我在用户空间中有一个库 可以拦截套接字层调用 例如socket connect accept 等等 我只处理 TCP 套接字 在内核空间中 我有一个网络内核模块 它处理所有 TCP 连接 我需要能够在驱动程序中识别哪些套接字被用户空间库拦截
  • Boost ASIO:服务器如何知道客户端是否仍然连接?

    我在用boost asio对于服务器 客户端应用程序 服务器一次只接受一个连接 我想知道服务器验证客户端是否仍然连接的最佳方法是什么 这样做的目的是我希望能够知道客户端是否崩溃 以便我可以重新开始侦听新的连接尝试 在我的应用程序中 我使用以
  • 为什么我无法发送这个IP数据包?

    我正在尝试使用 C 发送 IP 数据包 destAddress IPAddress Parse 192 168 0 198 destPort 80 Create a raw socket to send this packet rawSoc
  • Socat未关闭tcp连接

    I use socat 1 7 3 1 r0并在alpine 3 3linux服务器 socat d d d PTY link dev ttyFOOBAR echo 0 raw unlink close 0 TCP LISTEN 7000
  • 是否可以通过 TCP 连接到正在侦听 3G 网络端口的 iPhone?

    我正在开发一个严重依赖 P2P 的应用程序 但我目前没有任何 SIM 卡可供实验 因此我正在 wifi 网络上进行测试 我想知道 3G 网络上的 iPhone 是否可以连接以及是否需要穿越 NAT 设备 您位于提供商的路由器后面 您的 IP
  • Socket ReceiveAsync 合并数据包

    我打算通过套接字接收数据包 但由于它们是从发送方以高频率发送的 因此其中许多数据包被打包成一个byte array SocketAsyncEventArgs Buffer然后保存多个数据包 即使它们是单独发送的 使用验证wireshark
  • 在 Perl 中如何接受多个 TCP 连接?

    我对 Linux 的 Perl 脚本有疑问 它的主要目的是成为 3 个应用程序之间的中间人 它应该做什么 它应该能够等待 UDP 文本 不带空格 udp port 当它收到 UDP 文本时 它应该将其转发到连接的 TCP 客户端 问题是我的
  • Web 服务器可以处理多少个套接字连接?

    假设我要获得共享 虚拟或专用托管 我在某处读到服务器 计算机一次只能处理 64 000 个 TCP 连接 这是真的吗 无论带宽如何 任何类型的托管可以处理多少个 我假设 HTTP 通过 TCP 工作 这是否意味着只有 64 000 个用户可
  • PHP 上的多个 TCP 套接字请求

    是否可以使用 PHP 上的套接字服务器接受多个请求 并行 如果可以的话 怎样做 普通的 PHP 脚本无法接收多个请求 但如果你真的计划创建一个套接字服务器 作为 cmdline php 脚本启动 那么是的 这是可能的 调查http pear
  • 是否可以通过互联网在两个移动设备 (iPhone) 之间连接套接字?

    是否可以通过互联网在两个移动设备 iPhone 之间连接套接字 我正在尝试发现每个设备的IP并直接连接 我知道可以使用 Bonjour 来完成 但这只适用于本地网络 我需要通过互联网在两个设备之间建立高速连接 Thanks 如果你有两个 I
  • 在 Python 中通过 TCP 套接字发送文件

    我已经成功地将文件内容 图像 复制到新文件 然而 当我通过 TCP 套接字尝试同样的事情时 我遇到了问题 服务器循环未退出 客户端循环在到达 EOF 时退出 但服务器无法识别 EOF 这是代码 Server import socket Im

随机推荐