标签:
TCP和UDP属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它事先为要发送的数据开辟好连接通道(三次握手),然后再进行数据发送;而UDP则不为IP提供可靠性,一般用于实时的视频流传输,像rtp、rtsp就是建立在udp的基础上的。
首先谈谈tcp socket
tcp简单的三次握手过程如图,
SYN(Synchronize Sequence Numbers):同步标志
ACK(Acknowledgement Number) :确认标志
图中可以看出,三次握手的过程是在c的connect()和s的bind()、listen()、accept()函数中完成的,这样开辟了相对可靠的连接通道,来传输数据。
UDP的socket编程过程如下图所示:
下面翠花上代码啦!
服务端:
- #include <stdio.h>
- #include <Winsock2.h> //windows socket的头文件
-
- #pragma comment( lib, "ws2_32.lib" )// 链接Winsock2.h的静态库文件
-
- void main()
- {
-
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 )
- {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )
- {
- WSACleanup( );
- return;
- }
-
- SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
-
- SOCKADDR_IN addrSrv;
- addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
-
-
- addrSrv.sin_family=AF_INET;
- addrSrv.sin_port=htons(4000);
-
- bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
-
- listen(sockSrv,5);
-
- SOCKADDR_IN addrClient;
- int len=sizeof(SOCKADDR);
-
- while(1)
- {
- SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
-
- char sendBuf[50];
- sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(addrClient.sin_addr));
- send(sockConn,sendBuf,strlen(sendBuf)+1,0);
-
- char recvBuf[50];
- recv(sockConn,recvBuf,50,0);
- printf("%s\n",recvBuf);
-
- closesocket(sockConn);
- Sleep(2000);
- }
- WSACleanup();
- }
客户端:
- #include <stdio.h>
- #include <Winsock2.h>
-
- #pragma comment( lib, "ws2_32.lib" )
-
-
- void main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 )
- {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )
- {
- WSACleanup( );
- return;
- }
- for(int index=0;;index++)
- {
- SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
-
- SOCKADDR_IN addrClt;
- addrClt.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");
- addrClt.sin_family=AF_INET;
- addrClt.sin_port=htons(4000);
-
- connect(sockClient,(SOCKADDR*)&addrClt,sizeof(SOCKADDR));
- char recvBuf[50];
- recv(sockClient,recvBuf,50,0);
- printf("my reply is : %s\n",recvBuf);
-
- char sendBuf[50];
- sprintf(sendBuf,"%3d,",index);
- strcat(sendBuf,"server node of: yaopeng");
- send(sockClient,sendBuf,strlen(sendBuf)+1,0);
-
- closesocket(sockClient);
- Sleep(2000);
- }
- WSACleanup();
- }
对于tcp socket,有几点需要注意:
一、TCP的TIME_WAIT状态(等待客户端的相应)
注* TIME_WAIT 状态最大保持时间是2 * MSL,也就是1-4分钟(MSL是最大分段生存期,指明TCP报文在Internet上最长生存时间)
当服务器端socket绑定本地地址并占用了端口,此时如果匆忙结束;或者连接的服务器异常退出,这个时候被占用的端口不能马上释放,需要TIME_WAIT。即便调用closesocket()一般也不会立即关闭socket,仍可继续重用该socket。所以重新启动服务器时可能会出现问题。例如MFC中在子窗口中实现socket通信,那么关闭子窗口再打开就会出问题了。
解决方法是在bind()之前添加setsockopt()函数,解除端口绑定。
介绍setsockopt()之前我们再来回顾一下三次握手协议的具体流程:
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。
setsockopt()使用方法如下:
1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));
更多setsockopt()函数用例可参考百度百科:http://baike.baidu.com/view/569217.htm
二、对于大型文件,一般需要将其剁碎了一部分一部分的传。TCP不能保证接收方顺序的收到包,对于需要实时显示的文件可以在发送方发出包后设置来自接收方的响应,即对方收到前一个包后再发送下一个包。
目前就这么多,各位看官有其他的注意事项拜托请留言补充,小弟感激啊。
下面简单说下UDP socket
UDP不能保证双方的可靠连接,容易出现丢包现象。
UDP的socket编程过程如下图所示:
上代码了,哈哈。
服务端:
- #include <stdio.h>
- #include <Winsock2.h>
-
- #pragma comment( lib, "ws2_32.lib" )
-
- void main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 ) {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||
- HIBYTE( wsaData.wVersion ) != 1 ) {
- WSACleanup( );
- return;
- }
- SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);
-
- int len=sizeof(SOCKADDR);
-
- SOCKADDR_IN from;
- SOCKADDR_IN local;
- local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
- local.sin_family=AF_INET;
- local.sin_port=htons(27015);
-
- int a = bind(sockSrv,(SOCKADDR*)&local,len);
-
-
-
- while(1)
- {
- char recvBuf[50];
- recvfrom(sockSrv,recvBuf,50,0,(SOCKADDR*)&from,&len);
- printf("%s\n",recvBuf);
- printf("%s\n",inet_ntoa(local.sin_addr));
- char sendBuf[50];
- sprintf(sendBuf,"Welcome %s to here!",inet_ntoa(from.sin_addr));
- sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&from,len);
-
- Sleep(2000);
- }
- closesocket(sockSrv);
- WSACleanup();
- }
客户端:
- #include <stdio.h>
- #include <Winsock2.h>
-
- #pragma comment( lib, "ws2_32.lib" )
-
-
- void main()
- {
- WORD wVersionRequested;
- WSADATA wsaData;
- int err;
-
- wVersionRequested = MAKEWORD( 1, 1 );
-
- err = WSAStartup( wVersionRequested, &wsaData );
- if ( err != 0 ) {
- return;
- }
-
- if ( LOBYTE( wsaData.wVersion ) != 1 ||
- HIBYTE( wsaData.wVersion ) != 1 ) {
- WSACleanup( );
- return;
- }
-
-
- for(int index=0;;index++)
- {
- SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
-
- int len = sizeof(SOCKADDR);
-
- SOCKADDR_IN local;
- local.sin_addr.S_un.S_addr=inet_addr("192.168.0.30");
- local.sin_family=AF_INET;
- local.sin_port=htons(27015);
-
- char sendBuf[30];
- sprintf(sendBuf,"%3d,",index);
- strcat(sendBuf,"server node of: yaopeng");
- sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&local,len);
-
- char recvBuf[50];
- recvfrom(sockClient,recvBuf,50,0,(SOCKADDR*)&local,&len);
- printf("my reply is : %s\n",recvBuf);
- printf("%s\n",inet_ntoa(local.sin_addr));
-
- closesocket(sockClient);
- Sleep(2000);
- WSACleanup();
- }
- }
完了。关于socket编程还有很多函数没有涉及,急待跟进完善。欢迎大家给我留言!
C++基于TCP和UDP的socket通信
标签:
原文地址:http://www.cnblogs.com/thisway/p/5301046.html