API介绍

include <sys/socket.h>

int socket(int family,int type,int protocol)

@family:协议族,为0的时候系统根据后两个参数选择协议. 常用AF_INET
@type: 套接字类型,这边区分套接字是TCP还是UDP .如果是TCP就用SOCK_STREAM, 数据报套接字SOCK_DGRAM是UDP使用
@protocol: IPPROTO_TCP/IPROTO_UDP/IPROTO_SCTP 分别对应三种传输协议.如果是TCP直接写0也可以
返回值: 成功的时候返回非负的套接字.

int connect(int sockfd,const struct sockaddr * servaddr,socklen_t addrlen)

//相应结构体定义
struct sockaddr {  
     sa_family_t sin_family;//地址族
    char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息               

};

struct sockaddr_in
{

    short sin_family; //一般来说,AF_INET(地址族)PF_INET(协议族)

    unsigned short sin_port;      /* Portnumber (必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/

    struct in_addr sin_addr;        /* Internet address. */

    unsigned char sin_zero[8];  

    /* 字符数组sin_zero[8]的存在是为了保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等 */
};


int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
//成功返回0  出错返回-1 
//绑定端口和IP

int listen(int sockfd,int backlog); 

int accept(int sockfd,struct sockaddr * cliaddr,socklen_t *addrlen);
//连接



关于listen 的作用

当我们调用socket()创建一个套接字时,这个套接字是一个主动连接的套接字,通过listen()将这个套接字转换为被动连接的,指定内核应该接受指向该套接字的连接请求。

这时候socket的状态从closed转换到listen状态

第二个参数指定了内核应该为相应套接字排队的最大连接数。内核会为每一个给定的监听套接字维护两个队列:完成连接队列和未完成连接队列。

完成连接队列: 在这个队列中的套接字状态都是ESTABLISHED

未完成连接队列: 众所周知,三次握手最后一次的的包是从客户端发出的,服务器端的套接字需处SYN_RCVD状态等待响应的ACK包发送过来,这个队列存放的就是这样的套接字

SYN泛滥

通过高速率向服务端发送SYN包来将这个未完成连接队列填满,同时在每个SYN的源地址IP都设置 成未知数,这样服务端的下一个ACK包就不知道发往什么地方,进而导致整个队列为满,使得合法的SYN排不上队,导致合法客户的连接请求被拒绝.

问题

服务器简单实现了并发,同时也解决僵尸进程的问题,但是这个服务器有一个问题,当客户端和服务器建立连接之后,我们通过kill杀死服务器中客户端对应的子进程时,客户端此时正阻塞在输入中,当我们再次输入文本的时候他将返回terminated prematurely .

这是一个错误信息,因为此时客户端正在应对两个描述符:套接字和文本输入.此时阻塞在文本输入中,但是正常来说当服务器发送FIN时我们总是想要客户端能够正确马上响应,所以程序应该阻塞在其中任何一个源的输入上.这也是select和poll两个函数的目的.

代码

server

/*************************************************************************
	> File Name: main.cpp
	> Author: 
	> Mail: 
	> Created Time: 2020年01月26日 星期日 16时39分52秒
 ************************************************************************/
 # include <signal.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <sys/socket.h>
# include <unistd.h>
# include <string.h>
# include <stdlib.h>
# include <ctype.h>
# include <errno.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/ioctl.h>
# include <stdarg.h>
# include <fcntl.h>

# include<iostream>
using namespace std;
typedef void Sigfunc(int);

void sig_chld(int signo)
{
    pid_t pid;
    int stat;
    if((pid=waitpid(-1,&stat,WNOHANG))>0)
        cout<<"child terminated\n"<<endl;
    return ;
}
Sigfunc* 
signal_bind(int signo,Sigfunc* func)
{
    struct sigaction act,oact;
    act.sa_handler=func;
    sigemptyset(&act.sa_mask);
    act.sa_flags=0;
    act.sa_flags|=SA_RESTART;
    if(sigaction(signo,&act,&oact)<=0)
        return SIG_ERR;
    return oact.sa_handler;

}
void 
str_echo(int fd)
{
    size_t n;
    char buf[4096];
    cout<<"str_echo"<<endl;
    while((n=read(fd,buf,4096))>0)
    {
        write(fd,buf,strlen(buf));
        memset(buf,0,sizeof(char)*4096);
    }
    if(n<=0)
        cout<<"str_echo:read error"<<endl;
}
int
main(int argc,char ** argv)
{
    int listenfd,connfd;
    pid_t childpid;
    socklen_t cliadr_len;
    struct sockaddr_in cliaddr,servaddr;
    signal_bind(SIGCHLD,sig_chld);
    if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
    {
        cout<<"socket error"<<endl;
        exit(0);
    }

    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servaddr.sin_port=htons(8888);
    if(bind(listenfd,(sockaddr *)&servaddr,sizeof(cliaddr))<0)
    {
        cout<<"bind error"<<endl;
        exit(0);
    }
    if(listen(listenfd,20)<0){
        cout<<"listen error"<<endl;
        exit(0);
    }
    for(;;)
    {
        cliadr_len=sizeof(cliaddr);
        connfd=accept(listenfd,(sockaddr*)&cliaddr,&cliadr_len);
        if(errno==EINTR)
            continue;
        if(connfd<0)
            exit(0);
        if((childpid=fork())==0)
        {
            //child
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        close(connfd);
    }


}

client


/*************************************************************************
	> File Name: main.cpp
	> Author: 
	> Mail: 
	> Created Time: 2020年01月26日 星期日 16时39分52秒
 ************************************************************************/
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
# include <unistd.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <ctype.h>
# include <errno.h>
# include <malloc.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <sys/ioctl.h>
# include <stdarg.h>
# include <fcntl.h>
# include <fcntl.h>

# include<iostream>
using namespace std;
void 
str_cin(int sockfd)
{
    char sendline[4096],recvline[4096];
    while(1)
    {
        cin>>sendline;
        
        write(sockfd,sendline,strlen(sendline));
        if(read(sockfd,recvline,4096)<=0)
        {
            cout<<"str_cli:server terminated permaturely";
            exit(0);
        }
        cout<<recvline<<endl;
        memset(recvline,0,sizeof(char)*4096);

    }
}
int
main(int argc,char ** argv)
{
    int sockfd;
    struct sockaddr_in servaddr;
    if(argc<2)
    {
        cout<<"usage:tcpcli <IPaddress>"<<endl;
        exit(0);


    }
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(8888);
    inet_pton(AF_INET,argv[1],&servaddr.sin_addr);
    connect(sockfd,(struct sockaddr * )&servaddr,sizeof(servaddr));
    str_cin(sockfd);
    return 0;

}