草庐IT

C++简单实现Scoket编程

君子以阅川 2023-11-14 原文

只实现了服务器端的服务
和其他语言一样,实现通信需要以下步骤

  1. 创建socket套接字
  2. 定义服务端地址和端口
  3. 服务端绑定地址和端口
  4. 服务端进行端口监听
  5. 服务端与客户端进行连接
  6. 服务端接收客户端发送的信息
  7. 服务端返回信息给客户端
  8. 关闭套接字

首先要导入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;
                }
            }
        }
    }
    

} 


有关C++简单实现Scoket编程的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  3. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  4. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  5. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  6. ruby - 使用 Ruby 通过 Outlook 发送消息的最简单方法是什么? - 2

    我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  9. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  10. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

随机推荐