Socket编程基本流程实践
通讯基本流程图如下所示:
Server端代码(ServerDemo.cpp):
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <sstream> using namespace std; #pragma comment(lib, "WS2_32.lib") int main()
{
/*
WSAstartup 必须是应用程序首先调用的 Winsock 函数。它允许应用程序指定所需的
Windows Sockets API 的版本,获取特定 Winsock 实现的详细信息。仅当这个函数成功执行之
后,应用程序才能调用其他 Winsock API
每一个对WSAStartup的调用必须对应一个对WSACleanup的调用, 这个函数释放Winsock库。
*/
WORD wVersion = MAKEWORD(, );
WSADATA WSAData;
::WSAStartup(wVersion, &WSAData); stringstream os;
cout << "初始化套接字...." << endl;
SOCKET s;
s = ::socket(AF_INET, SOCK_STREAM, );
if(s == INVALID_SOCKET)
{
cout << "socket fail!" << endl;
goto __end;
}
sockaddr_in addr_in;
addr_in.sin_family = AF_INET; //设置地址家族
addr_in.sin_addr.S_un.S_addr = INADDR_ANY;
addr_in.sin_port = htons(); // 转化端口号8080到网络字节顺序,并安排它到正确的成员 // 绑定这个套节字到一个本地地址
cout << "绑定端口8080...." << endl;
if(::bind(s, (LPSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
{
cout << "bind error!" << endl;
goto __end;
} // listen 函数置套节字进入监听状态
os << "开始监听...." << inet_ntoa(addr_in.sin_addr) << endl; //inet_ntoa() 将32 位的二进制数转化为字符串
cout << os.str() << endl;
if(::listen(s, ) == SOCKET_ERROR)
{
cout << "listen error!" << endl;
goto __end;
} SOCKET s_client;
sockaddr_in addr_client;
char szServerMsg[] = "Hello client, this is server!";
int nMsgLen = sizeof(szServerMsg);
while(true)
{
// accept 函数用于接受到来的连接。
if ((s_client = ::accept(s, (LPSOCKADDR)&addr_client, &nMsgLen)) == SOCKET_ERROR)
{
cout << "accept error!" << endl;
goto __end;
}
os.str("");
os.clear();
os << "接收到来自" << inet_ntoa(addr_client.sin_addr) << "的连接!";
cout << os.str() << endl;
// send 函数在一个连接的套节字上发送缓冲区内的数据,返回发送数据的实际字节数。flag参数通常设置为0
::send(s_client, szServerMsg, , );
::closesocket(s_client);
}
::closesocket(s); __end:
::WSACleanup();
system("pause");
return ;
}
Client端代码(ClientDemo.cpp)
#include <WinSock2.h>
#include <Windows.h>
#include <iostream>
#include <string>
#include <sstream> #pragma comment(lib, "WS2_32.lib") using namespace std; int main()
{
WORD wVersion = MAKEWORD(, );
WSADATA WSAData;
::WSAStartup(wVersion, &WSAData);
SOCKET s = ::socket(AF_INET, SOCK_STREAM, );
if (s == INVALID_SOCKET)
{
cout << "socket fail!" << ::WSAGetLastError() << endl;
::WSACleanup();
return ;
}
sockaddr_in serverAddr;
// inet_addr函数转化一个"aa.bb.cc.dd"类型的IP地址字符串到长整型,它是以网络字节顺序记录的IP地址,
// sin_addr.S_un.S_addr指定了地址联合中的此长整型
serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(); // 客户端程序在创建套节字之后,要使用 connect 函数请求与服务器连接
if (::connect(s, (LPSOCKADDR)&serverAddr, sizeof(sockaddr_in)))
{
cout << "connect server fail!" << endl;
::WSACleanup();
return ;
} char buf[];
// recv 函数从对方接收数据,并存储它到指定的缓冲区。flag参数通常设置为0
::recv(s, buf, , );
stringstream os;
os << "从服务器接收到数据:" << buf;
cout << os.str() << endl; system("pause");
return ;
}
TCP 协议由于可靠、稳定的特征而被用在大部分场合,但它对系统资源要求比较高。UDP
协议是一个简单的面向数据报的传输层协议,又叫用户数据报协议。它提供了无连接的、不可
靠的数据传输服务。无连接是指他不像 TCP 协议那样在通信前先与对方建立连接以确定对方
的状态。不可靠是指它直接安装指定 IP 地址和端口号将数据包发出去,如果对方不在线的话
数据就可能丢失。UDP 协议编程流程如下:
1.服务器端
(1)创建套节字(socket) 。
(2)绑定 IP 地址和端口(bind) 。
(3)收发数据(sendto/recvfrom) 。
(4)关闭连接 (closesocket) 。
2.客户端
(1)创建套节字(socket) 。
(2)收发数据(sendto/recvfrom) 。
(3)关闭连接(closesocket) 。
UDP 协议用于发送和接收数据的函数是 sendto 和 recvfrom。它们的原形如下。
int sendto (
SOCKET s, // 用来发送数据的套节字
const char FAR * buf, // 指向发送数据的缓冲区
int len, // 要发送数据的长度
int flags, // 一般指定为0
const struct sockaddr * to, // 指向一个包含目标地址和端口号的sockaddr_in结构
int tolen // 为 sockaddr_in 结构的大小
);
同样 UDP 协议接收数据也需要知道通信对端的地址信息。
int recvfrom (SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen);
这个函数不同于 recv 函数的是多出来的最后两个参数,from 参数是指向 sockaddr_in 结
构的指针,函数在这里返回数据发送方的地址,fromlen 参数用于返回前面的 sockaddr_in 结构
的长度。
与 TCP 协议相比,UDP 协议简单多了,编程细节就不详细介绍了。
TCPClient封装类(tcpClient.hpp):
#ifndef __TCP_CLIENT_H__
#define __TCP_CLIENT_H__ #include <winsock2.h>
#include <stdio.h>
#include <iostream> class CTcpClient
{
public:
std::string m_strErrInfo;//错误信息 CTcpClient()
{
WSAData wsaData;
if(WSAStartup(MAKEWORD(,),&wsaData) != )
{
m_strErrInfo = "WSAStartup失败";
printf(m_strErrInfo.c_str());
}
if(LOBYTE(wsaData.wVersion) != || HIBYTE(wsaData.wVersion) != )
{
m_strErrInfo = "WSAStartup SOCKET版本不对";
printf(m_strErrInfo.c_str());
}
} ~CTcpClient()
{
WSACleanup();
} int SendData(const char *pAddr, const char *pPort
, int iSendTimeOut, int iRecvTimeOut
, const char *pSendData, int nSendLen
, char *pRecvData, int nRecevLen)
{
int iTimeOut;
struct sockaddr_in addrServer;
m_strErrInfo="";
int nRet = ; //创建SOCKET
SOCKET sockClient = socket(AF_INET,SOCK_STREAM,);
do
{
if(sockClient == INVALID_SOCKET)
{
m_strErrInfo = "socket创建失失败";
nRet = -;
break;
}
//连接到服务器
memset(&addrServer,,sizeof(sockaddr_in));
addrServer.sin_family = AF_INET;
addrServer.sin_addr.s_addr = inet_addr(pAddr);
addrServer.sin_port = htons(atoi(pPort)); if(connect(sockClient,(const struct sockaddr *)&addrServer,sizeof(sockaddr)) != )
{
nRet = -;
m_strErrInfo = "连接到服务器失败.";
break;
}
//设置发送超时
iTimeOut = iSendTimeOut; if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
{
m_strErrInfo = "setsockopt失败";
nRet = -;
break;
}
//设置接收超时
iTimeOut = iRecvTimeOut; if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
{
m_strErrInfo = "setsockopt失败";
nRet = -;
break;
}
//发送请求
if(send(sockClient, pSendData, nSendLen * sizeof(char), ) <= )
{
m_strErrInfo = "发送失败.";
nRet = -;
break;
} //接收服务端应答
memset(pRecvData, , nRecevLen * sizeof(char));
int rc = SOCKET_ERROR;
int cnt = nRecevLen * sizeof(char); while(cnt > )
{
rc = recv(sockClient, pRecvData, nRecevLen * sizeof(char), ); if(rc == SOCKET_ERROR)
{
m_strErrInfo = "接收失败";
nRet = -;
break;
}
if(rc == )
{
if(nRet <= )
{
nRet = -;
m_strErrInfo = "后台无应答";
}
//nRet = ( ? -7 : nRet);
break;
}
nRet += rc;
pRecvData += rc;
cnt -= rc;
} }while (); closesocket(sockClient);
return nRet;
} int SendData(const char *pAddr, const char *pPort
, int iSendTimeOut, int iRecvTimeOut
, const char *pSendData, std::string &strRecv, int iMulRecv = )
{
int iRet;
int iTimeOut;
struct sockaddr_in addrServer;
char szRecvDataBuf[*+]; m_strErrInfo=""; //创建SOCKET
SOCKET sockClient = socket(AF_INET,SOCK_STREAM,);
if(sockClient == INVALID_SOCKET)
{
m_strErrInfo = "socket创建失败";
return -;
} //连接到服务器
memset(&addrServer,,sizeof(sockaddr_in));
addrServer.sin_family = AF_INET;
addrServer.sin_addr.s_addr = inet_addr(pAddr);
addrServer.sin_port = htons(atoi(pPort));
if(connect(sockClient,(const struct sockaddr *)&addrServer,sizeof(sockaddr)) != )
{
m_strErrInfo = "连接服务器失败";
goto _end;
} //设置发送超时
iTimeOut = iSendTimeOut;
if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
{
m_strErrInfo = "setsockopt失败";
goto _end;
}
//设置接收超时
iTimeOut = iRecvTimeOut;
if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
{
m_strErrInfo = "setsockopt失败";
goto _end;
} //发送请求
if(send(sockClient, pSendData, strlen(pSendData), ) <= )
{
m_strErrInfo = "发送失败";
goto _end;
} //接收服务端应答
strRecv = "";
do
{
memset(szRecvDataBuf, , sizeof(szRecvDataBuf));
iRet = recv(sockClient, szRecvDataBuf, sizeof(szRecvDataBuf)-, );
strRecv += szRecvDataBuf;
} while (iRet > && iMulRecv);
if( == strRecv.length())
{
m_strErrInfo = "接收失败";
} //关闭SOCKET
closesocket(sockClient);
return ; _end:
closesocket(sockClient);
return -;
} std::string GetIPAddrByDNS(const char *pDNS)
{
//通过域名得到IP地址
std::string strAddr;
WSADATA wsadata;
WSAStartup(MAKEWORD(,),&wsadata);
hostent *phost=gethostbyname(pDNS);
if(phost)
{
in_addr addr;
for(int i=;;i++)
{
char *p=phost->h_addr_list[i];
if(p==NULL) break;
memcpy(&addr.S_un.S_addr,p,phost->h_length);
char* ip=inet_ntoa(addr);
strAddr = ip; if (strAddr.length())
break;
}
}
return strAddr;
}
};
#endif
Socket编程基本流程实践的更多相关文章
- Socket编程简介
目录 背景 基础 流程 参考 本文系读书笔记,非深入研究,也无代码,如非所需,请见谅. 哦,这里有份不错的:Linux的SOCKET编程详解 背景 花了好久的时间(大约一周,我太垃圾)看完了一篇英文文 ...
- Go语言系列- Socket编程和Redis
Socket编程 一.socket编程概述 什么是socket编程? socket编程是计算机PC机器上2个程序通过一个双向的通信连接实现数据的交互,这个连接的一端就是一个socket.socket的 ...
- C# socket编程实践
C# socket编程实践——支持广播的简单socket服务器 在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# ...
- Socket编程实践(10) --select的限制与poll的使用
select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...
- Socket编程实践(6) --TCP服务端注意事项
僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 sign ...
- Socket编程实践(6) --TCPNotes服务器
僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 sign ...
- Socket编程实践(2) Socket API 与 简单例程
在本篇文章中,先介绍一下Socket编程的一些API,然后利用这些API实现一个客户端-服务器模型的一个简单通信例程.该例子中,服务器接收到客户端的信息后,将信息重新发送给客户端. socket()函 ...
- Socket编程实践(1) 基本概念
1. 什么是socket socket可以看成是用户进程与内核网络协议栈的编程接口.TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间.用户空间的程序需要通 ...
- C# socket编程实践——支持广播的简单socket服务器
在上篇博客简单理解socket写完之后我就希望写出一个websocket的服务器了,但是一路困难重重,还是从基础开始吧,先搞定C# socket编程基本知识,写一个支持广播的简单server/clie ...
随机推荐
- C# 特殊处理使用方法
1.时间处理 Model.PiDaiTime.ToString("yyyyMMdd") == "00010101" ? DateTime.Now.ToStrin ...
- HTTP 头部
通用头域(即通用头) 通用头域包含请求和响应消息都支持的头域,通用头域包含Cache-Control. Connection.Date.Pragma.Transfer-Encoding.Upgra ...
- fragment的生命周期及其各个周期方法的作用
先上生命周期图: Fragment的生命周期图: 与Activity的生命周期对比图: 由于Fragment是嵌在Activity中使用的,故其生命周期也是依赖于Activity的周期的,或者说Fra ...
- hibernate连接查询
Hibernate的HQL语言类似于SQL语言,更适合于Java面向对象的思想. 类与数据库映射好了,不必考虑数据库. 实现Class1的表与Class2的表的联合查询: Class1的class2属 ...
- (译)如何优化cocos2d程序的内存使用和程序大小:第二部分(完)
前言:从上周发布教程的微博反应情况来看,cocos2der们对于游戏的内存问题还是非常关心的.本文是上一篇博文的续,旨在教大家如何减少cocos2d程序的大小. 全文如下: 减少你的程序的大小 把纹理 ...
- RHEL5.8使用yum安装应用时包冲突的处理
RHEL5.8使用yum安装应用时包冲突的处理办法记录,总体思路是如果发现包冲突的情形,那么就把冲突的包给删除掉,然后再重新使用yum安装即可. 冲突示例: Transaction Check Err ...
- SQLServer 2012之AlwaysOn —— 指定数据同步链路,消除网络抖动导致的提交延迟问题
事件起因:近期有研发反应,某数据库从08切换到12环境后,不定期出现写操作提交延迟的问题: 事件分析:在排除了系统资源争用等问题后,初步分析可能由于网络抖动导致同步模式alwayson节点经常出现会话 ...
- mongoDB研究笔记:复制集数据同步机制
http://www.cnblogs.com/guoyuanwei/p/3279572.html 概述了复制集,整体上对复制集有了个概念,但是复制集最重要的功能之一数据同步是如何实现的?带着这个问题 ...
- .NET Socket服务编程之-高效连接接入编
在.NET上编写网络服务深入都有2,3年了,而这些时间时如何在.NET里实现网络服务积累了一些经验.在接下来的时间里会把这方面的经验通过博客的方式分享出来.而这一章主要是讲解在如果提高服务连接接入的效 ...
- C++11 并发指南五(std::condition_variable 详解)
前面三讲<C++11 并发指南二(std::thread 详解)>,<C++11 并发指南三(std::mutex 详解)>分别介绍了 std::thread,std::mut ...