基于Linux的C++之网络编程

套接字的基本概念

通信类型: 控制套接字如何传输和处理数据,数据以包的形式传输

  • 连接(connection)类型:确保所有包依序传输,如果丢包,则请求重传
  • 数据报(datagram)类型:不保证包的到达顺序,包可能丢失

名空间: 指定套接字地址格式

  • 本地名空间:套接字地址为普通文件名
  • Internet名空间:套接字地址由Internet地址和端口号(用于区分一台主机上的多个套接字)确定

协议: 确定数据如何传输

套接字函数

socket()函数: 创建套接字

  • 原型:int socket(int domain,int type,int protocol);
  • 参数:名空间、通信类型和协议
  • 名空间:PF_LOCAL(本地)或PF_INET(Internet)
  • 通信类型:SOCK_STREAM(连接类型)或SOCK_DGRAM(数据报类型)
  • 协议:传递0,让系统自动选择协议(通常为最佳协议)
  • 返回值:套接字描述符

close()函数: 释放套接字

  • 原型:int close(int fd);

connect()函数: 创建两个套接字之间的连接

  • 客户发起此系统调用,试图与服务器建立套接字连接
  • 原型:int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
  • 参数:sockfd 为文件描述符; addr 为指向套接字地址结构体指针(服务器地址);addrlen 为服务器地址字符串的长度
  • 返回值:0 连接成功; -1 连接失败

send()函数: 发送数据

  • 原型: ssize_t send(int sockfd,const void* buf,size_t len,int flags);
  • 只有在套接字处于连接状态时才可调用

bind()函数: 绑定套接字与其服务器地址

  • 原型:int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);

listen()函数: 侦听客户连接

  • 原型:int listen(int sockfd,int backlog);
  • 参数:backlog 指定有多少个连接可以进入队列,超出该值的连接将被抛弃

accept()函数: 接受连接,为该连接创建一个新的套接字

  • 原型 int accept(int sockfd,struct sockaddr* addr,socklen_t addrlen);
  • 参数: addr 为指向套接字地址结构体(客户地址)的指针
  • 返回值:创建一个新的套接字,以接受客户连接,返回值为新的套接字文件描述符
  • 原先套接字文件描述符可以继续接受新连接

本地套接字示例:服务器端

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

//持续读取消息,直到套接字关闭或接收到客户发送的“quit”消息
// 前者返回true,后者返回 false,服务器随后将停止服务

bool Serve(int client_socket)
{
  while (true) {
    int length;
    char* msg;
    //从套接字中读取文本消息的长度,返回值为0表示客户连接已关闭
    if(read(client_socket,&length,sizeof(length)) == 0)
      return true;
    msg = new char[length];
    read(client_socket,msg,length);
    std::cout<<msg<<std::endl;
    if(!strcmp(msg,"quit"))  {delete[] msg,msg = NULL;return false;}
    else delete[] msg, msg = NULL;
  }
}

int main(int argc,char* const argv[])
{
  const char* const socket_name = argv[1];
  ing socket_fd;
  struct sockaddr_un name;
  bool serving = true;
  //创建套接字
  socket_fd = socket(PF_LOCAL,SOCK_STREAM,0);
  //设定服务器性质
  name.sun_family = AF_LOCAL;
  strcpy(name.sun_path,socket_name);
  // 绑定套接字
  bind(socket_fd,(struct sockaddr*)&name,SUN_LEN(&name));
  //侦听客户连接
  listen(socket_fd,5);
  //重复接受连接,直到某个客户发出“quit”消息
  while(serving)
  {
    struct sockaddr_un client_name;
    socklen_t client_name_len;
    int client_socket_fd;
    //接受客户连接请求
    client_socket_fd = accept(socket_fd,(struct sockaddr*)&client_name,&client_name_len);
    serving = Serve(client_socket_fd); //服务连接请求
    close(socket_fd);//关闭客户连接
  }
  close(socket_fd);
  unlink(socket_name); //删除套接字文件
  return 0;
}

本地套接字示例,客户端

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

void SendMsg(int socket_fd,const char* msg)
{
  int length = strlen(msg) + 1;
  write(socket_fd,&length,sizeof(length));
  write(socket_fd,msg,length);
}
int main(int argc, char const *argv[]) {
  const char* const socket_name = argv[1];
  const char* const msg = argv[2];
  int socket_fd;
  struct sockaddr_un name;
  //创建套接字
  socket_fd = socket(PF_LOCAL,SOCK_STREAM,0);
  //套接字地址中存储服务器名称
  name.sun_family = AF_LOCAL;
  strcpy(name.sun_path,socket_name);
  //连接
  connect(socket_fd,(struct sockaddr*)&name,SUN_LEN(&name));
  //发送消息
  SendMsg(socket_fd,msg);
  close(socket_fd);
  return 0;
}

本地套接字示例:运行

程序测试运行

  • 编译链接服务端程序和客户端程序
  • 进入服务端程序目录,在终端输入:./server/tmp/socket; ./server 为服务器端程序名, /tmp/socket 为本服务器启动后的套接字文件名
  • 进入客户端程序目录,在终端中输入:./client/tmp/socket"Hello World!"; ./client 为客户端程序名
  • 停止服务器,在客户端输入命令:./client/tmp/socket"quit"

网络套接字示例:客户端

#include <iostream>
#include <stdio.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

// 请求Web服务器的主页
void GetHomepage(int socket_fd)
{
  char buffer[8192];
  sprintf(buffer,"GET /
");
  write(socket_fd,buffer,strlen(buffer));
  while(true){
    ssize_t count = read(socket_fd,buffer,8192);
    if(count == 0) return;
    fwrite(buffer,sizeof(char),count,stdout);
  }
}

int main(int argc, char const *argv[])
{
  int socket_fd;
  struct sockaddr_in name;
  struct hostent* hostinfo;
  socket_fd = socket(PF_INET,SOCK_STREAM,0);
  name.sin_family = AF_INET;
  hostinfo =  gethostbname(argv[1]);
  if(hostinfo == NULL) return 1;
  else name.sin_addr = *((struct in_addr*)hostinfo ->h_addr);
  name.sin_port = htons(80);
  if(connect(socket_fd,(struct in_addr*)&name,sizeof(struct sockaddr_in)) == -1) {
  perror("Failure to connect the server.");
  return 2;
}
  GetHomepage(socket_fd);
  return 0;
}

“`

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一门linuxc++通讯架构实战课程,针对c/c++语言已经掌握的很熟并希望进一步深造以将来用c++linux下从事网络通讯领域/网络服务器的开发和架构工作。这门课程学习难度颇高但也有着极其优渥的薪水(最少30K月薪,最高可达60-80K月薪),这门课程,会先从nginx源码的分析和讲解开始,逐步开始书写属于自己的高性能服务器框架代码,完善个人代码库,这些,将会是您日后能取得高薪的重要筹码。本课程原计划带着大家逐行写代码,但因为代码实在过于复杂和精细,带着写代码可能会造成每节课至少要4~5小时的超长时间,所以老师会在课前先写好代码,主要的时间花费在逐行讲解这些代码上,这一点望同学们周知。如果你觉得非要老师领着写代码才行的话,老师会觉得你当前可能学习本门课程会比较吃力,请不要购买本课程,以免听不懂课程并给老师差评,差评也会非常影响老师课程的销售并造成其他同学的误解。 这门课程要求您具备下面的技能:(1)对c/c++语言掌握的非常熟练,语言本身已经不是继续学习的障碍,并不要求您一定熟悉网络或者linux;(2)对网络通讯架构领域有兴趣、勇于挑战这个高难度的开发领域并期望用大量的付出换取高薪;在这门课程中,实现了一个完整的项目,其中包括通讯框架和业务逻辑框架,浓缩总结起来包括如下几点:(1)项目本身是一个极完整的多线程高并发的服务器程序;(2)按照包头包体格式正确的接收客户端发送过来的数据包, 完美解决收包时的数据粘包问题;(3)根据收到的包的不同来执行不同的业务处理逻辑;(4)把业务处理产生的结果数据包正确返回给客户端;本项目用到的主要开发技术和特色包括:(1)epoll高并发通讯技术,用到的触发模式是epoll中的水平触发模式【LT】;(2)自己写了一套线程池来处理业务逻辑,调用适当的业务逻辑处理函数处理业务并返回给客户端处理结果;(3)线程之间的同步技术包括互斥量,信号量等等;(4)连接池中连接的延迟回收技术,这是整个项目中的精华技术,极大程度上消除诸多导致服务器程序工作不稳定的因素;(5)专门处理数据发送的一整套数据发送逻辑以及对应的发送线程;(6)其他次要技术,包括信号、日志打印、fork()子进程、守护进程等等;

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值