客户端
#include "stdafx.h"
#include <iostream>
#include <Winsock2.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
const char* ip = "127.0.0.1";
static USHORT port = 5000;
int _tmain(int argc, _TCHAR* argv[])
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
SOCKET s = INVALID_SOCKET;
sockaddr_in server;
char sendbuf[1024], recvbuf[1024];
int iResult;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if(-1 == err)
{
cerr << "WSAStartup调用失败." << endl;
goto Error;
}
if(HIBYTE(wsaData.wVersion) != 2 || LOBYTE(wsaData.wVersion) != 2)
{
cerr << "无法调用指定版本的Winsock2.dll." << endl;
goto Error;
}
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == s)
{
cerr << "SOCKET创建失败,错误代码:" << WSAGetLastError() << endl;
goto Error;
}
Reconnect:
server.sin_family = AF_INET;
server.sin_addr.S_un.S_addr = inet_addr(ip);
server.sin_port = htons(port);
err = connect(s, (sockaddr*)&server, sizeof(server));
if(SOCKET_ERROR == err)
{
cerr << ip << "连接失败,错误代码:" << WSAGetLastError() << endl;
goto Error;
}
memset(sendbuf, 0, 1024);
cout << "send: ";
cin.getline(sendbuf, 1024);
iResult = send(s, sendbuf, strlen(sendbuf), 0);
if (SOCKET_ERROR == iResult)
{
cerr << "发送信息失败,错误代码:" << WSAGetLastError() << endl;
goto Error;
}
while(true)
{
memset(recvbuf, 0, 1024);
iResult = recv(s, recvbuf, 1024, 0);
if (iResult > 0)
{
cout << "从服务器接收" << iResult << "个字节:" << recvbuf << endl;
}else if(0 == iResult)
{
cout << "服务器断开连接." << endl;
goto Reconnect;
}else
{
cerr << "接收信息失败,错误代码:" << WSAGetLastError() << endl;
goto Error;
}
}
Error:
if(INVALID_SOCKET != s)
closesocket(s);
WSACleanup();
getchar();
return 0;
}
服务端
#include "stdafx.h"
#include <iostream>
#include <Winsock2.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
static USHORT port = 5000;
static int backlog = 3;
int _tmain(int argc, _TCHAR* argv[])
{
WORD iVersionRequested;
WSADATA wsaData;
int err;
SOCKET s = INVALID_SOCKET;
SOCKADDR_IN server;
char sendbuf[1024], recvbuf[1024];
int iResult;
iVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(iVersionRequested, &wsaData);
if(-1 == err)
{
cerr << "WSAStartup调用失败." << endl;
goto Error;
}
if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
cerr << "无法调用指定版本的Winsock2.dll." << endl;
goto Error;
}
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == s)
{
cerr << "SOCKET创建失败.错误代码:" << WSAGetLastError() << endl;
goto Error;
}
server.sin_family = AF_INET;
server.sin_addr.S_un.S_addr = INADDR_ANY;
server.sin_port = htons(port);
err = bind(s, (sockaddr*)&server, sizeof(server));
if(SOCKET_ERROR == err)
{
cerr << "SOCKET绑定到本机地址失败. 错误代码:" << WSAGetLastError() << endl;
goto Error;
}
err = listen(s, backlog);
if (SOCKET_ERROR == err)
{
cerr << "创建监听队列失败. 错误代码:" << WSAGetLastError() << endl;
goto Error;
}
while(true)
{
SOCKET sClient = INVALID_SOCKET;
SOCKADDR_IN client;
int addrlen = sizeof(client);
sClient = accept(s, (sockaddr*)&client, &addrlen);
if(SOCKET_ERROR == sClient)
{
cerr << "接收客服端连接失败.错误代码:" << WSAGetLastError() << endl;
goto Error;
}
cout << "接收来自" << inet_ntoa(client.sin_addr) << "的连接" << endl;
memset(sendbuf, 0, 1024);
strcpy(sendbuf, "你好,这里是服务器.");
send(sClient, sendbuf, strlen(sendbuf), 0);
while(true)
{
memset(recvbuf, 0, 1024);
iResult = recv(sClient, recvbuf, 1024, 0);
if(iResult > 0)
{
cout << "接收" << iResult << "个字节.内容:" << recvbuf << endl;
memset(sendbuf, 0, 1024);
strcpy(sendbuf, recvbuf);
send(sClient, sendbuf, strlen(sendbuf), 0);
}else if(0 == iResult)
{
cout << "客户端断开连接." << endl;
break;
}else
{
cerr << "接收信息失败.错误代码:" << WSAGetLastError() << endl;
goto Error;
}
}
}
Error:
if (INVALID_SOCKET == s)
closesocket(s);
WSACleanup();
getchar();
return 0;
}
- TCP与UDP区别总结:
1、TCP面向连接(如打电话要先拨号建立连接),需要三次握手;UDP是无连接的,即发送数据之前不需要建立连接;
2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保 证可靠交付;
3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的。UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等);
4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信;
5、TCP首部开销20字节;UDP的首部开销小,只有8个字节;
6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。 - listen函数中的第二个参数backlog解析
当有多个客户端一起请求的时候,服务端不可能来多少就处理多少,这样如果并发太多,就会因为性能的因素发生拥塞,然后造成雪崩。所以对于监听套接字,一般会有两个队列,未完成连接套接字和已完成连接套接字,当请求到达时,新建套接字会被存放在未完成队列中,3路握手完毕就会被转移到已完成队列里。listen里面的第二个参数backlog就是设置这个未完成连接套接字队列的长度。如果将队列长度设置成10,那么如果有20个请求一起过来,服务端就会先放10个请求进入这个队列,因为长度只有10。然后其他的就直接拒绝。如果三次握手完成了,就会将完成三次握手的请求取出来,放入另一个队列中,这样队列就空出一个位置,其他重发SYN的请求就可以进入队列中。