C++: Windows下的Socket编程

前言

  • 协议选择TCP
  • 这里只给出每个步骤的大致流程,具体实现依照自身需求编写。(循环或者并发等等)

服务器端

1. 加载Socket库

WSAStartup(): 通过进程启动 Winsock DLL 的使用

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    //版本2.2(lowbyte,highbyte)
    wVersionRequested = MAKEWORD(2, 2);
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        //找不到 winsock.dll
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        //找不到2.2版本的Winsock.dll
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
        printf("The Winsock 2.2 dll was found okay\n");

启动Socket库的固定模板,直接使用即可。

2. 创建socket

套接字 socket(协议族,套接字类型。协议号)

  • 这里使用IPv4协议族,所以选择AF_INET
  • 套接字类型选择SOCK_STREAM,对应的是TCP协议
  • 协议号默认为0
    SOCKET socklisten = socket(AF_INET, SOCK_STREAM, 0);
    if(INVALID_SOCKET == sockclient){
        printf("socket err\n");
        WSACleanup();
        return 1;
    }
3. 绑定地址信息bind

bind(套接字, (const sockaddr*)&addr, sizeof(addr))
addrsockaddr_in类型的变量,存储着我们要绑定的地址信息。

    sockaddr_in addr;
    addr.sin_family = AF_INET;   // IPv4
    addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");  //ip地址为本机地址
    addr.sin_port = htons(8080);
    if(SOCKET_ERROR == bind(m_socklisten, (const sockaddr*)&addr, sizeof(addr))){
        printf("bind err\n");
        closesocket(socklisten);
        WSACleanup();
        return 1;
    }

inet_addr():将IPv4地址的字符串转化为IN_ADDR结构的正确地址
htons():主机字节顺序转化为网络字节顺序

4. 监听listen

listen(套接字, 挂起连接队列的最大长度)

    if(SOCKET_ERROR == listen(socklisten, 100)){
        closesocket(socklisten);
        WSACleanup();
        return 1;
    }
5. 接收accept

接收套接字 accept(监听套接字, (sockaddr*)&addrclient, &nSize)
addrclient:存储接收到的客户端的地址信息

    sockaddr_in addrclient;
    int nSize = sizeof(addrclient);
    SOCKET sockWaiter = accept(m_socklisten, (sockaddr*)&addrclient, &nSize);  // accept用来创建接收数据的套接字
    if(INVALID_SOCKET == sockWaiter){
        printf("与客户端数据传送的套接字建立失败,错误代码为:%d\n", WSAGetLastError());
        return 1;
    }
    printf("client ip:%s\nclient port:%d\n", inet_ntoa(addrclient.sin_addr), addrclient.sin_port);

inet_ntoa(): 将网络地址转换为IPv4标准点十进制格式

6. 接收数据

接收到的数据大小 recv(接收套接字, buf, sizeof(buf), 0)
考虑到TCP协议是面向字节流的,所以进程在接收缓冲区读取数据时会出现粘包问题
所以,我们采用规定传输格式的方法(接收发送双方共同遵守):

  • 先发送包大小:告知服务器要接收的包大小,好让服务器动态分配出相应的空间
  • 再发送包数据:服务器按照字节顺序进行接收
	//接收包大小
	int nPackSize;
	int nRecvNum = recv(sockWaiter, (char*)&nPackSize, sizeof(int), 0);
	if(nRecvNum <= 0){
		closesocket(sockWaiter);
	}
	
	//接收包数据(申请空间)
	char *pszbuf = new char[nPackSize];
	int offset = 0;
	while(nPackSize){
		int nRecvNum = recv(sockWaiter, pszbuf+offset, nPackSize, 0);  // 由于每次接收的数据大小不一定为整个数据包的大小,所以需要设置偏移量
		offset += nRecvNum;
		nPackSize -= nRecvNum;
	 }
7. 发送数据

send(接收套接字, buf, sizeof(buf), 0)

	char szbuf[1024] = {0};
	int nlen = sizeof(szbuf);
	printf("Send:");
	cin.getline(szbuf, nlen);
	// 发送包大小
    send(sockWaiter, (char*)&nlen, sizeof(nlen), 0);
    // 发送包数据
    send(sockWaiter, szbuf, nlen, 0);
8. 关闭套接字
    closesocket(sockWaiter);
    closesocket(socklisten);
9. 终止Socket库的使用
    WSACleanup();



客户端

1. 加载Socket库

WSAStartup(): 通过进程启动 Winsock DLL 的使用

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    //版本2.2(lowbyte,highbyte)
    wVersionRequested = MAKEWORD(2, 2);
    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
        //找不到 winsock.dll
        printf("WSAStartup failed with error: %d\n", err);
        return 1;
    }
    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        //找不到2.2版本的Winsock.dll
        printf("Could not find a usable version of Winsock.dll\n");
        WSACleanup();
        return 1;
    }
    else
        printf("The Winsock 2.2 dll was found okay\n");

启动Socket库的固定模板,直接使用即可。

2. 创建socket

套接字 socket(协议族,套接字类型。协议号)

  • 这里使用IPv4协议族,所以选择AF_INET
  • 套接字类型选择SOCK_STREAM,对应的是TCP协议
  • 协议号默认为0
    SOCKET sockclient = socket(AF_INET, SOCK_STREAM, 0);
    if(INVALID_SOCKET == sockclient){
        printf("socket err\n");
        WSACleanup();
        return 1;
    }
3. 连接connect

客户端不需要绑定地址和端口,绑定由操作系统来完成
connect(客户端套接字, (const sockaddr*)&addr, sizeof(addr))
addr:存放着客户端要连接的服务器的地址信息

    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");   //客户端要连接到的服务器ip地址
    addr.sin_port = htons(8080);
    if(SOCKET_ERROR == connect(sockclient, (const sockaddr*)&addr, sizeof(addr))){
    	printf("connect error\n");
    	closesocket(sockclient);
    	WSACleanup();
        return 1;
    }
4. 接收数据

接收到的数据大小 recv(客户端套接字, buf, sizeof(buf), 0)
考虑到TCP协议是面向字节流的,所以进程在接收缓冲区读取数据时会出现粘包问题
所以,我们采用规定传输格式的方法(接收发送双方共同遵守):

  • 先发送包大小:告知服务器要接收的包大小,好让服务器动态分配出相应的空间
  • 再发送包数据:服务器按照字节顺序进行接收
	//接收包大小
	int nPackSize;
	int nRecvNum = recv(sockclient, (char*)&nPackSize, sizeof(int), 0);
	if(nRecvNum <= 0){
		closesocket(sockclient);
	}
	
	//接收包数据(申请空间)
	char *pszbuf = new char[nPackSize];
	int offset = 0;
	while(nPackSize){
		int nRecvNum = recv(sockclient, pszbuf+offset, nPackSize, 0);  // 由于每次接收的数据大小不一定为整个数据包的大小,所以需要设置偏移量
		offset += nRecvNum;
		nPackSize -= nRecvNum;
	 }
5. 发送数据

send(客户端套接字, buf, sizeof(buf), 0)

	char szbuf[1024] = {0};
	int nlen = sizeof(szbuf);
	printf("Send:");
	cin.getline(szbuf, nlen);
	// 发送包大小
    send(sockclient, (char*)&nlen, sizeof(nlen), 0);
    // 发送包数据
    send(sockclient, szbuf, nlen, 0);
6. 关闭套接字
    closesocket(sockclient);
7. 终止Socket库的使用
    WSACleanup();
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值