<网络> TCP:C/S模式设计 以及相关知识

一.服务器端Server

1.基本思路:

①加载库:WSAStartup();

②创建套接字:socket();

③选址绑定:bind():

④监听:listen();

⑤接受客户端连接:accept();

⑥收发数据:recv - send

⑦关闭套接字:closesocket();(两个)

⑧卸载库:WSACleanup();

2.代码实现:

 1 #include<iostream>
 2 #include<WinSock2.h>
 3 using namespace std;
 4 
 5 // Need to link with Ws2_32.lib
 6 #pragma comment(lib, "ws2_32.lib")
 7 
 8 int main()
 9 {
10     //1.选择一个种类:加载库
11     WORD wVersionRequested;
12     WSADATA wsaData;
13     int err;
14 
15 /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
16     wVersionRequested = MAKEWORD(2, 2);
17 
18     err = WSAStartup(wVersionRequested, &wsaData);
19     if (err != 0) {
20         /* Tell the user that we could not find a usable */
21         /* Winsock DLL.                                  */
22         printf("WSAStartup failed with error: %d\n", err);
23         return 1;
24     }
25 
26 /* Confirm that the WinSock DLL supports 2.2.*/
27 /* Note that if the DLL supports versions greater    */
28 /* than 2.2 in addition to 2.2, it will still return */
29 /* 2.2 in wVersion since that is the version we      */
30 /* requested.                                        */
31 
32     if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
33         /* Tell the user that we could not find a usable */
34         /* WinSock DLL.                                  */
35         printf("Could not find a usable version of Winsock.dll\n");
36         WSACleanup();
37         return 1;
38     }
39     else
40         printf("The Winsock 2.2 dll was found okay\n");
41 
42     //2.雇佣店长:创建套接字 socket
43     SOCKET sockListen = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
44     if(sockListen == INVALID_SOCKET)
45     {
46         WSACleanup();
47         return 1;
48     }
49 
50     //3.选址:绑定 bind
51     sockaddr_in addrServer;
52     addrServer.sin_family = AF_INET;
53     addrServer.sin_port = htons(1234);
54     addrServer.sin_addr.S_un.S_addr = INADDR_ANY;
55     
56     if(bind(sockListen,(const sockaddr *)&addrServer,sizeof(addrServer)) == SOCKET_ERROR)
57     {
58         closesocket(sockListen);
59         WSACleanup();
60         return 1;
61     }
62 
63     //4.店长开始宣传:监听 listen
64     if(SOCKET_ERROR == listen(sockListen,3))
65     {
66         closesocket(sockListen);
67         WSACleanup();
68         return 1;
69     }
70 
71     //5.把客人拉进店里 交给服务员:接受客户端连接
72     SOCKET sockWaiter = accept(sockListen,NULL,NULL);
73 
74     //6.服务员 客人:recv - send
75     char szbuf[1024] = {0};
76     while(1)
77     {
78         int nres = recv(sockWaiter,szbuf,sizeof(szbuf),0);
79         if(nres > 0)
80         {
81             cout << szbuf << endl;
82             cin >> szbuf;
83             send(sockWaiter,szbuf,sizeof(szbuf),0);
84         }
85     }
86 
87     //7.客人离开 服务员下班
88     closesocket(sockWaiter);
89 
90     //8.店长下班
91     closesocket(sockListen);
92 
93     //9.挂门:卸载库
94     WSACleanup();
95 
96     system("pause");
97     return 0;
98 }

二.客户端Client

1.基本思路:

①加载库:WSAStartup();

②创建套接字:socket();

③connect();

④收发数据:recv - send

⑤关闭套接字:closesocket();

⑥卸载库:WSACleanup();

2.代码实现:

 1 #include<iostream>
 2 #include<WinSock2.h>
 3 using namespace std;
 4 
 5 // Need to link with Ws2_32.lib
 6 #pragma comment(lib, "ws2_32.lib")
 7 
 8 int main()
 9 {
10     //1.选择一个种类:加载库
11     WORD wVersionRequested;
12     WSADATA wsaData;
13     int err;
14 
15 /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
16     wVersionRequested = MAKEWORD(2, 2);
17 
18     err = WSAStartup(wVersionRequested, &wsaData);
19     if (err != 0) {
20         /* Tell the user that we could not find a usable */
21         /* Winsock DLL.                                  */
22         printf("WSAStartup failed with error: %d\n", err);
23         return 1;
24     }
25 
26 /* Confirm that the WinSock DLL supports 2.2.*/
27 /* Note that if the DLL supports versions greater    */
28 /* than 2.2 in addition to 2.2, it will still return */
29 /* 2.2 in wVersion since that is the version we      */
30 /* requested.                                        */
31 
32     if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
33         /* Tell the user that we could not find a usable */
34         /* WinSock DLL.                                  */
35         printf("Could not find a usable version of Winsock.dll\n");
36         WSACleanup();
37         return 1;
38     }
39     else
40         printf("The Winsock 2.2 dll was found okay\n");
41 
42     //
43     SOCKET sockClient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
44     if(sockClient == INVALID_SOCKET)
45     {
46         WSACleanup();
47         return 1;
48     }
49 
50     //
51     sockaddr_in addrClient;
52     addrClient.sin_family = AF_INET;
53     addrClient.sin_port = htons(1234);
54     addrClient.sin_addr.S_un.S_addr = inet_addr("192.168.2.167");
55     
56     if(connect(sockClient,(const sockaddr *)&addrClient,sizeof(addrClient)) == SOCKET_ERROR)
57     {
58         closesocket(sockClient);
59         WSACleanup();
60         return 1;
61     }
62 
63     //
64     char szbuf[1024] = {0};
65     while(1)
66     {
67         cin >> szbuf;
68         send(sockClient,szbuf,sizeof(szbuf),0);
69 
70         int nres = recv(sockClient,szbuf,sizeof(szbuf),0);
71         if(nres > 0)
72         {
73             cout << szbuf << endl;
74         }
75     }
76 
77     //
78     closesocket(sockClient);
79 
80     //卸载库
81     WSACleanup();
82 
83     system("pause");
84     return 0;
85 }

三.TCP的相关知识:TCP 全双工通信

1.TCP的6个标志:发一次可以包含多个标志

①SYN:请求连接

②ACK:请求回复

③PSH:推送数据

④RST:重置连接

⑤FIN:结束连接

⑥URG:紧急指针

2.TCP建立连接的三次握手

①序号和确认序号:校验包是否正确收到

ESTablished:进入这个状态才可以进行通信

③TCP有重传和校验机制 不会丢包

④TCP接收时可能粘包:但不是所有粘包都需要处理 同种就不需要处理 只需处理不同种类的粘包(UDP就不会出现粘包的问题)

粘包的解决:

a.发包大小(用第一个字节) 即在包头加数据长度

b.socket短连接

3.TCP断开连接的四次挥手

①MSL:数据包最长生命周期

在windows中为30s 在TCP文档中为2min

在TIME_WAIT处要等2MSL(1min~·4min)的原因:

a.保证连接正常中止

b.允许老的重复分节在网络中消失

4.UDP和TCP的比较:

①UDP中:

当发送缓冲区<接受缓冲区:正常发送

当发送缓冲区>接受缓冲区:丢

TCP中:

当发送缓冲区<接受缓冲区:正常发送

当发送缓冲区>接受缓冲区:分片发送 不会产生丢数据的现象

②特点:

UDP:

a.面向无连接:广播 组播→1对多

b.数据报文

c.效率高

d.丢包→重传 乱序→序号标识

TCP:

a.面向连接:3握4挥→1对1

b.数据流:粘包 可拆

c.重传和校验:拥塞控制 流量控制→滑动窗口

5.慢开始与拥塞避免

①发送方维持一个叫拥塞窗口cwnd的状态变量:每收到一个ACK就+1

②当cwnd>ssthresh的时候进入拥塞避免:每经过一个RTT往返时间cwnd就+1

③当发送方判断网络出现拥塞:把慢开始门限设置为出现拥塞时发送窗口大小的一半 然后把拥塞窗口设置为1 执行慢开始算法

6.快重传和快恢复

①快重传:

当接收方在收到一个失序的报文段后就立即发出重复确认

当发送方收到连续三个重复确认ACK就立即传送对方尚未收到的报文段

②快恢复:

当发送方收到连续三个重复确认ACKssthresh为窗口cwnd的一半

cwnd设置为门限ssthresh大小(或门限ssthresh+3) 执行拥塞避免

注:如果有丢包 但是没有收到3个重复确认的ACK

比如说只收到两个 因为剩余只有两个包未发送 那么也认为发生了拥塞

将cwnd设为1开始慢启动 新门限设置为拥塞时门限的一半

这说明快重传和快恢复一定是在收到三个连续重复ACK的时候才执行的!

posted @ 2018-08-09 14:41  Aaaaaalei  阅读(607)  评论(0编辑  收藏  举报