socket就在应用程序的传输层和应用层之间,传输层的底一层的服务提供给socket抽象层,socket抽象层再提供给应用层。
第一次握手:客户端尝试连接服务器,向服务器发送syn包,
第二次握手:服务器接收客户端syn包并确认,同时向客户端发送一个SYN包,即SYN+ACK包
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,完成三次握手,建立连接
第一次挥手:某个应用进程首先调用closesocket主动关闭连接,这时主动方(客户端或者服务端)发送一个FIN;
第二次挥手:被动方接收到FIN之后,执行被动关闭,对这个FIN进行确认,发送ACK确认信号。
第三次挥手:一段时间之后,被动关闭的一方在完成所有数据发送后,调用close()操作。这导致它也发送一个FIN;
第四次挥手:主动方接收到这个FIN,向被动方发送ACK进行确认,一段时间后进入关闭状态。被动方接收之后,进入关闭状态。
这样每个方向上都有一个FIN和ACK。
1. socket函数
int socket(int domain, int type, int protocol);
socket()用于创建一个socket的套接字,它唯一标识一个socket。后续的操作都有用到这个套接字,把它作为参数,通过它来进行一些读写操作。
参数:
domain:即协议域,又称为协议族(family)。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL等;
type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET等等;
protocol:指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等。当protocol为0时,会自动选择type类型对应的默认协议。
2. bind函数
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。
参数:
sockfd:即socket套接字,它是通过socket()函数创建了,唯一标识一个socket;
addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址;
addrlen:对应的是地址的长度。
3. listen、connect函数
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。
服务器:int listen(int sockfd, int backlog);
参数:
sockfd:要监听的socket套接字;
backlog:相应socket可以排队的最大连接个数。
客户端:int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfd:客户端的socket套接字;
addr:服务器的socket地址;
addrlen:socket地址的长度
4. accept函数
SOCKET accept(SOCKET sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd:服务器的socket描述字;
addr:指向struct sockaddr *的指针,用于返回客户端的协议地址;
addrlen:协议地址的长度;
accept函数返回的是已连接的socket套接字。
5. send函数
int send( SOCKET s, const char FAR *buf, int len, int flags );
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
参数:
s:指定发送端套接字描述符;
buf:指明一个存放应用程序要发送数据的缓冲区;
len:指明实际要发送的数据的字节数;
flags:一般置0。
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户程序发送应答。
6. recv函数
int recv(SOCKET s,char *buf,int len,int flags);
参数:
s:接收端的套接字描述符;
buf:接收数据的缓冲区;
len:接收数据的字节数;
flags:一般置0。
7. closesocket函数
int closesocket(SOCKET s)
参数:
s:需要关闭的socket套接字。
在QT中使用之前需在.pro中添加socket库
LIBS += -lpthread libwsock32 libws2_32
服务端:
#include
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//初始化套接字库
WORD word = MAKEWORD(2,2);//版本号
WSADATA wsadata;
WSAStartup(word,&wsadata);
//定义服务端套接字,接受请求套接字
SOCKET serviceSocket;
SOCKET clientSocket;
//服务端地址,客户端地址
SOCKADDR_IN socketAddr;
SOCKADDR_IN clientAddr;
//填充服务端信息
socketAddr.sin_family = AF_INET;
socketAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
socketAddr.sin_port = htons(10000);
//创建套接字
serviceSocket = socket(AF_INET, SOCK_STREAM, 0);
//绑定套接字
int bRes = bind(serviceSocket, (SOCKADDR*)&socketAddr, sizeof(SOCKADDR)); //绑定注意的一点就是记得强制类型转换
if (SOCKET_ERROR == bRes)
{
cout << "Bind failure!" << endl;
}
else
{
cout << "Bind success!" << endl;
}
//设置套接字为监听状态
int lLen = listen(serviceSocket, SOMAXCONN);
if (SOCKET_ERROR == lLen)
{
cout << "Listen failure!" << endl;
WSACleanup();
}
else
{
cout << "Listen success!" << endl;
}
cout << "The server is listening for connection, please wait!" << endl;
//接受连接请求
int revSize = sizeof(SOCKADDR);
clientSocket = accept(serviceSocket, (SOCKADDR*)&clientAddr, &revSize);
if (clientSocket == SOCKET_ERROR)
{
cout << "Accept connectionfailure!" << endl;
WSACleanup();
return 0;
}
cout << "Connection established, ready to receive data!" << endl;
while (1)
{
char recvBuf[1024];
int recv_len = recv(clientSocket,recvBuf,100,0);
if (recv_len < 0)
{
cout << "Acceptfailure!" << endl;
break;
}
else
{
cout << "Received client information:" << recvBuf << endl;
}
cout << "Please enter a reply message:";
char sendBuf[1024];
cin >> sendBuf;
int send_len = send(clientSocket,sendBuf,strlen(sendBuf),0);
if (send_len < 0)
{
cout << "Send failure!" << endl;
break;
}
}
//关闭socket
closesocket(serviceSocket);
//终止
WSACleanup();
return a.exec();
}
客户端:
#include
#include
#include
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
//初始化套接字库
WORD word = MAKEWORD(2,2);//版本号
WSADATA wsadata;
WSAStartup(word,&wsadata);
//定义客户端套接字
SOCKET clientSocket;
//服务端地址
SOCKADDR_IN socketAddr;
//填充服务端信息
socketAddr.sin_family = AF_INET;
socketAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
socketAddr.sin_port = htons(10000);
//创建套接字
clientSocket = socket(AF_INET, SOCK_STREAM, 0);
//连接服务器
int ret = connect(clientSocket, (SOCKADDR*)&socketAddr, sizeof(SOCKADDR));
if(ret == SOCKET_ERROR)
{
cout << "Connect failure!" << endl;
WSACleanup();
}
else
{
cout << "Connect success!" << endl;
}
//发送数据
while (1)
{
char sendBuf[1024];
cout << "Please enter send information:" << endl;
cin >> sendBuf;
int sendLen = send(clientSocket, sendBuf, 100, 0);
if (sendLen < 0)
{
cout << "Send success!" << endl;
break;
}
char recvBuf[100];
int recvLen = recv(clientSocket,recvBuf,100,0);
if (recvLen < 0)
{
cout << "Send failure!" << endl;
break;
}
else
{
cout << "Received server information:" << recvBuf << endl;
}
}
//关闭套接字
closesocket(clientSocket);
//释放DLL资源
WSACleanup();
return a.exec();
}