只实现了服务器端的服务
和其他语言一样,实现通信需要以下步骤
- 创建socket套接字
- 定义服务端地址和端口
- 服务端绑定地址和端口
- 服务端进行端口监听
- 服务端与客户端进行连接
- 服务端接收客户端发送的信息
- 服务端返回信息给客户端
- 关闭套接字
首先要导入socket库
文章目录
1. 导入winsock.h库函数
#include<winsock.h>
2. 创建套接字
因为我们需要和客户端进行连接,所以既要创建服务端套接字,也要创建接收端套接字
SOCKET s_socket;
SOCkET s_accept;
3. 定义服务端地址和端口
SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET; //指定地址族为IPV4
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);// Host to network long INADDR_ANY对应0.0.0,0
server_addr.sin_port = htos(5010)// Host to newtwork short 设置端口
4. 绑定端口和地址
if(bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
cout<<"绑定失败"<<endl;
WSACleanup();
return;
}
cout<<"绑定成功"<<endl;
5. 开始监听
if(listen(s_server, SOMAXCONN) < 0)
{
cout<<"开启监听失败"<<endl;
WSACleanup();
return;
}
cout<<"开启监听成功"<<endl;
6. 创建接收套接字地址
当客户端与服务端进行连接时,需要创建里一个套接字地址来进行接收
SOCKADDR accept_addr;
7.接收套接字建立连接
int len = sizeof(SOCKADDR)
s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &sizeof(SOCKADDR));
if(s_accept == SOCKET_ERROR)
{
cout<<"建立连接失败";
WSACleanup();
return;
}
cout<<"建立连接成功"<<endl<<"准备接受数据";
8.死循环和目标机进行通信
while(true)
{
recv_len = recv(s_accept, recv_buf, 1024, 0);
if(recv_len < 0)
{
cout << "接受失败" <<endl;
break;
}
else{
cout << "客户信息:" << endl << recv_buf << endl;
}
string s= "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\nConnection: keep-alive\nCache-Control: no-store, no-cache, must-revalidate\nPragma: no-cache\nContent-Length: ";
String data = "<h1>Hello C++ Socket</h1>";
s = s + data.length() + "\n\n" + data ;
strcpy(send_bug, s.c_str());
memcpy(send_buf, ptr, 1024);
send_len = send(s_accept, send_buf, 1024, 0);
if(send_len < 0)
{
cout << "发送失败" << endl;
break;
}
}
9.关闭套接字
closesocket(s_server);
closesocket(s_accept);
WSACleanup();
retrun;
10.关于版本
当socket版本不符合时,是不能通信的
这就需要在通信前检测版本是否符合
void checkVision()
{
WORD w_req = MAKEWORD(2, 2);
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if(err!=0)
{
cout<<"初始化套接字库失败";
}
else
{
cout<<"初始化套接字库成功";
}
// 检测版本号
if(LOBYTE(wsadata.wVersion)!= 2|| HIBYTE(wsadata.wHighVersion)!=2 )
{
cout<<"套接字库版本号不符合";
WSACleanup();
}
else
{
cout<<"套接字库版本正确";
}
}
整体代码
使用的编译器是QtCraete,就简单使用qDebug来进行打印输出了
#include "selfsocket.h"
SelfSocket::SelfSocket()
{
WORD w_req = MAKEWORD(2, 2);
WSADATA wsadata;
int err;
err = WSAStartup(w_req, &wsadata);
if(err!=0)
{
qDebug()<<"初始化套接字库失败";
}
else
{
qDebug()<<"初始化套接字库成功";
}
// 检测版本号
if(LOBYTE(wsadata.wVersion)!= 2|| HIBYTE(wsadata.wHighVersion)!=2 )
{
qDebug()<<"套接字库版本号不符合";
WSACleanup();
}
else
{
qDebug()<<"套接字库版本正确";
}
}
void SelfSocket::Init()
{
//创建套接字
SOCKET s_server;
SOCKET s_accept;
s_server = socket(AF_INET, SOCK_STREAM, 0);// param: 协议族,协议类型,协议编号
//创建长度
int send_len, recv_len, len = 0;
// 创建接受和发送缓存区
char send_buf[1024];
char recv_buf[1024];
// 填充服务端信息:端口,地址
SOCKADDR_IN server_addr;
SOCKADDR_IN accept_addr;
server_addr.sin_family = AF_INET; //指定地址族为IPV4
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);// Host to network long INADDR_ANY对应0.0.0,0
server_addr.sin_port = htons(5010);// Host to network short
// 套接字绑定
if(bind(s_server,(SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR){
qDebug()<<"套接字绑定失败";
WSACleanup();
return;
}
qDebug()<<"套接字绑定成功";
// 开启监听
if(listen(s_server, SOMAXCONN) < 0)
{
qDebug()<<"监听开启失败";
WSACleanup();
return;
}
qDebug()<<"开启监听"<<endl<<"服务器监听连接中>>:";
// 接受连接请求
len = sizeof(SOCKADDR);
s_accept = accept(s_server,(SOCKADDR*)&accept_addr, &sizeof(SOCKADDR));
if(s_accept == SOCKET_ERROR)
{
qDebug()<<"建立连接失败";
WSACleanup();
return;
}
qDebug()<<"建立连接成功"<<endl<<"准备接受数据";
while(true)
{
recv_len = recv(s_accept, recv_buf, 1024, 0);
if(recv_len < 0)
{
qDebug()<<"接受失败"<<endl;
break;
}
else{
qDebug()<<"客户信息:"<<recv_buf << endl;
}
QString s= "HTTP/1.1 200 OK\nContent-Type: text/html; charset=UTF-8\nConnection: keep-alive\nCache-Control: no-store, no-cache, must-revalidate\nPragma: no-cache\nContent-Length: %1\n\n";
QString data = "<h1>Hello C++ Socket</h1>";
s += data;
s = s.arg(data.length());
qDebug()<<s.toLocal8Bit().data()<<endl;
QByteArray qb = s.toLatin1();
char *ptr;
ptr = qb.data();
memcpy(send_buf, ptr, 1024);
send_len = send(s_accept, send_buf, 1024, 0);
if(send_len < 0)
{
qDebug()<<"发送失败"<<endl;
break;
}
}
// 关闭套接字
closesocket(s_server);
closesocket(s_accept);
WSACleanup();
return;
}
运行后在浏览器输入:localhost:5010就能展示成功
11. linux下的socket服务器编写
基于socket下的epoll通知机制
底层采用红黑树,并非selest和poll的轮询单链表机制
没有fd表上限
每个fd都会绑定一个回调函数采用通知的方式来触发事件的执行
#include<sys/socket.h>
#include<sys/epoll.h>
#include<sys/fcntl.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<unistd.h>
#include<iostream>
#define BUFLEN 128
int main(int argc, char** argv){
int serverFd, clientFd;
int len, ret, rlen;
char buf[BUFLEN];
sockaddr_in serverAddr, clinetAddr;
if(argc!=2){
std::cout<<"Usage: " << argv[0] << "port" << std::endl;
return 0;
}
short port;
port = std::atoi(argv[1]);
len = sizeof(serverAddr);
serverFd = socket(AF_INET, SOCK_STREAM, 0);
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddr.sin_port = htons(port);
int reuse = 1;
setsockopt(serverFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
ret = bind(serverFd, (sockaddr*) &serverAddr, len);
if(ret<0){
std::cout<<"Failed to bind"<<std::endl;
return -1;
}
ret = listen(serverFd, 10);
if(ret<0){
std::cout<<"Failed to bind"<< std::endl;
return -1;
}
int epfd,epct, i;
epoll_event event;
epoll_event events[20];
memset(events, 0, 20*sizeof(epoll_event));
epfd = epoll_create(1);
event.data.fd = serverFd;
event.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, serverFd, &event);
while (true)
{
epct = epoll_wait(epfd, events, 20 , -1);
for(int i = 0; i<epct; ++i){
if(events[i].data.fd == serverFd){
clientFd = accept(events[i].data.fd, (sockaddr*)&clinetAddr,(socklen_t *)&len);
std::cout<<"new fd = "<<clientFd <<" ip = " << inet_ntoa(clinetAddr.sin_addr);
event.data.fd = clientFd;
event.events = EPOLLET | EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, clientFd, &event);
}else{
memset(buf, 0, BUFLEN);
rlen = read(events[i].data.fd, buf, BUFLEN);
if(rlen < 0){
std::cout<<"fd "<< clientFd << "disconnected"<< std::endl;
close(events[i].data.fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, &event);
continue;
}
}
}
}
}