利用Socket实现双机通信

  • 目的
  • 环境
  • 所需知识
  • 实验分析
  • 实验结果
  • 代码

目的

  1. 利用WinSock来实现双机通信,理解TCP状态图
  2. 要求使用WinSock编程,采用其中的TCP面向连接方式,实现文本数据的交换。

环境

  1. Windows10操作系统、DevC++
  2. 对于使用MinGW的devc++,是没有ws2_32.lib这个库文件,它对应的库文件是libws2_32.a;可以在项目属性里的链接器添加这个库文件,或者在 工具->编译选项->在连接器命令行加入以下命令 中,删除原有命令,添加 -lws2_32 命令。

所需知识

  1. 计算机网络通讯
    (1)实质:进程之间的通讯
    (2)两台计算机之间通讯的基础:主机唯一标识符(IP)、进程唯一标识符(PORT)
  2. Socket简介
    (1)Socket是支持TCP/IP 协议的网络通信的基本单元
    (2)Socket包含5种信息:连接使用的协议,本机IP,本机PORT,远地主机IP,远地主机PORT。
    (3)Socket如何标识唯一进程的:通过IP地址标识网络中的唯一主机,通过传输层协议+PORT唯一标识主机中的进程。
    (4)Socket在网络结构中的位置:位于传输层与应用层之间。本质是一个编程接口,封装传输层为应用层提供服务。

    (5)Socket架构:C/S架构,典型的客户端、服务端架构。提供数据的传输服务。
  3. windows socket函数详解

(1)函数库头文件

#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")

(2)WSAStartup 初始化Ws2_32.dll的函数

int WSAStartup(__in WORD wVersionRequested,  __out LPWSADATA lpWSAData);
//WSAStartup 函数用于初始化供进程调用的Winsock相关的dll。
//wVersionRequested标识了用户调用的Winsock的版本号。通常使用MAKEWORD来生成一个版本号,现在一般为2。
//lpWSAData指向WSADATA结构体的指针,lpWSAData返回了系统对Windows Sockets 的描述。
//如果调用成功,WSAStartup 函数返回0。否则,将返回五种错误代码之一。但绝对不能使用WSAGetLastError获取错误代码。

(3)WSACleanup 释放Ws2_32.dll的函数

int WSACleanup(void);
//该函数释放对Winsock链接库的调用。

(4)socket 创建socket的函数

SOCKET WSAAPI socket( __in int af, __in int type,__in int protocol);
//socket函数将创建指定传输服务的socket。
//af:指明地址簇类型,常用的地址簇如下,其余地址簇在Winsock2.h中定义AF_INET(IPv4)、AF_INET6(IPv6)。
//type:指明socket的类型,常见类型:SOCK_STREAM(流套接字,使用TCP协议)、SOCK_DGRAM(数据报套接字,使用UDP协议)、SOCK_RAW(原始套接字)
//protocol:指明数据传输协议,该参数取决于af和type参数的类型。
//如果不出错,socket函数将返回socket的描述符(句柄),否则,将返回INVALID_SOCKET。

(5)bind 服务端将socket与地址关联

int bind( __in SOCKET s, __in const struct sockaddr* name, __in int namelen); 
//bind函数将socket关联一个本地地址。
//s:指定一个未绑定的socket。
//name:指向sockaddr地址的指针,该结构含有IP和PORT
//namelen:参数name的字节数。
//无错误返回0,有错误返回SOCKET_ERROR。

(6)listen 服务端网络监听

int listen( __in SOCKET s, __in int backlog );
//s:socket描述符,该socket是一个未连接状态的socket
//backlog:挂起连接的最大长度,如果该值设置为SOMAXCONN,负责socket的底部服务提供商将设置该值为最大合理值。并没有该值的明确规定。
//没有错误发生将返回0,否则返回SOCKET_ERROR

(7) accept服务端connect接收

SOCKET accept(__in SOCKET s,__out struct sockaddr* addr, __in_out int* addrlen );
//accept函数将创建连接。
//s:listen函数用到的socket。
//addr:指向通信层连接实体地址的指针。addr 的格式取决于bind函数内地址簇的类型。
//addrlen:addr的长度
//如果不发生错误,accept将返回一个新的SOCKET描述符,即新建连接的socket句柄。否则,将返回INVALID_SOCKET。
//传进去的addrlen应该是参数addr的长度,返回的addrlen是实际长度。

(8)connect客户端请求服务端连接

int connect( __in SOCKET s,__in const struct sockaddr* name,__in int namelen );
//s:一个没有完成连接的socket;
//name:指向sockaddr地址的指针,该结构含有IP和PORT;
//namelen:参数name的字节数;
//返回0表示正确,否则,将返回SOCKET_ERROR。如果是阻塞式的socket连接,返回值代表了连接正常与失败。

(9)send、recv发送接收数据

int send(__in SOCKET s,__in const char* buf,__in int len,__in int flags );
int recv(__in SOCKET s,__out char* buf, __in int len, __in int flags );
//s:socket
//buf:数据buffer
//len:待发送/接收数据的长度
//flags:send(recv)函数的发送(接收)数据方式。
//send的返回值标识已发送数据的长度,这个值可能比参数len小,这也意味着数据缓冲区没有全部发出去,要进行后续处理。返回SOCKET_ERROR标识send出错。
//recv的返回值标识已接收数据的长度。如果连接已关闭,返回值将是0。返回SOCKET_ERROR标识recv出错。

(10)closesocket关闭socket

closesocket(__in SOCKET s);
//如果无错误发生,函数返回0。否则,返回SOCKET_ERROR。
  1. socket tcp建立连接,关闭连接以及点对点通讯
    (1)建立连接(三次握手)

    (2)关闭连接

    (3)点对点通信

实验分析

双机通信可以通过一台计算机上运行服务端,一台计算机上运行客户端,通过服务端与客户端通信来实现双机通信。
(1)客户端
客户端需要先初始化Ws2_32.dll的函数,然后建立socket,通过connect()函数与服务端建立连接,如果连接建立成功,客户端需要建立一个用于发送信息的线程来向服务端保证能够随时发送数据,在主线程中需要一直对服务端发送来的信息保持接收。

(2)服务端
服务端要一直等待客户端的连接请求,接受请求后,连接建成,则服务端建立一个线程用于随时向服务端发送数据,同时在主线程中还要一直对客户端发来的信息保持接收。
等待客户请求:

实验结果

首先运行服务端,然后运行客户端,在客户端指定好要连接的服务端的IP地址和端口号则可以与对应服务端连接,然后就可以进行通讯了。
下面中展示的是在我自己电脑上建立了一个服务端一个客户端,它们两个间进行通讯,实际上如果服务端与客户端分别运行在两台电脑上,它们也能成功实现通讯。

代码

服务器端

#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#pragma comment(lib, "ws2_32.lib")
#include<iostream>
using namespace std;
void* recv1(SOCKET sockConn)
{  char recvBuf[10000];memset(recvBuf, 0, sizeof(recvBuf));//接收数据while(true){int nRecv = ::recv(sockConn, recvBuf, sizeof(recvBuf), 0);cout<<endl;if(nRecv>0){cout<<"server receive:"<<recvBuf<<endl;}else break;}
}
void* send1(void* args)
{SOCKET sockClient1 = *( (SOCKET*)args );//建立套接字while(true){char buff1[10000];cin>>buff1;if(buff1 == ""){break;}int e = send(sockClient1, buff1, sizeof(buff1), 0);if(e == SOCKET_ERROR){printf("send failed");break;}cout<<"server send:"<<buff1<<endl;}
}
int main()
{WSADATA wsaData;int port = 5099;char buf[] = "Server: hello, I am a server.....";if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) //加载套接字库{printf("Failed to load Winsock");return 0;}//创建用于监听的套接字SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);SOCKADDR_IN addrSrv;addrSrv.sin_family = AF_INET;//IPV4addrSrv.sin_port = htons(port); //1024以上的端口号addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //INADDR_ANY 代表任意ip,htonl():将主机字节顺序从u_long转换为网络字节 int retVal = bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));if(retVal == SOCKET_ERROR){printf("Failed bind:%d\n", WSAGetLastError());return 0;}if(listen(sockSrv,10) ==SOCKET_ERROR){//10代表允许连接的个数printf("Listen failed:%d", WSAGetLastError());return 0;}SOCKADDR_IN addrClient;int len = sizeof(SOCKADDR);while(true){//等待客户请求到来SOCKET sockConn = accept(sockSrv, (SOCKADDR *) &addrClient, &len);if(sockConn == SOCKET_ERROR){printf("Accept failed:%d", WSAGetLastError());break;}printf("Accept client IP:[%s]\n", inet_ntoa(addrClient.sin_addr));//发送数据int iSend = send(sockConn, buf, sizeof(buf) , 0);if(iSend == SOCKET_ERROR){printf("send failed");break;}pthread_t tids[2];int ret = pthread_create( &tids[0], NULL, send1, (void*)&sockConn ); if( ret != 0 ) //创建线程成功返回0{cout << "pthread_create error:error_code=" << ret << endl;}recv1(sockConn);closesocket(sockConn);}closesocket(sockSrv);WSACleanup();system("pause");
}

客户端:

#include <WinSock2.h>
#include <stdio.h>
#include <pthread.h>
#pragma comment(lib, "ws2_32.lib")
#include<iostream>
using namespace std;
void* recv1(SOCKET sockConn)
{  char recvBuf[10000];memset(recvBuf, 0, sizeof(recvBuf));//每个字节都用0填充 //接收数据while(true){int nRecv = ::recv(sockConn, recvBuf, sizeof(recvBuf), 0);if(nRecv>0){cout<<"client receive:"<<recvBuf<<endl;} else break;}
}
void* send1(void* args)
{SOCKET sockClient1 = *( (SOCKET*)args );while(true){char buff1[10000];cin>>buff1;if(buff1 == ""){break;}int e = send(sockClient1, buff1, sizeof(buff1), 0);if(e == SOCKET_ERROR){printf("send failed");break;}cout <<"client send:"<<buff1<<endl;}}
int main()
{//加载套接字WSADATA wsaData;char buff[1024];memset(buff, 0, sizeof(buff));if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)//初始化DDL {printf("Failed to load Winsock");return 0;}SOCKADDR_IN addrSrv; //服务端地址addrSrv.sin_family = AF_INET;//IPV4 addrSrv.sin_port = htons(5099);//port addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//serverIP地址,inet_addr将点分十进制地址转换为无符号4字节的整数地址//创建客户端套接字SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);//创建指定传输服务的socket,流步套接字if(SOCKET_ERROR == sockClient){printf("Socket() error:%d", WSAGetLastError());return 0;}//向服务器发出连接请求if(connect(sockClient, (struct  sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET){printf("Connect failed:%d", WSAGetLastError());return 0;}else{    pthread_t tids[2];int ret = pthread_create( &tids[0], NULL, send1, (void*)&sockClient ); if( ret != 0 ) //创建线程成功返回0{cout << "pthread_create error:error_code=" << ret << endl;}//接收数据recv(sockClient, buff, sizeof(buff), 0);printf("%s\n", buff);}recv1(sockClient);//send1((void*)&sockClient);//关闭套接字closesocket(sockClient);WSACleanup();
}

1.1 利用Socket实现双机通信相关推荐

  1. 计算机网络实验设计-利用Socket实现双机通信

    文章目录 实验目的: 具体要求: 我的实现程度 思路 关键代码和解释 实验目的: 利用 Socket 来实现双机通信,理解 TCP 状态机图 实验内容:使用 Socket 编程,采用其中的 TCP 面 ...

  2. sock 文件方式控制宿主机_利用socket实现双机通信

    {"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],&q ...

  3. android 安卓 pc 电脑 通讯,安卓利用Socket和PC通信无法连接?

    安卓利用Socket和PC通信无法连接 已经设置了 相同代码PC端和PC端通信没有问题 会在new Socket(ip,port)这里一直堵塞: 服务器代码 public ServiceThread( ...

  4. Android利用Socket与硬件通信之智能家居APP

    Android利用Socket与硬件通信之智能家居APP - JCLovebiancheng - 博客园 在socket使用时,可以用1024-65535的端口号

  5. EChat(简易聊天项目)八、Socket实现即时通信(包括部分修改)

    利用Socket实现即时通信 在MyEclipse中写Server端 工程结构如图 SocketMessage.java 该类是一个消息类,用于表示消息是由谁发给谁的.消息内容是什么.接收时间是多少, ...

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

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

  7. Java中利用socket实现简单的服务端与客户端的通信(基础级)

    在上一篇文章中,简单的介绍了java中入门级的socket编程,简单的实现了客户端像服务器端发送数据,服务器端将数据接收并显示在控制台,没有涉及多线程.上一篇文章的链接:Java中利用socket实现 ...

  8. Java中利用socket实现简单的服务端与客户端的通信(入门级)

    Java编程中,要想要使用网络通信,就离不开Socket编程,在此对socket进行简单的介绍.首先声明,这是一个入门级的介绍,仅仅简单的实现了客户端向服务端发送数据,服务端正常的接收数据,当接收到特 ...

  9. C#中利用Socket实现网络语音通信[初级版本]

    现在时下的VOIP软件很多,比较有名的就是Skype,还有其它诸如UUcall.快门等等.它们提供的功能除了网络上的语音通话外,还可以与固定电话.手机等通话.在本篇中主要介绍利用C#实现语音通信的基本 ...

最新文章

  1. 程序员到了35 岁就要被裁员?
  2. Unity 3D 2019.3.12版本创建一个按钮,并为该按钮添加点击Click的消息响应函数以及点击按钮切换场景
  3. websocket与socket.io
  4. Android学习之图片压缩,压缩程度高且失真度小
  5. .NET基金会讨论 .NET 开源事业之路
  6. 浅谈Jfinal急速开发框架
  7. fastai学习笔记——安装
  8. 看到别人的简历,mark一下。
  9. Android事件分发小结
  10. 学校如何把表格里的成绩,让学生以二维码的方式去扫描查询呢?
  11. 前端优化,包括css,jss,img,cookie
  12. ASIC 与FPGA比较谈
  13. Chrome 安装有道画词取义插件
  14. LabVIEW哪些软件需要运行时许可
  15. 小语种翻译软件,谷歌api接口批量翻译软件
  16. kubernets eviction策略
  17. Vue2.和Vue3.生命周期的区别,对比。 详解生命周期中的每个钩子函数,setup,
  18. Java do while循环语句如何使用呢?
  19. C语言数组初始化骚操作
  20. 谱定理、瑞利熵、PCA(主成分分析)、clustering algorihtm

热门文章

  1. 雷哥带你走进Javascript
  2. windowopen怎么不打开新网页_断舍离!闲置物品怎么办?带你打开泰国二手世界新大门!...
  3. Filecoin测试网Testnet 2解析-攻击不断Interesting
  4. 零数科技汽车数据溯源与存证平台入围《2022爱分析·国央企数字化实践报告》
  5. 移动电子商务≠移动购物,前者远远大于后者
  6. Win+数字 win7快捷键
  7. Oracle IS NULL
  8. 电气器件系列五:ups电源
  9. 随笔列表 - 按时间先后顺序排列
  10. 锚框(anchor box)/先验框(prior bounding box)概念介绍及其生成