Socket编程基本流程实践

通讯基本流程图如下所示:

Server端代码(ServerDemo.cpp):

 1 #include <WinSock2.h>
 2 #include <Windows.h>
 3 #include <iostream>
 4 #include <string>
 5 #include <sstream>
 6
 7 using namespace std;
 8
 9 #pragma comment(lib, "WS2_32.lib")
10
11 int main()
12 {
13     /*
14         WSAstartup 必须是应用程序首先调用的 Winsock 函数。它允许应用程序指定所需的
15     Windows Sockets API 的版本,获取特定 Winsock 实现的详细信息。仅当这个函数成功执行之
16     后,应用程序才能调用其他 Winsock API
17         每一个对WSAStartup的调用必须对应一个对WSACleanup的调用, 这个函数释放Winsock库。
18     */
19     WORD wVersion = MAKEWORD(2, 2);
20     WSADATA WSAData;
21     ::WSAStartup(wVersion, &WSAData);
22
23     stringstream os;
24     cout << "初始化套接字...." << endl;
25     SOCKET s;
26     s = ::socket(AF_INET, SOCK_STREAM, 0);
27     if(s == INVALID_SOCKET)
28     {
29         cout << "socket fail!" << endl;
30         goto __end;
31     }
32     sockaddr_in addr_in;
33     addr_in.sin_family = AF_INET;            //设置地址家族
34     addr_in.sin_addr.S_un.S_addr = INADDR_ANY;
35     addr_in.sin_port = htons(8080);            // 转化端口号8080到网络字节顺序,并安排它到正确的成员
36
37     // 绑定这个套节字到一个本地地址
38     cout << "绑定端口8080...." << endl;
39     if(::bind(s, (LPSOCKADDR)&addr_in, sizeof(addr_in)) == SOCKET_ERROR)
40     {
41         cout << "bind error!" << endl;
42         goto __end;
43     }
44
45     // listen 函数置套节字进入监听状态
46     os << "开始监听...." << inet_ntoa(addr_in.sin_addr) << endl;  //inet_ntoa() 将32 位的二进制数转化为字符串
47     cout << os.str() << endl;
48     if(::listen(s, 2) == SOCKET_ERROR)
49     {
50         cout << "listen error!" << endl;
51         goto __end;
52     }
53
54     SOCKET s_client;
55     sockaddr_in addr_client;
56     char szServerMsg[256] = "Hello client, this is server!";
57     int nMsgLen = sizeof(szServerMsg);
58     while(true)
59     {
60         // accept 函数用于接受到来的连接。
61         if ((s_client = ::accept(s, (LPSOCKADDR)&addr_client, &nMsgLen)) == SOCKET_ERROR)
62         {
63             cout << "accept error!" << endl;
64             goto __end;
65         }
66         os.str("");
67         os.clear();
68         os << "接收到来自" << inet_ntoa(addr_client.sin_addr) << "的连接!";
69         cout << os.str() << endl;
70         // send 函数在一个连接的套节字上发送缓冲区内的数据,返回发送数据的实际字节数。flag参数通常设置为0
71         ::send(s_client, szServerMsg, 256, 0);
72         ::closesocket(s_client);
73     }
74     ::closesocket(s);
75
76 __end:
77     ::WSACleanup();
78     system("pause");
79     return 0;
80 }

Client端代码(ClientDemo.cpp)

 1 #include <WinSock2.h>
 2 #include <Windows.h>
 3 #include <iostream>
 4 #include <string>
 5 #include <sstream>
 6
 7 #pragma comment(lib, "WS2_32.lib")
 8
 9 using namespace std;
10
11 int main()
12 {
13     WORD wVersion = MAKEWORD(2, 2);
14     WSADATA WSAData;
15     ::WSAStartup(wVersion, &WSAData);
16     SOCKET s = ::socket(AF_INET, SOCK_STREAM, 0);
17     if (s == INVALID_SOCKET)
18     {
19         cout << "socket fail!" << ::WSAGetLastError() << endl;
20         ::WSACleanup();
21         return 0;
22     }
23     sockaddr_in serverAddr;
24     // inet_addr函数转化一个"aa.bb.cc.dd"类型的IP地址字符串到长整型,它是以网络字节顺序记录的IP地址,
25     // sin_addr.S_un.S_addr指定了地址联合中的此长整型
26     serverAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
27     serverAddr.sin_family = AF_INET;
28     serverAddr.sin_port = htons(8080);
29
30     // 客户端程序在创建套节字之后,要使用 connect 函数请求与服务器连接
31     if (::connect(s, (LPSOCKADDR)&serverAddr, sizeof(sockaddr_in)))
32     {
33         cout << "connect server fail!" << endl;
34         ::WSACleanup();
35         return 0;
36     }
37
38     char buf[256];
39     // recv 函数从对方接收数据,并存储它到指定的缓冲区。flag参数通常设置为0
40     ::recv(s, buf, 256, 0);
41     stringstream os;
42     os << "从服务器接收到数据:" << buf;
43     cout << os.str() << endl;
44
45     system("pause");
46     return 0;
47 }

  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):

  1 #ifndef __TCP_CLIENT_H__
  2 #define __TCP_CLIENT_H__
  3
  4 #include <winsock2.h>
  5 #include <stdio.h>
  6 #include <iostream>
  7
  8 class CTcpClient
  9 {
 10 public:
 11     std::string m_strErrInfo;//错误信息
 12
 13     CTcpClient()
 14     {
 15         WSAData wsaData;
 16         if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0)
 17         {
 18             m_strErrInfo = "WSAStartup失败";
 19             printf(m_strErrInfo.c_str());
 20         }
 21         if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
 22         {
 23             m_strErrInfo = "WSAStartup SOCKET版本不对";
 24             printf(m_strErrInfo.c_str());
 25         }
 26     }
 27
 28     ~CTcpClient()
 29     {
 30         WSACleanup();
 31     }
 32
 33     int SendData(const char *pAddr, const char *pPort
 34         , int iSendTimeOut, int iRecvTimeOut
 35         , const char *pSendData, int nSendLen
 36         , char *pRecvData, int nRecevLen)
 37     {
 38         int iTimeOut;
 39         struct sockaddr_in addrServer;
 40         m_strErrInfo="";
 41         int nRet = 0;
 42
 43         //创建SOCKET
 44         SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
 45         do
 46         {
 47             if(sockClient == INVALID_SOCKET)
 48             {
 49                 m_strErrInfo = "socket创建失失败";
 50                 nRet = -1;
 51                 break;
 52             }
 53             //连接到服务器
 54             memset(&addrServer,0,sizeof(sockaddr_in));
 55             addrServer.sin_family = AF_INET;
 56             addrServer.sin_addr.s_addr = inet_addr(pAddr);
 57             addrServer.sin_port = htons(atoi(pPort));
 58
 59             if(connect(sockClient,(const struct sockaddr *)&addrServer,sizeof(sockaddr)) != 0)
 60             {
 61                 nRet = -2;
 62                 m_strErrInfo = "连接到服务器失败.";
 63                 break;
 64             }
 65             //设置发送超时
 66             iTimeOut = iSendTimeOut;
 67
 68             if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
 69             {
 70                 m_strErrInfo = "setsockopt失败";
 71                 nRet = -3;
 72                 break;
 73             }
 74             //设置接收超时
 75             iTimeOut = iRecvTimeOut;
 76
 77             if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
 78             {
 79                 m_strErrInfo = "setsockopt失败";
 80                 nRet = -4;
 81                 break;
 82             }
 83             //发送请求
 84             if(send(sockClient, pSendData, nSendLen * sizeof(char), 0) <= 0)
 85             {
 86                 m_strErrInfo = "发送失败.";
 87                 nRet = -5;
 88                 break;
 89             }
 90
 91             //接收服务端应答
 92             memset(pRecvData, 0, nRecevLen * sizeof(char));
 93             int rc = SOCKET_ERROR;
 94             int cnt = nRecevLen * sizeof(char);
 95
 96             while(cnt > 0)
 97             {
 98                 rc = recv(sockClient, pRecvData, nRecevLen * sizeof(char), 0);
 99
100                 if(rc == SOCKET_ERROR)
101                 {
102                     m_strErrInfo = "接收失败";
103                     nRet = -6;
104                     break;
105                 }
106                 if(rc == 0)
107                 {
108                     if(nRet <= 0)
109                     {
110                         nRet = -7;
111                         m_strErrInfo = "后台无应答";
112                     }
113                     //nRet = ( ? -7 : nRet);
114                     break;
115                 }
116                 nRet += rc;
117                 pRecvData += rc;
118                 cnt -= rc;
119             }
120
121         }while (0);
122
123         closesocket(sockClient);
124         return nRet;
125     }
126
127     int SendData(const char *pAddr, const char *pPort
128         , int iSendTimeOut, int iRecvTimeOut
129         , const char *pSendData, std::string &strRecv, int iMulRecv = 0)
130     {
131         int iRet;
132         int iTimeOut;
133         struct sockaddr_in addrServer;
134         char szRecvDataBuf[1024*64+1];
135
136         m_strErrInfo="";
137
138         //创建SOCKET
139         SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);
140         if(sockClient == INVALID_SOCKET)
141         {
142             m_strErrInfo = "socket创建失败";
143             return -1;
144         }
145
146         //连接到服务器
147         memset(&addrServer,0,sizeof(sockaddr_in));
148         addrServer.sin_family = AF_INET;
149         addrServer.sin_addr.s_addr = inet_addr(pAddr);
150         addrServer.sin_port = htons(atoi(pPort));
151         if(connect(sockClient,(const struct sockaddr *)&addrServer,sizeof(sockaddr)) != 0)
152         {
153             m_strErrInfo = "连接服务器失败";
154             goto _end;
155         }
156
157         //设置发送超时
158         iTimeOut = iSendTimeOut;
159         if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
160         {
161             m_strErrInfo = "setsockopt失败";
162             goto _end;
163         }
164         //设置接收超时
165         iTimeOut = iRecvTimeOut;
166         if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(char *)&iTimeOut,sizeof(iTimeOut))==SOCKET_ERROR)
167         {
168             m_strErrInfo = "setsockopt失败";
169             goto _end;
170         }
171
172         //发送请求
173         if(send(sockClient, pSendData, strlen(pSendData), 0) <= 0)
174         {
175             m_strErrInfo = "发送失败";
176             goto _end;
177         }
178
179         //接收服务端应答
180         strRecv = "";
181         do
182         {
183             memset(szRecvDataBuf, 0, sizeof(szRecvDataBuf));
184             iRet = recv(sockClient, szRecvDataBuf, sizeof(szRecvDataBuf)-1, 0);
185             strRecv += szRecvDataBuf;
186         } while (iRet > 0 && iMulRecv);
187         if(0 == strRecv.length())
188         {
189             m_strErrInfo = "接收失败";
190         }
191
192         //关闭SOCKET
193         closesocket(sockClient);
194         return 0;
195
196 _end:
197         closesocket(sockClient);
198         return -1;
199     }
200
201     std::string GetIPAddrByDNS(const char *pDNS)
202     {
203         //通过域名得到IP地址
204         std::string strAddr;
205         WSADATA wsadata;
206         WSAStartup(MAKEWORD(2,2),&wsadata);
207         hostent *phost=gethostbyname(pDNS);
208         if(phost)
209         {
210             in_addr addr;
211             for(int i=0;;i++)
212             {
213                 char *p=phost->h_addr_list[i];
214                 if(p==NULL)    break;
215                 memcpy(&addr.S_un.S_addr,p,phost->h_length);
216                 char* ip=inet_ntoa(addr);
217                 strAddr = ip;
218
219                 if (strAddr.length())
220                     break;
221             }
222         }
223         return strAddr;
224     }
225 };
226 #endif
时间: 2024-10-16 00:23:49

Socket编程基本流程实践的相关文章

Go语言系列(九)- Socket编程和Redis

Socket编程 一.socket编程概述 什么是socket编程? socket编程是计算机PC机器上2个程序通过一个双向的通信连接实现数据的交互,这个连接的一端就是一个socket.socket的翻译意思上还有个插座的概念,其实,也可以很形象的比喻为插座插上去了就有通电了(网络通了).socket编程其实作为UNIX系统的进程间通信机制,通常称为“套接字”,用来描述IP地址和端口的集合,在unix系统下是一个通信的句柄(文件描述符,因为UNIX下所有都是文件). UNIX socket编程的

Java TCP/UDP socket 编程流程总结

最近正好学习了一点用java socket编程的东西.感觉整体的流程虽然不是很繁琐,但是也值得好好总结一下. Socket Socket可以说是一种针对网络的抽象,应用通过它可以来针对网络读写数据.就像通过一个文件的file handler就可以都写数据到存储设备上一样.根据TCP协议和UDP协议的不同,在网络编程方面就有面向两个协议的不同socket,一个是面向字节流的一个是面向报文的. 对socket的本身组成倒是比较好理解.既然是应用通过socket通信,肯定就有一个服务器端和一个客户端.

Socket编程实践(10) --select的限制与poll的使用

select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或者使用setrlimit函数设置,但一个系统所能打开的最大数也是有限的,跟内存大小有关,可以通过cat /proc/sys/fs/file-max 查看 /**示例: getrlimit/setrlimit获取/设置进程打开文件数目**/ int main() { struct rlimit rl;

Socket编程实践(19) --Socket API封装(2)

注:这一片博客与下一篇博客<Socket编程实践(20)>合为一篇,由于代码较多,所以分为两篇,本篇为上篇,主要讲解前一篇讲解的Socket类的增强,下一篇主要讲解怎样使用这个增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)! 思想来源: 1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html 2)http://blog.csdn.net/column/details/linux66.

Socket编程实践(20) --Socket API封装(3)

注:这一片博客与上一篇博客<Socket编程实践(19)>合为一篇,由于代码较多,所以分为两篇,本篇为下篇,这一篇主要讲解怎样使用上一篇开发的增强版的Socket类(ServerSocket/ClientSocket类的实现与使用)! 思想来源: 1)http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html 2)http://blog.csdn.net/column/details/linux66.html 3)http://blo

Socket编程实践(6) --TCPNotes服务器

僵尸进程过程 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中加入 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法.解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 假设多个客户端同一时候关闭, 问题描写叙述如以下两幅图所看到的: watermark/2/text/aHR0cDovL2Jsb2cuY

Socket编程实践(6) --TCP服务端注意事项

僵尸进程处理 1)通过忽略SIGCHLD信号,避免僵尸进程 在server端代码中添加 signal(SIGCHLD, SIG_IGN); 2)通过wait/waitpid方法,解决僵尸进程 signal(SIGCHLD,onSignalCatch); void onSignalCatch(int signalNumber) { wait(NULL); } 3) 如果多个客户端同时关闭, 问题描述如下面两幅图所示: /** client端实现的测试代码**/ int main() { int s

网络协议 10 - Socket 编程:实践是检验真理的唯一标准

系列文章传送门: 网络协议 1 - 概述 网络协议 2 - IP 是怎么来,又是怎么没的? 网络协议 3 - 从物理层到 MAC 层 网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校 网络协议 5 - ICMP 与 ping:投石问路的侦察兵 网络协议 6 - 路由协议:敢问路在何方? 网络协议 7 - UDP 协议:性善碰到城会玩 网络协议 8 - TCP 协议(上):性恶就要套路深 网络协议 9 - TCP协议(下):聪明反被聪明误 ????前面一直在说各种协议,偏理论方面的知

Linux Socket编程-(转自吴秦(Tyler))

"一切皆Socket!" 话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠socket?那什么是socket?socket的类型有哪些?还有socket的基本函数,这些都是本文想介绍的.本文的主要内容如下: 1.网络中进程之间如何通信?