sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW)。
基于TCP的socket编程是采用的流式套接字。
服务端编程步骤
1:加载套接字库,创建套接字(WSAStartup()/socket());
2:绑定套接字到一个IP地址和一个端口上(bind());
3:将套接字设置为监听模式等待连接请求(listen());
4:请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());
5:用返回的套接字和客户端进行通信(send()/recv());
6:返回,等待另一连接请求;
7:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
客户端编程步骤
1:加载套接字库,创建套接字(WSAStartup()/socket());
2:向服务器发出连接请求(connect());
3:和服务器端进行通信(send()/recv());
4:关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup())。
具体操作
1、 加载/释放Winsock库
1)加载方法:
WSADATA wsa;//初始化socket资源
if (WSAStartup(MAKEWORD(1,1),&wsa) != 0)
{
return; //代表失败
}
2)释放方法:
WSACleanup();
2、构造SOCKET
1)服务端:构造监听SOCKET,流式SOCKET
SOCKET Listen_Sock = socket(AF_INET, SOCK_STREAM, 0);
2)客户端:构造通讯SOCKET,流式SOCKET
SOCKET Client_Sock = socket(AF_INET, SOCK_STREAM, 0);
3、服务端配置监听地址和端口:
SOCKADDR_IN serverAddr
ZeroMemory((char *)&serverAddr,sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(1234); //本地监听端口:1234
4、服务端绑定监听SOCKET
bind(Listen_Sock,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
5、服务端/客户端连接
1)服务端:等待客户端接入
SOCKET Command_Sock = accept(Listen_Sock, ...);
2)客户端:请求与服务端连接
int ret = connect(Client_Sock, ...);
6、收/发数据
1)服务端:等待客户端接入.
char buf[1024];
接收数据:recv(Command_Sock,buf, ...);
或
发送数据:send(Command_Sock,buf, ...);
2)客户端:请求与服务端连接
char buf[1024];
发送数据:send(Client_Sock,buf, ...);
或
接收数据:recv(Client_Sock,buf, ...);
7、关闭SOCKET
1)服务端:关闭SOCKET.
closesocket(Listen_Sock);
closesocket(Command_Sock);
2)客户端:关闭SOCKET.
closesocket(Client_Sock);
实例
客户端发送文本信息,服务端接收来自客户端的信息
1、开发环境:Clion
2、配置工作
1)创建两个项目,分别是服务端和客户端
2)创建项目后在CMmakeLists.txt中添加下面两行代码:
link_libraries(ws2_32)
add_executable(untitled0 main.c)//untitled0是项目文件夹名称,main.c是主程序
3、编程
1)服务端代码:
#include <stdio.h>
#include <winsock2.h>
#define SERVER_PORT 5208 //侦听端口
int main()
{
WORD wVersionRequested;
WSADATA wsaData;
int ret, nLeft, length;
SOCKET sListen, sServer; //侦听套接字,连接套接字
struct sockaddr_in saServer, saClient; //地址信息
char *ptr;//用于遍历信息的指针
//WinSock初始化
wVersionRequested=MAKEWORD(2, 2); //希望使用的WinSock DLL 的版本
ret=WSAStartup(wVersionRequested, &wsaData);
if(ret!=0)
{
printf("WSAStartup() failed!\n");
return -1;
}
//构造监听Socket,使用TCP协议
sListen=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sListen == INVALID_SOCKET)
{
WSACleanup();
printf("socket() faild!\n");
return -1;
}
//构建本地地址信息,配置监听地址和端口
saServer.sin_family = AF_INET; //地址家族
saServer.sin_port = htons(SERVER_PORT); //注意转化为网络字节序
saServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //使用INADDR_ANY 指示任意地址
//绑定监听Socket
ret = bind(sListen, (struct sockaddr *)&saServer, sizeof(saServer));
if (ret == SOCKET_ERROR)
{
printf("bind() faild! code:%d\n", WSAGetLastError());
closesocket(sListen); //关闭套接字
WSACleanup();
return -1;
}
//侦听连接请求
ret = listen(sListen, 5);
if (ret == SOCKET_ERROR)
{
printf("listen() faild! code:%d\n", WSAGetLastError());
closesocket(sListen); //关闭套接字
return -1;
}
printf("Waiting for client connecting!\n");
printf("Tips: Ctrl+c to quit!\n");
//阻塞等待接受客户端连接
while(1)//循环监听客户端,永远不停止,所以,在本项目中,我们没有心跳包。
{
length = sizeof(saClient);
sServer = accept(sListen, (struct sockaddr *)&saClient, &length);//等待客户端接入
if (sServer == INVALID_SOCKET)
{
printf("accept() faild! code:%d\n", WSAGetLastError());
closesocket(sListen); //关闭套接字
WSACleanup();
return -1;
}
char receiveMessage[5000];
nLeft = sizeof(receiveMessage);
ptr = (char *)&receiveMessage;
while(nLeft > 0)
{
//接收数据
ret = recv(sServer, ptr, 5000, 0);
if (ret == SOCKET_ERROR)
{
printf("recv() failed!\n");
return -1;
}
if (ret == 0) //客户端已经关闭连接
{
printf("Client has closed the connection\n");
break;
}
nLeft -= ret;//少了这一行循环不会结束
ptr += ret;
}
printf("receive message:\n");
printf("%s\n", receiveMessage);//打印我们接收到的消息
}
//closesocket(sListen);
//closesocket(sServer);
//WSACleanup();
return 0;
}
2)客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#define SERVER_PORT 5208 //侦听端口
int main()
{
WORD wVersionRequested;
WSADATA wsaData;
int ret;
SOCKET sClient; //连接套接字
struct sockaddr_in saServer; //地址信息
char *ptr;
BOOL fSuccess = TRUE;
//WinSock初始化
wVersionRequested = MAKEWORD(2, 2); //希望使用的WinSock DLL的版本
ret = WSAStartup(wVersionRequested, &wsaData);
if(ret!=0)
{
printf("WSAStartup() failed!\n");
return -1;
}
//确认WinSock DLL支持版本2.2
if(LOBYTE(wsaData.wVersion)!=2 || HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
printf("Invalid WinSock version!\n");
return -1;
}
//构造通讯Socket,使用TCP协议
sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sClient == INVALID_SOCKET)
{
WSACleanup();
printf("socket() failed!\n");
return -1;
}
//构建服务器地址信息
saServer.sin_family = AF_INET; //地址家族
saServer.sin_port = htons(SERVER_PORT); //注意转化为网络节序
saServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//连接服务器
ret = connect(sClient, (struct sockaddr *)&saServer, sizeof(saServer));//请求与服务器连接
if (ret == SOCKET_ERROR)
{
printf("connect() failed!\n");
closesocket(sClient); //关闭套接字
WSACleanup();
return -1;
}
char sendMessage[]="WDNMD";
ret = send (sClient, (char *)&sendMessage, sizeof(sendMessage), 0);
if (ret == SOCKET_ERROR)
{
printf("send() failed!\n");
}
else
printf("client info has been sent!");
closesocket(sClient); //关闭套接字
WSACleanup();
return 0;
}
3、运行
1)先运行服务端。服务端会等待客户端发送信息,终端显示如下:
Waiting for client connecting!
Tips: Ctrl+c to quit!
2)运行客户端,客户端会发送消息给服务端,终端显示如下则表示信息发送成功:
client info has been sent!
Process finished with exit code 0
3)服务端接收消息。终端显示出客户端发送的信息即成功:
Waiting for client connecting!
Tips: Ctrl+c to quit!
Client has closed the connection
receive message:
WDNMD
本文介绍了如何使用C语言进行TCP Socket编程,包括服务端和客户端的编程步骤,涉及加载套接字库、创建套接字、绑定、监听、接受连接、通信以及关闭套接字等关键操作。通过具体的实例展示了服务端等待客户端连接并接收信息,以及客户端发起连接并发送信息的过程。

2411

被折叠的 条评论
为什么被折叠?



