您的位置:首页 > 理论基础 > 计算机网络

基于TCP的简单socket通信

2013-05-09 15:38 411 查看
前两天开始翻看UNIX网络编程,看到了一个基于TCP的socket通信实例(客户端向服务器请求当前时间),于是自己在vs上实现了下,以加深对socket通信流程的了解,代码如下:

客户端

#include <windows.h>
#include <iostream>
#include <cassert>
#include <io.h>
#include "../../Server/Server/SocketUtil.h"

#pragma comment(lib, "Ws2_32.lib")

const int ServerPort = 2013;
const char* pServerIP = "127.0.0.1";

int main()
{
//初始化winsock
WSAData wsaData;
memset(&wsaData, NULL, sizeof(WSAData));
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
RecordError("init winsock failed");
return 0;
}

SocketHolder sholder;
//创建socket
int sockFd = 0;
if ((sockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
RecordError("socket create error");
return 0;
}

sholder.SetSocketFd(sockFd);
//初始化socket地址
sockaddr_in servAddr;
//sockaddr_in最后8个字节用于占位,为了与sockaddr大小一致
memset(&servAddr, NULL, sizeof(sockaddr_in));
servAddr.sin_family = AF_INET;
/*
* htons将host字节序排列的数转化为network字节序排列的short类型(端口号是16位的)
* ServerPort=13,在本地按主机字节序存储为(按16位)0000 0000 0000 1101,
* 转化为网络字节序为0000 1101 0000 0000,为3328
*/
servAddr.sin_port = htons(ServerPort);
assert(pServerIP);
/*
* inet_pton将标准文本形式的IP地址转化为数字二进制形式
* server ip为127.0.0.1
* s_addr=1<<24 + 127=16777343,IP地址为32位
*/
servAddr.sin_addr.s_addr = inet_addr(pServerIP);

//请求连接服务器
std::cout<<"start connect ..."<<std::endl;
if (connect(sockFd, (sockaddr*)(&servAddr), sizeof(servAddr)) < 0)
{
//10061:服务器拒绝连接 10060:连接超时
RecordError("connect failed");
return 0;
}

std::cout<<"host sockfd = "<<sockFd<<std::endl;
PrintClientConnectInfo(servAddr);
std::cout<<std::endl;
std::cout<<"connect successed"<<std::endl;
int nCount = 0;
char buf[MAX_PATH] = {0};
while ((nCount = recv(sockFd, buf, sizeof(buf), 0)) > 0)
{
std::cout<<buf;
memset(buf, NULL, sizeof(buf));
}
return 0;
}


服务器端

#include <Windows.h>
#include <ctime>
#include <iostream>
#include <io.h>
#include "SocketUtil.h"

#pragma comment(lib, "Ws2_32.lib")
const int ServerPort = 2013;

int main ()
{
WSAData wsaData;
memset(&wsaData, NULL, sizeof(WSAData));
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
{
RecordError("init winsock failed");
return 0;
}

SocketHolder sholder;

int nListeningSocket = socket(AF_INET, SOCK_STREAM, 0);
if (nListeningSocket < 0)
{
RecordError("listening socket create error");
return 0;
}

sholder.SetSocketFd(nListeningSocket);

sockaddr_in serverAddr;
memset(&serverAddr, NULL, sizeof(sockaddr_in));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(ServerPort);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

int nBindResult = bind(nListeningSocket, (sockaddr*)&serverAddr, sizeof(serverAddr));
if (nBindResult != 0)
{
RecordError("server bind failed");
return 0;
}

int nListeningResult = listen(nListeningSocket, 5);
if (nListeningResult != 0)
{
RecordError("listen failed");
return 0;
}

int nConnectedSock = INVALID_SOCKET;
time_t t = 0;
char timeBuf[MAX_PATH] = {0};

std::cout<<"start server ..."<<std::endl;
while (true)
{
nConnectedSock = accept(nListeningSocket, (sockaddr*)NULL, NULL);
if (nConnectedSock == INVALID_SOCKET)
{
RecordError("accept failed");
continue;
}

sockaddr_in clientAddr;
memset(&clientAddr, NULL, sizeof(clientAddr));
int nSize = sizeof(clientAddr);
getpeername(nConnectedSock,(sockaddr *)&clientAddr,&nSize);

std::cout<<"receive a request sockfd = "<<nConnectedSock<<" ";
PrintClientConnectInfo(clientAddr);
std::cout<<std::endl;

t = time(NULL);
sprintf_s(timeBuf, sizeof(timeBuf), "%.24s\r\n", ctime(&t));
//write(nConnectedSock, timeBuf, strlen(timeBuf));
send(nConnectedSock, timeBuf, strlen(timeBuf), 0);
closesocket(nConnectedSock);
}
std::cout<<"stop server ..."<<std::endl;
return 0;
}


工具

#pragma once

void int2ipstr (const int ip, char *buf);

struct sockaddr_in;
void PrintClientConnectInfo (const sockaddr_in& clientAddr);
void RecordError(const char* pInfo);

class SocketHolder
{
public:
SocketHolder();
~SocketHolder();

void SetSocketFd(int nSockFd);

private:
int m_nSockFd;
};
#include "SocketUtil.h"
#include <Windows.h>
#include <iostream>
#include <cassert>

void int2ipstr (const int ip, char *buf)
{
sprintf_s(buf, MAX_PATH, "%u.%u.%u.%u",
(unsigned char) * ((char *) &ip + 0),
(unsigned char) * ((char *) &ip + 1),
(unsigned char) * ((char *) &ip + 2),
(unsigned char) * ((char *) &ip + 3));
}

void PrintClientConnectInfo (const sockaddr_in& clientAddr)
{
std::cout<<"port = "<<ntohs(clientAddr.sin_port)<<",";
char ip[MAX_PATH] = {0};
int2ipstr(clientAddr.sin_addr.s_addr, ip);
std::cout<<"IP ="<<" "<<ip;
}

void RecordError(const char* pInfo)
{
int errorCode = WSAGetLastError();
std::cout<<pInfo<<std::endl;
}

SocketHolder::SocketHolder()
: m_nSockFd(0)
{

}

void SocketHolder::SetSocketFd(int nSockFd)
{
assert(nSockFd);
m_nSockFd = nSockFd;
}

SocketHolder::~SocketHolder()
{
if(m_nSockFd > 0)
closesocket(m_nSockFd);
WSACleanup();
}


在windows中,客户端程序的流程是 初始化winsock库->创建socket->创建服务器地址(端口和IP)->请求连接->接收响应数据->释放资源

服务器端的流程是 初始化winsock库->创建监听socket->创建监听地址->将监听socket文件描述符与监听地址绑定->开始监听->接收请求->处理请求并响应->继续等待请求

客户端输出结果:



服务器端输出结果(客户端运行四次):



以上的程序可以成功的在客户端输出向服务器请求到的时间,但是我对服务器端getpeername(nConnectedSock,(sockaddr *)&clientAddr,&nSize)得到的客户端机器的地址值不是很理解,我本以为服务器端监听到的socket描述符(88)应该和客户端创建的socket描述符(84) 是相等的,而且端口号也应该是相等的(2013),但每次接到的请求端口号都增加1,该问题有待解决。

补充:

通过向朋友请教并查看资料,对以上提出的两个疑问有了理解。

(1)当服务器端收到一个请求后服务器会产生一个socket句柄,他和客户端创建的是不一样的,这个新产生的句柄是用来连接客户端和服务器的;

(2)服务器收到的请求的客户机的地址中端口一直在变,是因为客户端在通信时是不用绑定端口的,内核会选择一个可用端口进行通信,所以会变。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: