windows环境下⽤c++实现socket编程
⼀、什么是Socket
socket即套接字,⽤于描述地址和端⼝,是⼀个通信链的句柄。应⽤程序通过socket向⽹络发出请求或者回应。
sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);前两种较常⽤。基于TCP的socket编程是采⽤的流式套接字。
(1)SOCK_STREAM表⽰⾯向连接的数据传输⽅式。数据可以准确⽆误地到达另⼀台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常⽤的HTTP协议就使⽤SOCK_STREAM传输数据,因为要确保数据的正确性,否则⽹页不能正常解析。
(2)SOCK_DGRAM表⽰⽆连接的数据传输⽅式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另⼀台计算机,是没有办法补救的。也就是说,数据错了就错了,⽆法重传。因为SOCK_DGRAM所做的校验⼯作少,所以效率⽐SOCK_STREAM⾼。
socket编程聊天室基本流程QQ视频聊天和语⾳聊天就使⽤SOCK_DGRAM传输数据,因为⾸先要保证通信的效率,尽量减⼩延迟,⽽数据的正确性是次要的,即使丢失很⼩的⼀部分数据,视频和⾳频也可以正常解析,最多出现噪
点或杂⾳,不会对通信质量有实质的影响。
注意:SOCK_DGRAM没有想象中的糟糕,不会频繁的丢失数据,数据错读只是⼩概率事件。
有可能多种协议使⽤同⼀种数据传输⽅式,所以在socket编程中,需要同时指明数据传输⽅式和协议。
⼆、客户端/服务端模式:
在TCP/IP⽹络应⽤中,通信的两个进程相互作⽤的主要模式是客户/服务器模式,即客户端向服务器发出请求,服务器接收请求后,提供相应的服务。客户/服务器模式的建⽴基于以下两点:
(1)建⽴⽹络的起因是⽹络中软硬件资源、运算能⼒和信息不均等,需要共享,从⽽就让拥有众多资源的主机提供服务,资源较少的客户请求服务这⼀⾮对等作⽤。
(2)⽹间进程通信完全是异步的,相互通信的进程间既不存在⽗⼦关系,⼜不共享内存缓冲区。
因此需要⼀种机制为希望通信的进程间建⽴联系,为⼆者的数据交换提供同步,这就是基于客户/服务端模式的TCP/IP。
服务端:建⽴socket,声明⾃⾝的端⼝号和地址并绑定到socket,使⽤listen打开监听,然后不断⽤accept去查看是否有连接,如果有,捕获socket,并通过recv获取消息的内容,通信完成后调⽤closeSocket关闭这个对应accept到的socket,如果不再需要等待任何客户端连接,那么⽤closeSocket关闭掉⾃⾝的socket。
客户端:建⽴socket,通过端⼝号和地址确定⽬标服务器,使⽤Connect连接到服务器,send发送消息,等待处理,通信完成后调⽤closeSocket关闭socket。
三、编程步骤
(1)服务端
1、加载套接字库,创建套接字(WSAStartup()/socket());
2、绑定套接字到⼀个IP地址和⼀个端⼝上(bind());
3、将套接字设置为监听模式等待连接请求(listen());
4、请求到来后,接受连接请求,返回⼀个新的对应于此次连接的套接字(accept());
5、⽤返回的套接字和客户端进⾏通信(send()/recv());
6、返回,等待另⼀个连接请求;
7、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup());
(2)客户端
1、加载套接字库,创建套接字(WSAStartup()/socket());
2、向服务器发出连接请求(connect());
3、和服务器进⾏通信(send()/recv());
4、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup());
四、windows下实现socket简单实例
使⽤软件:devc++
(⼀)TCP协议
(1)代码
服务端:server.cpp
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[])
{
//初始化WSA
WORD sockVersion = MAKEWORD(2,2);
WSADATA wsaData;
if(WSAStartup(sockVersion, &wsaData)!=0)
{
return 0;
}
//创建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(slisten == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}
//绑定IP和端⼝
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR) {
printf("bind error !");
}
//开始监听
if(listen(slisten, 5) == SOCKET_ERROR)
{
printf("listen error !");
return 0;
}
//循环接收数据
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
char revData[255];
while (true)
{
printf("等待连接...\n");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen); if(sClient == INVALID_SOCKET)
{
printf("accept error !");
continue;
}
printf("接受到⼀个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
//接收数据
int ret = recv(sClient, revData, 255, 0);
if(ret > 0)
{
revData[ret] = 0x00;
printf(revData);
}
/
/发送数据
const char * sendData = "你好,TCP客户端!\n";
send(sClient, sendData, strlen(sendData), 0);
closesocket(sClient);
}
closesocket(slisten);
WSACleanup();
return 0;
}
客户端代码:client.cpp
#include<WINSOCK2.H>
#include<STDIO.H>
#include<iostream>
#include<cstring>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
int main()
{
WORD sockVersion = MAKEWORD(2, 2);
WSADATA data;
if(WSAStartup(sockVersion, &data)!=0)
{
return 0;
}
while(true){
SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sclient == INVALID_SOCKET)
{
printf("invalid socket!");
return 0;
}
sockaddr_in serAddr;
serAddr.sin_family = AF_INET;
serAddr.sin_port = htons(8888);
serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
if(connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
{ //连接失败
printf("connect error !");
closesocket(sclient);
return 0;
}
string data;
cin>>data;
const char * sendData;
sendData = data.c_str(); //string转const char*
//char * sendData = "你好,TCP服务端,我是客户端\n";
send(sclient, sendData, strlen(sendData), 0);
//send()⽤来将数据由指定的socket传给对⽅主机
//int send(int s, const void * msg, int len, unsigned int flags)
//s为已建⽴好连接的socket,msg指向数据内容,len则为数据长度,参数flags⼀般设0 //成功则返回实际传送出去的字符数,失败返回-1,错误原因存于error
char recData[255];
int ret = recv(sclient, recData, 255, 0);
if(ret>0){
recData[ret] = 0x00;
printf(recData);
}
closesocket(sclient);
}
WSACleanup();
return 0;
}
(2)可能遇到的问题:
(1)undefined reference to '_imp_WSAStartup'
解决⽅案:
⼯具->编译选项->在连接器命令⾏加⼊如下命令⾥⾯添加-lwsock32 即可
然后重启devc++运⾏程序问题解决。
(2)deprecated conversion from string constant to 'char *'[-Wwrite-strings]
解决⽅法:将char * 改为const char *
(3)结果运⾏
先运⾏服务端,运⾏service.cpp,服务端显⽰如下:
然后运⾏客户端,运⾏client.cpp,在客户端输⼊数据,即可传送到服务器端显⽰如下:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论