Socket编程之TCP实例(附CC++代码详解)
说明:
主要分步骤给出Windows平台下socket编程的⼀个TCP实例;使⽤WINDOWS下⽹络编程规范Winsock完成⽹络通信;
对程序各部分细节进⾏描述。
套接字有三种传输类型SOCK_STREAM    SOCK_DGRAM  SOCK_RAW;
⼀、服务器
功能:监控端⼝,等待客户端的请求;建⽴连接成功后,服务器每输⼊⼀次数据,发送⼀组数据;若输⼊ q,则停⽌发送。
1、加载套接字库,创建套接字。
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")  //静态加⼊⼀个lib⽂件
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0) //WSAStartup返回0表⽰设置初始化成功
return 0;
/*创建套接字*/
//AF_INET表⽰IPv4,SOCK_STREAM数据传输⽅式,IPPROTO_TCP传输协议;
SOCKET listenSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
socket编程聊天室基本流程if (listenSocket == INVALID_SOCKET)
{
printf("套接字创建失败");
WSACleanup();
return 0;
}
说明:
(1)WORD是微软SDK中的⽆符号16位整形数;WSADATA是⼀个结构体;
(2)MAKEWORD(a,b)是⼀个宏,这⾥⽤来指定使⽤的Winsock版本;
(3)WSAStartup,即WSA(Windows Sockets Asynchronous,Windows异步)的启动命令;WSAStartup必须是应⽤程序或DLL调⽤的第⼀个Windows Sockets函数。它允许应⽤程序或DLL指明Windows Sockets API的版本号及获得特定Windows Sockets实现的细节。应⽤程序或DLL只能在⼀次成功的WSAStartup()调⽤之后才能调⽤进⼀步的Windows Sockets API函数。
(4)函数socket(),socket()函数⽤于根据指定的地址族、数据类型和协议来分配⼀个的描述字及其所⽤的资源;若⽆错误发
⽣,socket()返回引⽤新的描述字。否则的话,返回INVALID_SOCKET错误。
2、绑定套接字到⼀个IP地址和⼀个端⼝上
/*绑定IP和端⼝*/
//配置监听地址和端⼝
sockaddr_in addrListen;
addrListen.sin_family = AF_INET;    //指定IP格式
addrListen.sin_port = htons(8888);  //绑定端⼝号
addrListen.sin_addr.S_un.S_addr = INADDR_ANY;  //表⽰任何IP  service.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(listenSocket, (SOCKADDR*)&addrListen, sizeof(addrListen)) == SOCKET_ERROR)  //(SOCKADDR*)
{
printf("绑定失败");
closesocket(listenSocket);
return 0;
}
说明:
(1)sockaddr_in是⼀个数据结构;⽤做bind、connect、recvfrom、sendto等函数的参数,指明地址信息。
(2)bind()函数int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen);
bind()函数通过给⼀个套接字接⼝分配⼀个地址来建⽴捆绑。
3、监听指定端⼝
/*开始监听*/
if (listen(listenSocket, 5) == SOCKET_ERROR)
{
printf("监听出错");
closesocket(listenSocket);
return 0;
}
说明:
int listen( int sockfd, int backlog);
sockfd:⽤于标识⼀个已捆绑未连接套接⼝的描述字。
backlog:等待连接队列的最⼤长度。
4、等待客户端请求,若收到请求,建⽴⼀个对应于此次连接的套接字
/*等待连接,连接后建⽴⼀个新的套接字*/
SOCKET revSocket;  //对应此时所建⽴连接的套接字的句柄
sockaddr_in remoteAddr;  //接收连接到服务器上的地址信息
int remoteAddrLen = sizeof(remoteAddr);
printf("等待连接...\n");
/*等待客户端请求,服务器接收请求*/
revSocket = accept(listenSocket, (SOCKADDR*)&remoteAddr, &remoteAddrLen);  //等待客户端接⼊,直到有客户端连接上来为⽌
if (revSocket == INVALID_SOCKET)
{
printf("客户端发出请求,服务器接收请求失败:\n",WSAGetLastError());
closesocket(listenSocket);
WSACleanup();
return 0;
}
else
{
printf("客服端与服务器建⽴连接成功:%s \n", inet_ntoa(remoteAddr.sin_addr));
}
说明:
(1)⾸先定义⼀个新的套接字,注意该套接字是与之前的不同,数据传输时,使⽤本套接字。
(2)SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
函数从等待连接队列中抽取第⼀个连接,创建⼀个同类的新的套接⼝并返回句柄;
sockfd:套接字描述符,该套接⼝在后监听连接;
addr:(可选)指针,指向⼀缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接⼝创建时所产⽣的地址族确定。
addrlen:(可选)指针,输⼊参数,配合addr⼀起使⽤,指向存有addr地址长度的整型数。
5、接收客户端数据
char revData[255] = "";
char *sendData = new char[100];
/*通过建⽴的连接进⾏通信*/
int res = recv(revSocket, revData, 255, 0);
if (res > 0)
{
printf("Bytes received: %d\n", res);
printf("客户端发送的数据: %s\n", revData);
}
else if (res == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
说明:
int recv( SOCKET s,    char FAR *buf,      int len,    int flags    );
不论是客户还是服务器应⽤程序都⽤recv函数从TCP连接的另⼀端接收数据,根据返回值判断数据接收情况。s指定接收端套接字描述符;
buf指明⼀个缓冲区,该缓冲区⽤来存放recv函数接收到的数据;
len指明buf的长度;
flags⼀般为0;
6、向客户端发送数据
while (cin>>sendData)
{
//cout << strlen(sendData) << endl;
if (strcmp(sendData, "q") == 0)
{
printf("服务器停⽌发送数据!\n");
break;
}
//发送数据
send(revSocket, sendData, strlen(sendData), 0);
}
说明:
int send( SOCKET s,  const char FAR *buf,  int len,  int flags );
含义基本同recv()函数。
7、关闭套接字,关闭加载的套接字库
closesocket(listenSocket);
WSACleanup();
⼆、客户端
部分函数与服务器端相同,不再单独列出!
1、加载套接字库,创建套接字
WORD sockVerson = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVerson, &wsaData) != 0)
return 0;
//建⽴客户端socket
SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSocket == INVALID_SOCKET)
{
printf("套接字创建失败");
WSACleanup();
return 0;
}
2、向服务器发出连接请求
//定义要连接的服务器地址
sockaddr_in addrConServer;
addrConServer.sin_family = AF_INET;
addrConServer.sin_port = htons(8888);
addrConServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if (connect(clientSocket, (SOCKADDR*)&addrConServer, sizeof(addrConServer)) == SOCKET_ERROR)
{
printf("客户端建⽴连接失败!\n");
closesocket(clientSocket);
WSACleanup();
return 0;
}
else
printf("客户端建⽴连接成功,准备发送数据!\n");
说明:
int connect(SOCKET s, const struct sockaddr * name, int namelen);
本函数⽤于创建与指定的连接,对于流类套接⼝(SOCK_STREAM类型),利⽤名字来与⼀个远程主机建⽴连接,⼀旦套接⼝调⽤成功返回,它就能收发数据了。对于数据报类套接⼝(SOCK_DGRAM类型),则设置成⼀个缺省的⽬的地址,并⽤它来进⾏后续的send()与recv()调⽤。
3、与服务器之间进⾏数据传输
//发送数据
int sendRes = send(clientSocket, sendBuf, (int)strlen(sendBuf), 0); if (sendRes == SOCKET_ERROR)
{
printf("客户端send()出现错误 : %d\n", WSAGetLastError());
closesocket(clientSocket);
WSACleanup();
return 0;
}
else
printf("客户端发送数据成功!\n");
//接收服务端数据
/*通过建⽴的连接进⾏通信*/
do
{
char revSerData[100] = "";
res = recv(clientSocket, revSerData, sizeof(revSerData), 0);
if (res > 0)
{
printf("Bytes received: %d\n", res);
printf("服务器发送的数据: %s\n", revSerData);
}
else if (res == 0)
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
} while (res > 0);
4、关闭套接字,关闭加载的套接字库
closesocket(clientSocket);
WSACleanup();
三、运⾏结果
先运⾏服务器,后运⾏客户端;
然后在服务器中输⼊拟发送的数据;
1、服务器运⾏初始界⾯
2、打开客户端后的初始界⾯

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。