记录一下在windows平台实现基本的socket编程,实现一个简易的回声服务器和客户端,废话不多说,直接上代码,里面有详尽的注释。
操作系统:win10 64位
编辑器:vscode,需要安装c/c++插件。
编译器:MinGw编译器
服务器:
1 #include <stdio.h>
2 #include <winsock2.h>
3 #pragma comment(lib,"ws2_32.lib")
4
5 #define PORT 6666
6
7 int main()
8 {
9 WSADATA wsaData = {0}; //定义一个结构体,用来接收函数给的参数
10 int err = -1;
11 //第一步:定义我们需要的winsock的版本,这里是2.2版本(目前最高的版本号)
12 err = WSAStartup(MAKEWORD(2,2), &wsaData);
13 if(0 != err) //返回非0 ,代表出错
14 {
15 printf("failed with error:%d\n", err);
16 system("pause");
17 return 1;
18 }
19 //第二部:查看当前系统支持的版本,如果不支持我们上面定义的版本就用不了
20 if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
21 {
22 printf("system not support for this version\n");
23 WSACleanup(); //释放系统资源
24 system("pause");
25 return 1;
26 }
27 else
28 {
29 printf("The WinSock 2.2 dll was found\n");
30 }
31 //第三步:开始创建套接字
32 SOCKET server = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
33 if(INVALID_SOCKET == server)
34 {
35 printf("create socket failed with error:%d\n",WSAGetLastError());
36 WSACleanup();
37 system("pause");
38 return 1;
39 }
40 //第四步:准备结构体,绑定socket
41 SOCKADDR_IN addr = {};
42 addr.sin_family = AF_INET;
43 addr.sin_port = htons(PORT); //将本地字节序转换成网络字节序
44 addr.sin_addr.S_un.S_addr = INADDR_ANY;
45 //如果需要指定IP,可以这样
46 //addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
47 if(SOCKET_ERROR == bind(server,(SOCKADDR*)&addr,sizeof(addr))) //成功返回0
48 {
49 printf("bind socket failed with error:%d\n",WSAGetLastError());
50 closesocket(server);
51 WSACleanup();
52 system("pause");
53 return 1;
54 }
55 //第五步:监听
56 if(SOCKET_ERROR == listen(server,SOMAXCONN)) //成功返回0
57 {
58 printf("listen socket failed with error:%d\n",WSAGetLastError());
59 closesocket(server);
60 WSACleanup();
61 system("pause");
62 return 1;
63 }
64 //第六步,等待连接
65 SOCKADDR_IN cli_addr = {};
66 int len = sizeof(cli_addr);
67 printf("all is okay,waitting for client......\n");
68 while(1) //因为可以接收很多的客户,这里使用无限循环
69 {
70 // 这一步将会阻塞,直到有客户端连接进来(接客)
71 SOCKET cli = accept(server,(SOCKADDR*)&cli_addr,&len);
72 //这一步可以优化,用线程来做,主线程只负责接客,子线程来服务客人(有效的连接)。
73 if(INVALID_SOCKET == cli)
74 {
75 printf("invalide socket,error:%d\n",WSAGetLastError());
76 closesocket(server);
77 WSACleanup();
78 system("pause");
79 return 1;
80 }
81 //客户信息有效,打印下看看吧
82 printf(">>client$\t%s:%d connected!\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port));
83
84 //第七步:开始通讯,这里写一个回声服务器(将收到的数据,原数发回给客户端)
85 char buff[MAXBYTE] = {}; //存储客户端发来的信息
86 int bufflen = 0;
87 do //客户端可能会发送多次数据,这里暂使用无限循环
88 {
89 //每次接收数据前,需要将上一次接收的缓冲区数据清空
90 ZeroMemory(buff,sizeof(buff)); //该函数底层调用的memset函数
91 bufflen = recv(cli,buff,sizeof(buff),0);
92 if(bufflen == 0) //recv函数可以接受0个参数,代表对方关闭连接了
93 {
94 printf("connection closed!\n");
95 }
96 else if (SOCKET_ERROR == bufflen)
97 {
98 printf("recv failed with error:%d\n",WSAGetLastError());
99 }
100 else //接收到了数据
101 {
102 buff[bufflen] = '\0';
103 printf("received:%s\n",buff);
104 //send函数可以发送大于等于0的数据
105 int len = send(cli,buff,bufflen,0);
106 if(SOCKET_ERROR == len)
107 {
108 printf("send failed with error:%d\n",WSAGetLastError());
109 break;
110 }
111 else
112 {
113 printf("send successfully!\nrecv:%d Bytes,send:%d Bytes\n",bufflen,len);
114 }
115
116 }
117
118 } while(bufflen>0);
119 closesocket(cli);
120 }
121 closesocket(server);
122 WSACleanup();
123 getchar();
124 return 0;
125 }
客户端:
1 #include <stdio.h>
2 #include <winsock2.h>
3 #pragma comment(lib,"ws2_32.lib")
4
5 #define PORT 6666
6 #define SERVERADDR "127.0.0.1"
7
8 int main()
9 {
10 WSADATA wsaData = {};
11 int err = WSAStartup(MAKEWORD(2,2),&wsaData);
12 if(0 != err)
13 {
14 printf("failed with error:%d\n", err);
15 system("pause");
16 return 1;
17 }
18 //创建套接字
19 SOCKET client = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
20 if(INVALID_SOCKET == client)
21 {
22 printf("create socket failed with error:%d\n",WSAGetLastError());
23 WSACleanup();
24 system("pause");
25 return 1;
26 }
27 //准备服务器的信息
28 SOCKADDR_IN server_addr = {};
29 server_addr.sin_family = AF_INET;
30 server_addr.sin_port = htons(PORT); //将本地字节序转换成网络字节序
31 server_addr.sin_addr.S_un.S_addr = inet_addr(SERVERADDR);
32 //开始连接服务器
33 if(SOCKET_ERROR == connect(client,(SOCKADDR*)&server_addr,sizeof(server_addr)))
34 {
35 printf("connect to server with error:%d\n",WSAGetLastError());
36 closesocket(client);
37 WSACleanup();
38 system("pause");
39 return 1;
40 }
41 int len = 0;
42 char buff[MAXBYTE] = {0};
43 char recv_buff[MAXBYTE];
44 int flag=0;
45 do
46 {
47 memset(buff,'\0',MAXBYTE);
48 printf("请输入需要发送的内容:\n");
49 rewind(stdin);
50 flag = scanf("%s",buff);
51 if(EOF!= flag)
52 {
53 //printf("buff:%s,%d\n",buff,strlen(buff));
54 char t_buff[MAXBYTE] = "你好啊哈";
55 int len = send(client,t_buff,strlen(t_buff),0);
56 if(SOCKET_ERROR == len)
57 {
58 printf("send failed with error:%d\n",WSAGetLastError());
59 break;
60 }
61 //准备接收服务器的回声
62 memset(recv_buff,0,sizeof(recv_buff));
63 int recv_len = recv(client,recv_buff,sizeof(recv_buff),0);
64 if(recv_len == 0) //对方关闭连接了
65 {
66 printf("service closed!\n");
67 break;
68 }
69 else if (SOCKET_ERROR == recv_len)
70 {
71 printf("recv failed with error:%d\n",WSAGetLastError());
72 break;
73 }
74 //打印接收到的数据
75 printf("received from server:%s\n",recv_buff);
76
77 }
78 } while(flag!=EOF);
79 closesocket(client);
80 WSACleanup();
81 system("pause");
82 return 0;
83 }
总结:
1. 服务器的程序在vscode上亲测没有问题,客户端程序通过查看代码可以看到,每次发的都是固定的字符串,原因是使用scanf函数获取字符串失败,目前没有找到原因,希望有人帮指点下。
2. vscode的控制台使用的是utf-8字符编码格式,所以出现中文不会有乱码,如果是使用的是windows的控制台(ASCII编码格式)打开程序,出现中文会显示乱码,这是需要转码才能正常显示。
参考:MultiByteToWideChar和WideCharToMultiByte函数,查询下MSDN。
最后贴上一个用python写的简易客户端程序,我当时是用来快速检验服务端程序的,证明服务端程序能正常运行。
1 from socket import socket
2 s=socket()
3 s.connect(('127.0.0.1',6666))
4 while True:
5 send_str = input("请输入需要发送的内容:\n")
6 if len(send_str)>0:
7 s.send(send_str.encode())
8 else:
9 break
10 res = s.recv(1024).decode()
11 print(res)
12 print('='*20)
13 s.close()
是不是感觉代码很简易,其实底层的实现都是一样的,python是做了一个封装,使用起来比较方便而已。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步