Socket编程基本流程实践
2015-05-14 17:11
169 查看
通讯基本流程图如下所示:
Server端代码(ServerDemo.cpp):
Client端代码(ClientDemo.cpp)
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):
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(2, 2); WSADATA WSAData; ::WSAStartup(wVersion, &WSAData); stringstream os; cout << "初始化套接字...." << endl; SOCKET s; s = ::socket(AF_INET, SOCK_STREAM, 0); 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); // 转化端口号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, 2) == SOCKET_ERROR) { cout << "listen error!" << endl; goto __end; } SOCKET s_client; sockaddr_in addr_client; char szServerMsg[256] = "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, 256, 0); ::closesocket(s_client); } ::closesocket(s); __end: ::WSACleanup(); system("pause"); return 0; }
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(2, 2); WSADATA WSAData; ::WSAStartup(wVersion, &WSAData); SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { cout << "socket fail!" << ::WSAGetLastError() << endl; ::WSACleanup(); return 0; } 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(8080); // 客户端程序在创建套节字之后,要使用 connect 函数请求与服务器连接 if (::connect(s, (LPSOCKADDR)&serverAddr, sizeof(sockaddr_in))) { cout << "connect server fail!" << endl; ::WSACleanup(); return 0; } char buf[256]; // recv 函数从对方接收数据,并存储它到指定的缓冲区。flag参数通常设置为0 ::recv(s, buf, 256, 0); stringstream os; os << "从服务器接收到数据:" << buf; cout << os.str() << endl; system("pause"); return 0; }
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(2,2),&wsaData) != 0) { m_strErrInfo = "WSAStartup失败"; printf(m_strErrInfo.c_str()); } if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { 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 = 0; //创建SOCKET SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0); do { if(sockClient == INVALID_SOCKET) { m_strErrInfo = "socket创建失失败"; nRet = -1; break; } //连接到服务器 memset(&addrServer,0,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)) != 0) { nRet = -2; m_strErrInfo = "连接到服务器失败."; break; } //设置发送超时 iTimeOut = iSendTimeOut; if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR) { m_strErrInfo = "setsockopt失败"; nRet = -3; break; } //设置接收超时 iTimeOut = iRecvTimeOut; if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR) { m_strErrInfo = "setsockopt失败"; nRet = -4; break; } //发送请求 if(send(sockClient, pSendData, nSendLen * sizeof(char), 0) <= 0) { m_strErrInfo = "发送失败."; nRet = -5; break; } //接收服务端应答 memset(pRecvData, 0, nRecevLen * sizeof(char)); int rc = SOCKET_ERROR; int cnt = nRecevLen * sizeof(char); while(cnt > 0) { rc = recv(sockClient, pRecvData, nRecevLen * sizeof(char), 0); if(rc == SOCKET_ERROR) { m_strErrInfo = "接收失败"; nRet = -6; break; } if(rc == 0) { if(nRet <= 0) { nRet = -7; m_strErrInfo = "后台无应答"; } //nRet = ( ? -7 : nRet); break; } nRet += rc; pRecvData += rc; cnt -= rc; } }while (0); closesocket(sockClient); return nRet; } int SendData(const char *pAddr, const char *pPort , int iSendTimeOut, int iRecvTimeOut , const char *pSendData, std::string &strRecv, int iMulRecv = 0) { int iRet; int iTimeOut; struct sockaddr_in addrServer; char szRecvDataBuf[1024*64+1]; m_strErrInfo=""; //创建SOCKET SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0); if(sockClient == INVALID_SOCKET) { m_strErrInfo = "socket创建失败"; return -1; } //连接到服务器 memset(&addrServer,0,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)) != 0) { 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), 0) <= 0) { m_strErrInfo = "发送失败"; goto _end; } //接收服务端应答 strRecv = ""; do { memset(szRecvDataBuf, 0, sizeof(szRecvDataBuf)); iRet = recv(sockClient, szRecvDataBuf, sizeof(szRecvDataBuf)-1, 0); strRecv += szRecvDataBuf; } while (iRet > 0 && iMulRecv); if(0 == strRecv.length()) { m_strErrInfo = "接收失败"; } //关闭SOCKET closesocket(sockClient); return 0; _end: closesocket(sockClient); return -1; } std::string GetIPAddrByDNS(const char *pDNS) { //通过域名得到IP地址 std::string strAddr; WSADATA wsadata; WSAStartup(MAKEWORD(2,2),&wsadata); hostent *phost=gethostbyname(pDNS); if(phost) { in_addr addr; for(int i=0;;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
相关文章推荐
- TCP网络编程流程及Socket API基本用法
- Linux下的socket编程实践(二)socket编程基本API简介
- socket编程的基本流程
- Linux下的socket编程实践(十) 基本UDP编程细节
- Socket编程实践(1) 基本概念
- linux网络编程之socket(二):C/S程序的一般流程和基本socket函数
- linux网络编程之socket(二):C/S程序的一般流程和基本socket函数
- python socket编程之基本流程
- Linux下的socket编程实践(二)socket编程基本API简介
- Socket 编程基本流程
- Linux下的socket编程实践(一) 网络基本知识以及 TCP/IP简述
- 网络应用的Socket API编程的基本调用流程
- DotNET(C#) Socket基本编程
- 基于TCP/UDP的socket编程流程
- python socket编程基本操作
- Linux Socket编程(不限Linux)基本函数用法
- 浅谈spring,基本编程模式和实现原理,以及相关操作流程
- Android网络编程实践之旅(二):基于Socket的网络通信 .
- DirectX 3D_实践之DirectX3D的基本绘制流程
- iOS网络编程实践--NSStream实现TCP Socket iPhone客户端