前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >网络编程入门_回显服务器

网络编程入门_回显服务器

作者头像
yifei_
发布2022-11-14 14:36:17
6800
发布2022-11-14 14:36:17
举报
文章被收录于专栏:yifei的专栏

文章目录

  1. 1. code
  2. 2. TCP连接过程
  3. 3. 服务端用到的几个网络编程api
    1. 3.1. 建立socket套接字
    2. 3.2. sockaddr_in结构
    3. 3.3. bzero置零函数
    4. 3.4. 字节序转换hton函数
    5. 3.5. bind将ip和端口绑定到socket
    6. 3.6. 监听套接字listen
    7. 3.7. 接受连接accept
  4. 4. 客户端用到的api
    1. 4.1. 发起连接请求connect
  5. 5. 参考

以回显服务器为例,整理一下网络编程的基础知识。

code

服务端代码

代码语言:javascript
复制
#include <iostream>
#include "unp.h"
using namespace std;

int main(){
    int listenfd,connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr,servaddr;
    listenfd=socket(AF_INET,SOCK_STREAM,0);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servaddr.sin_port=htons(SERV_PORT);
    bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
    listen(listenfd,LISTENQ);
    int i=0;
    for(;;){
        clilen=sizeof(cliaddr);
        connfd=accept(listenfd,(SA*)&cliaddr,&clilen);
        if((childpid=fork())==0){
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        close(connfd);
        i++;
        cout<<i<<" "<<endl;
    }


    return 0;
}

void str_echo(int sockfd){
    ssize_t n;
    char buf[MAXLINE];
again:
    while((n=read(sockfd,buf,MAXLINE))>0){
        write(sockfd,buf,n);
    }
    if(n<0 && errno==EINTR){
        goto again;
    }else if(n<0){
        cout<<"str_echo: read error"<<endl;
    }
}

客户端程序

代码语言:javascript
复制
#include <iostream>
#include "unp.h"
using namespace std;

int main(int argc,char **argv){
    int sockfd;
    struct sockaddr_in servaddr;
    if(argc!=2){
        cout<<"usage:echoclie <ip addr>"<<endl;
        return 0;
    }
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd<0){
        cout<<"socket create error"<<endl;
        return 0;
    }
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(SERV_PORT);
    inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
    int conn=connect(sockfd,(SA*)&servaddr,sizeof(servaddr));
    if(conn<0){
        cout<<"connect create error"<<endl;
        return 0;
    }
    str_cli(stdin,sockfd);
    return 0;
}

void str_cli(FILE *fp,int sockfd){
    char sendline[MAXLINE],recvline[MAXLINE];
    while(fgets(sendline,MAXLINE,fp) != NULL){
        write(sockfd,sendline,strlen(sendline));
        //if(readline(sockfd,recvline,MAXLINE)==0){
        if(read(sockfd,recvline,MAXLINE)<0){
            cout<<"str_cli:server terminated prematurely"<<endl;
        }
        fputs(recvline,stdout);
    }
}

TCP连接过程

TCP连接过程
TCP连接过程

服务端用到的几个网络编程api

建立socket套接字

函数原型

代码语言:javascript
复制
int socket(int domain, int type, int protocol);
domain 具体通信的域
    AF_UNIX      Local communication
    AF_LOCAL     Synonym for AF_UNIX
    AF_INET(常用) IPv4 Internet protocols
type 通信类型
    SOCK_STREAM(常用) Provides sequenced, reliable, two-way, connection-
                       based byte streams.  An out-of-band data transmission
                       mechanism may be supported.
    SOCK_DGRAM      Supports datagrams (connectionless, unreliable
                       messages of a fixed maximum length).
protocol 用来设置用tcp还是udp,一般为0
sockaddr_in结构
代码语言:javascript
复制
include <netinet/in.h>

struct sockaddr {
    unsigned short    sa_family;    // 2 bytes address family, AF_xxx
    char              sa_data[14];     // 14 bytes of protocol address
};

// IPv4 AF_INET sockets:

struct sockaddr_in {
    short            sin_family;       // 2 bytes e.g. AF_INET, AF_INET6
    unsigned short   sin_port;    // 2 bytes e.g. htons(3490)
    struct in_addr   sin_addr;     // 4 bytes see struct in_addr, below
    char             sin_zero[8];     // 8 bytes zero this if you want to
};

struct in_addr {
    unsigned long s_addr;          // 4 bytes load with inet_pton()
};

sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别: 程序员不应操作sockaddr,sockaddr是给操作系统用的 程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。 一般的用法为: 程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数

bzero置零函数

memset()函数也可完成这个功能,但是在填写参数时容易出错并且编译器不会提示,所以改用bzero

代码语言:javascript
复制
include <strings.h>
void bzero(void *s, size_t n);
%%%%
bzero(&servaddr,sizeof(servaddr));
字节序转换hton函数

servaddr.sin_addr.s_addr=htonl(INADDR_ANY);

代码语言:javascript
复制
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
The htonl() function converts the unsigned integer hostlong from host byte order to network byte order.
用来将主机字节序的无符号整型转换为网络字节序
bind将ip和端口绑定到socket
代码语言:javascript
复制
bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
函数原型:
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
sockfd socket套接字的描述符
addr 储存ip和端口
addrlen 前一个参数结构体的所占字节长度
监听套接字listen

listen(listenfd,LISTENQ);

代码语言:javascript
复制
include <sys/socket.h>
int listen(int socket,int backlog)
socket 监听的套接字描述符
backlog
    系统维护两个队列,分别是已完成三路握手的队列和未完成的,两个队列的长度不会超过backlog
接受连接accept
代码语言:javascript
复制
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
该函数会提取未完成队列中的第一个连接请求,完成三路握手,然后返回一个文件描述符,后续可以用来读写。
如果未完成队列为空,则默认会阻塞。

客户端用到的api

发起连接请求connect
代码语言:javascript
复制
int conn=connect(sockfd,(SA*)&servaddr,sizeof(servaddr));
客户端有connect()来发起请求。
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

参考

欢迎与我分享你的看法。 转载请注明出处:http://taowusheng.cn/

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-09-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • code
  • TCP连接过程
  • 服务端用到的几个网络编程api
    • 建立socket套接字
      • sockaddr_in结构
        • bzero置零函数
          • 字节序转换hton函数
            • bind将ip和端口绑定到socket
              • 监听套接字listen
                • 接受连接accept
                • 客户端用到的api
                  • 发起连接请求connect
                  • 参考
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档