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

2022-11-14 14:36:17 浏览数 (1)

文章目录

  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连接过程

服务端用到的几个网络编程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);

参考

  • 《UNIX网络编程(卷一)》
  • sockaddr和sockaddr_in的区别

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

0 人点赞