1.从简单的socket编程开始

socket也就是套接字,套接字对于程序猿来说是一套网络通信的接口,我们使用这套接口就可以完成网络通信。网络通信的主体主要可以分成2部分:客户端和服务端。而这里主要关注服务器端。

而之前也说了,我们这个服务器是基于TCP通信的。

1.TCP通信流程

TCP是一个面向连接的,安全可靠的,流式传输协议,这个协议是在传输层。

 2.服务器端的通信流程

1.创建用于监听的套接字(文件描述符)

// 创建一个套接字, 函数原型
int socket(int domain, int type, int protocol);

//使用
int fd=socket(AF_INET,SOCK_STREAM,0);

使用这个函数需要包含头文件<sys/socket.h>,若是已包含头文件<arpa/inet.h>,则前者不需要再包含了。

参数说明:

  • domain:使用的地址族协议(AF_INET表示使用IPv4格式的ip,AF_INET6表示使用IPv6格式的ip)
  • type:数据的传输协议(SOCK_STREAM表示使用流式的传输协议(TCP),SOCK_DGREAM表示使用报文式的传输协议(UDP))
  • protocol:一般写0就行,表示使用默认的协议

返回值:

  • 成功放回可用于套接字通信的文件描述符,失败返回-1.

2.将文件描述符和本地的ip地址和端口进行绑定

//函数原型
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

//使用
//设置服务器的地址族,ip和端口
strcut sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_IENT;
addr.sin_port=htons(10000);
addr.sin_addr.s_addr=htonl(INADDR_ANY);

int ret=bind(fd,(struct sockaddr*)&addr,sizeof(addr));

参数:

  • sockfd:服务器监听的文件描述符,是通过前面的socket()函数调用得到的返回值
  • addr:这是个传入参数,要讲绑定的ip和端口信息初始化到这个结构体中
  • addrlen:参数addr指向的内存大小,sizeof(struct sockaddr);

返回值:

  • 成功返回0,失败返回-1.

3.给监听的套接字设置监听

//函数原型
int listen(int sockfd,int backlog);

//使用
int ret=listen(fd,128);

 参数:

  • sockfd:文件描述符,也是通过socket()得到,但需要在使用bind()之后才可以给listen()使用
  • backlog:同时能处理的最大连接要求数量。

返回值:

  • 成功返回0,失败返回-1。

4.等待接受客户端的连接请求

// 函数原型	
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

//使用
struct sockaddr* caddr;
memset(&caddr,0,sizeof(caddr));
socklen_t len=sizeof(caddr);
int cfd=accept(fd,(struct sockaddr*)&caddr,&len);

参数:

  • sockfd:文件描述符,也是通过socket()得到。
  • addr:传出参数,内部是存储了简历连接的客户端的地址信息
  • addrlen:传出参数,存储addr指向的内存大小

返回值:

  • 成功得到一个文件描述符,用来和建立连接的这个客户端进行通信,失败返回-1。

注意:这个函数是一个阻塞函数,当没有新的客户端连接请求的时候,该函数会阻塞等待;当检测到有新的客户端连接请求时,阻塞解除,新连接就建立了,得到的返回值也是一个文件描述符,基于这个文件描述符就可以和客户端通信了。

5.与客户端进行通信

// 接收数据
    ssize_t read(int sockfd, void *buf, size_t size);
    ssize_t recv(int sockfd, void *buf, size_t size, int flags);

//使用
    char buf[1024]={0};
    int len=read(cfd,buf,sizeof(buf));

参数:

  • sockfd: 用于通信的文件描述符,即accept () 函数的返回值
  • buf: 指向一块有效内存,用于存储接收的数据
  • size: 参数 buf 指向的内存的容量
  • flags: 特殊的属性,一般指定为 0

返回值:有三种情况

  • 大于 0:实际接收的字节数
  • 等于 0:对方断开了连接
  • -1:接收数据失败了
// 发送数据的函数   函数原型
    ssize_t write(int fd, const void *buf, size_t len);
    ssize_t send(int fd, const void *buf, size_t len, int flags);

//使用
    char buf[1024]="23234234";
    int size=write(cfd,buf,strlen(buf));

参数:

  • fd: 通信的文件描述符,accept () 函数的返回值
  • buf: 传入参数,要发送的字符串
  • len: 要发送的字符串的长度
  • flags: 特殊的属性,一般不使用,指定为 0

返回值:

  • 大于 0:实际发送的字节数,和参数 len 是相等的
  • -1:发送数据失败了

剩下的就还有客户端使用的函数

//函数原型
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

//客户端使用
 // 连接服务器
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(10000);  
inet_pton(AF_INET, "1.1.1.1", &addr.sin_addr.s_addr);//第二个参数是服务端的ip地址

int ret=connect(fd,(struct sockaddr*)&addr,sizeof(addr));

按照流程图可以搭建出个简单的echo服务器了。代码放在GitHub上,code/server_v1内。

需要先按照自己电脑的linux环境的ip地址修改client.cpp的ip,接着使用make命令编译后,打开两个终端,一个先输入./Server运行服务端,另一终端再输入./client运行客户端,这样就可以进行通信了,这只是简单的回显(客户端发送什么,服务端就给客户端返回原来的信息)。

这里演示的代码为了简便,代码块内的使用是没有写出错处理的代码,这编程过程中是需要写的。在完整源代码内是写了出错处理的,这是为了可以快速定位到程序出错的位置和发生的错误。

这一节就用socket编程简单实现了个echo服务器。

完整源代码:https://github.com/liwook/CPPServer/tree/main/code/server_v1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值