运行环境:VS2015
基于https://blog.csdn.net/orange_xxx/article/details/7276868做出了简单修改,可以完全实现双向的数据通信;
服务器端步骤:
客户端步骤:
#include "winsock2.h"
#include
#include
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main(int argc, char* argv[])
{
const int BUF_SIZE = 64;
WSADATA wsd; //WSADATA变量
SOCKET sServer; //服务器套接字
SOCKET sClient; //客户端套接字
SOCKADDR_IN servAddr; //服务器地址
SOCKADDR_IN clientAddr; //客户端地址
char bufSend[BUF_SIZE]; //发送数据缓冲区
char bufRecv[BUF_SIZE]; //接收数据缓冲区
int retVal; //返回值
char* closeSymbol = "0"; //结束通信的标志
// 服务端套接字地址
servAddr.sin_family = AF_INET; //协议
servAddr.sin_port = htons(4999); //端口
//servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //INADDR_ANY
inet_pton(AF_INET, "127.0.0.1", (void*)&servAddr.sin_addr.S_un.S_addr);
// 初始化套接字动态库
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
cout << "WSAStartup failed !" << endl;
return 1;
}
// 创建服务端套接字
sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sServer)
{
cout << "socket failed!" << endl;
WSACleanup(); //释放套接字资源;
return -1;
}
else
{
cout << "Server Socket init!" << endl;
cout << "Server Socket IP: 127.0.0.1" << endl;
cout << "Server Socket Port: 4999" << endl;
}
// 套接字绑定IP和端口
retVal = bind(sServer, (LPSOCKADDR)&servAddr, sizeof(SOCKADDR_IN));
if (SOCKET_ERROR == retVal)
{
cout << "bind failed!" << endl;
closesocket(sServer); //关闭服务端套接字
WSACleanup(); //释放套接字资源;
return -1;
}
else
{
cout << "Server Socket bind IP & Port !" << endl;
}
// 开始监听当前套接字端口是否有数据
retVal = listen(sServer, 1);
if (0 == retVal)
{
cout << "Server Socket is listening !" << endl;
}
else if (SOCKET_ERROR == retVal)
{
cout << "listen failed!" << endl;
closesocket(sServer); //关闭服务端套接字
WSACleanup(); //释放套接字资源;
return -1;
}
else
{
cout << "One Client Socket is connecting !" << endl;
}
// 如果客户端发送请求,则接受客户端,开始从该客户端读取数据
cout << "Server Socket is waiting accpetion !" << endl;
int addrClientlen = sizeof(clientAddr);
sClient = accept(sServer, (sockaddr FAR*)&clientAddr, &addrClientlen);
if (INVALID_SOCKET == sClient)
{
cout << "accept failed!" << endl;
closesocket(sServer); //关闭服务端套接字
WSACleanup(); //释放套接字资源;
return -1;
}
else
{
cout << "Two Sockets are ready for communication !" << endl;
}
// 循环等待accept的端口发送数据,从客户端接收数据 & 向客户端发送数据
while (true) {
// 初始化缓冲空间
ZeroMemory(bufRecv, BUF_SIZE);
// 接收客户端发送的buf信息
retVal = recv(sClient, bufRecv, BUF_SIZE, 0);
if (SOCKET_ERROR == retVal)
{// 接收失败则关闭服务端客户端套接字
cout << "recv failed!" << endl;
closesocket(sServer); //关闭服务端套接字
WSACleanup(); //释放套接字资源;
return -1;
}
// 确认客户端发送的信息
bufRecv[retVal] = '\0'; // 接收的最后一位设为\0,避免烫烫的问题
cout << "Data recv from Client Socket: " << bufRecv << endl;
// 若客户端发送的数据是'0',则表示客户端想结束此次TCP通信
if (!strcmp(bufRecv, closeSymbol))
{
cout << "Client Socket wants to finish this communication" << endl;
break;
}
// 将sendBuf信息发送到客户端
cout << "Data send to Client Socket: ";
cin >> bufSend;
send(sClient, bufSend, strlen(bufSend), 0);
// 若服务端发送的数据是'0',则表示服务端想结束此次TCP通信
if (!strcmp(bufSend, closeSymbol))
{
cout << "Server Socket wants to finish this communication" << endl;
break;
}
}
// 退出
closesocket(sServer); //关闭服务端套接字
WSACleanup(); //释放套接字资源;
Sleep(5000);
return 0;
}
// Client.cpp : Defines the entry point for the console application.
#include "winsock2.h"
#include
#include
#pragma comment(lib, "ws2_32.lib")
#define _WINSOCK_DEPRECATED_NO_WARNINGS
using namespace std;
int main(int argc, char* argv[])
{
const int BUF_SIZE = 64;
WSADATA wsd; //WSADATA变量
SOCKET sClient; //客户端套接字
SOCKADDR_IN servAddr; //服务器地址
char bufSend[BUF_SIZE]; //发送数据缓冲区
char bufRecv[BUF_SIZE]; //接收数据缓冲区
int retVal; //返回值
char* closeSymbol = "0";//结束通信的标志
// 设置服务端地址
servAddr.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", (void*)&servAddr.sin_addr.S_un.S_addr);
servAddr.sin_port = htons((short)4999);
int nServAddlen = sizeof(servAddr);
// 初始化套接字动态库
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
cout << "WSAStartup failed!" << endl;
return -1;
}
// 创建服务端套接字
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sClient)
{
cout << "Socket failed !" << endl;
WSACleanup(); //释放套接字资源
return -1;
}
else
{
cout << "Client Socket init !" << endl;
}
// 客户端socket连接服务端
retVal = connect(sClient, (LPSOCKADDR)&servAddr, sizeof(servAddr));
if (SOCKET_ERROR == retVal)
{
cout << "connect failed!" << endl;
closesocket(sClient); //关闭套接字
WSACleanup(); //释放套接字资源
return -1;
}
else
{
cout << "Two Sockets are ready for communication !" << endl;
}
// 循环等待,向服务端发送数据 & 从服务端接收数据
while (true) {
// 初始化buf空间
ZeroMemory(bufSend, BUF_SIZE);
// 向服务端发送数据buf
cout << "Data send to Server Socket: ";
cin >> bufSend;
retVal = send(sClient, bufSend, strlen(bufSend), 0);
if (SOCKET_ERROR == retVal)
{
cout << "send failed!" << endl;
closesocket(sClient); //关闭服务端套接字
WSACleanup(); //释放套接字资源
return -1;
}
// 若客户端发送的数据是'0',则表示客户端想结束此次TCP通信
if (!strcmp(bufSend, closeSymbol))
{
cout << "Client Socket wants to finish this communication" << endl;
break;
}
// 从服务端接收数据bufRecv
retVal = recv(sClient, bufRecv, BUF_SIZE, 0);
bufRecv[retVal] = '\0';
cout << "Data recv from Server Socket: " << bufRecv << endl;
// 若服务端发送的数据是'0',则表示服务端想结束此次TCP通信
if (!strcmp(bufRecv, closeSymbol))
//if (bufRecv[0] == '0')
{
cout << "Server Socket wants to finish this communication" << endl;
break;
}
}
//退出
closesocket(sClient); //关闭服务端套接字
WSACleanup();
//释放套接字资源
Sleep(5000);
return 0;
}
inet_pton(AF_INET, "127.0.0.1", (void*)&servAddr.sin_addr.S_un.S_addr);这句话是替代了inet_addr的作用,vs2015里面inet_addr好像不能用了,只能用inet_pton替代,之前需要添加#include
WSAStartup(MAKEWORD(2,2),&wsaData):其中MAKEWORD(2,2)表示使用WINSOCK2版本,wsaData用来存储系统传回的关于WINSOCK的资料;
ZeroMemory(bufSend, BUF_SIZE); //端口发送数据,需要在每次交互前,把之前的buf数据清空,避免缓冲区冗余;但是不能对这个端口的接受数据的缓冲bufRecv清空;这样会造成永久性的空接受情况。
retVal = recv(sClient, bufRecv,BUF_SIZE , 0); //端口接受另一个端口数据,字符位要选择允许的满字符,这里为BUF_SIZE,确保每次都能把另一个端口的数据全都接收到;其中retVal记录了这组数据的长度;
这里需要注意,不论是客户端还是服务端,recv和send的第一个参数都是sClient,即客户端的socket。对于服务端来说,recv和send的都是请求方的socket,也就是客户端;对于客户端来说,recv和send的都是客户端自己的socket,也就是自己是作为本次通信的请求方,进行发送操作和接收操作的。
bufRecv[retVal] = '\0'; //每次接受到的数据因为没有初始化,初始结构都是“烫烫烫烫”,在接受到后,会把前面的若干位变成接受的数据,但是后面还是都为烫,因此需要这个语句将接受数据的缓冲收尾,接受有效的前面若干位数据,剔除烫烫烫烫。
最后通过输入0来结束本次通信,两个端口中任一一个发送了0,那就表示需要结束本次通信,两个进程会在5秒内自动关闭。
客户端与服务端的单个调试,可以通过事先打开一个exe,然后debug另一个分析其中的问题【我debug的是客户端】;
两个代码分别放于两个项目中,先运行启动服务端代码,然后运行客户端代码,就可以进行本机的两端通信调试。