流程概述
客户端与服务器之间的网络通信基本原理如下
对于服务端,通信流程如下
- 调用socket()函数创建监听套接字servSock
- 调用bind()函数将servSock绑定到某个IP和端口号组成的二元组上,IP和端口号组成的信息用
sockAddr_in
或SOCKADDR
结构体变量进行封装 - 调用listen()函数开启监听
- 当有客户端连接请求时,调用accept()函数接受连接,产生一个新的套接字
clntSock
,后续与客户端的通信(send()/recv()),均有该套接字完成 - 基于新产生的套接字
clntSock
,调用send()/recv()
函数开始与客户端进行数据交流 - 通信结束后,调用
closesocket()
函数关闭套接字
对于客户端,通信流程如下
- 调用sock()函数创建客户端套接字sock
- 调用connect()函数尝试连接服务器
- 连接成功后调用send()/recv()函数与服务器进行数据交流
- 通信结束后,调用closesocket()函数关闭监听socket.
服务器端代码
#include <stdio.h>
#include <winsock2.h>
#include <WS2tcpip.h>
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
int main() {
//初始化 DLL
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
SOCKET servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//绑定套接字
struct sockaddr_in sockAddr;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET; //使用IPv4地址
const char* src = "127.0.0.1";
inet_pton(AF_INET, src, &sockAddr.sin_addr);
//sockAddr.sin_addr.s_addr = inet_pton(src); //具体的IP地址
sockAddr.sin_port = htons(1234); //端口
bind(servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
//进入监听状态
listen(servSock, 20);
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
SOCKET clntSock = accept(servSock, (SOCKADDR*)&clntAddr, &nSize);
//向客户端发送数据
const char* str = "Hello World!";
send(clntSock, str, strlen(str) + sizeof(char), NULL);
//关闭套接字
closesocket(clntSock);
closesocket(servSock);
//终止 DLL 的使用
WSACleanup();