TCP套接字(server/client实现)

来源:互联网 发布:面向对象编程什么意思 编辑:程序博客网 时间:2024/05/24 01:11

套接字

  • 概念:源IP地址和目的IP地址以及源端口号和目的端口号的组合称为套接字。其用于标识客户端请求的服务器和服务。
  • 分类:
    • 流套接字(SOCK_STREAM)
    • 数据报套接字(SOCK_DGRAM)
    • 原始套接字(SOCK_RAW)

TCP套接字(流套接字)

  • TCP套接字工作流程:
    • 首先,服务器端启动进程,调用Socket创建一个基于TCP协议的流套接字描述符。
    • 其次,服务进程调用bind命名套接字,将套接字描述符绑定到本地地址和本地端口上。
    • 再次,服务器端调用listen,开始侦听客户端的Socket连接请求。
    • 接下来,客户端创建套接字描述符,并且调用connect向服务器端提交连接请求。服务器端接收到客户端连接请求后,调用accept,接受并创建一个新的套接字描述符与客户端建立连接,然后原套接字描述符继续侦听客户端的连接请求。
    • 客户端与服务器端新套接字进行数据传输,调用write或send向对方发送数据,调用read或recv接收数据。
  • 代码
server.c#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<unistd.h>#include<stdlib.h>#include<netinet/in.h>#include<arpa/inet.h>#include<string.h>#include<pthread.h>static void usage(const char* proc){    printf("usage: %s [local_ip] [local_port]\n",proc);}int start_up(const char* _ip,int _port){    int sk = socket(AF_INET,SOCK_STREAM,0);    if(sk < 0)    {        perror("socket");        exit(2);    }    struct sockaddr_in local;    local.sin_family = AF_INET;    local.sin_port = htons(_port);    local.sin_addr.s_addr = inet_addr(_ip);    if(bind(sk,(struct sockaddr *)&local,sizeof(local)) < 0)    {        perror("bind");        exit(3);    }    if(listen(sk,10) < 0)    {        perror("listen");        exit(4);    }    return sk;}void* handlerquest(void*arg){//  close(listen_sk);    int new_sk = (int)arg;    while(1)    {        char buf[1024];        ssize_t s = read(new_sk,buf,sizeof(buf)-1);        if(s > 0)        {            buf[s] = 0;            printf("client:%s\n",buf);            write(new_sk,buf,strlen(buf));        }        else        {            close(new_sk);            printf("client quit ...\n");            break;        }    }}int main(int argc,char* argv[]){    if(argc != 3)    {        usage(argv[0]);        return 1;    }    int listen_sk = start_up(argv[1],atoi(argv[2]));    while(1)    {        struct sockaddr_in client;        socklen_t len = sizeof(client);        int new_sk = accept(listen_sk,(struct sockaddr*)(&client),&len);        if(new_sk < 0)        {            perror("accept");            continue;        }        printf("Get a new client %s : %d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));//多线程        pthread_t id;        pthread_create(&id,NULL,handlerquest,(void*)new_sk);        pthread_detach(id);//多进程    //  pid_t id = fork();    //  if(id < 0)    //  {    //      close(new_sk);    //  }    //  else if(id == 0)    //  {    //      close(listen_sk);    //      if(fork()<0)    //      {    //          exit(0);    //      }    //      while(1)    //      {    //          char buf[1024];    //          ssize_t s = read(new_sk,buf,sizeof(buf)-1);    //          if(s > 0)    //          {    //              buf[s] = 0;    //              printf("client:%s\n",buf);    //              write(new_sk,buf,strlen(buf));    //          }    //          else    //          {    //              close(new_sk);    //              printf("client quit ...\n");    //              break;    //          }    //      }    //      close(new_sk);    //  }    //  else    //  {    //      close(new_sk);    //  }//普通版    //  while(1)    //  {    //      char buf[1024];    //      ssize_t s = read(new_sk,buf,sizeof(buf)-1);    //      if(s > 0)    //      {    //          buf[s] = 0;    //          printf("client:%s\n",buf);    //          write(new_sk,buf,strlen(buf));    //      }    //      else    //      {    //          close(new_sk);    //          printf("client quit ...\n");    //          break;    //      }    //  }    }    return 0;}
client.c#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<string.h>#include<stdlib.h>#include<netinet/in.h>#include<arpa/inet.h>void usage(const char* proc){    printf("usage:%s [local_ip] [local_port]\n");}int main(int argc,char* argv[]){    if(argc != 3)    {        usage(argv[0]);        return 1;    }    int sk = socket(AF_INET,SOCK_STREAM,0);    if(sk <0 )    {        perror("socket");        return 1;    }    struct sockaddr_in server;    server.sin_family = AF_INET;    server.sin_port = htons(atoi(argv[2]));    server.sin_addr.s_addr = inet_addr(argv[1]);     if(connect(sk,(struct sockaddr*)&server,sizeof(server))<0)    {        perror("connect");        exit(1);    }    char buf[1024];    while(1)    {        printf("Please Enter#:");        fflush(stdout);        ssize_t s = read(0,buf,sizeof(buf)-1);        if(s > 0)        {            buf[s-1] = 0;            write(sk,buf,strlen(buf));            s = read(sk,buf,sizeof(buf)-1);            if(s > 0)            {                buf[s] = 0;                printf("server echo# %s\n",buf);            }        }    }    return 0;}
makefile.PHONY:allall:tcp_server tcp_clienttcp_client:tcp_client.c    gcc -o $@ $^ tcp_server:tcp_server.c    gcc -o $@ $^ -lpthread.PHONY:cleanclean:    rm -f tcp_client tcp_serve

服务器退出,客户端不退出,当再次运行服务器(与之前的端口号相同),就会出现绑定失败。
  • 原因:
    主动关闭的一方在发送最后一个ack 后就会进入TIME_WAIT状态 停留2MSL(max segment lifetime)时间。这个TCP/IP必不可少的,也就是“解决”不了的。也就是TCP/IP设计者本来是这么设计的。主要有两个原因
    • 防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
    • 可靠的关闭TCP连接
      在主动关闭方发送的最后一个ack(fin) ,有可能丢失,这时被动方会重新发 fin, 如果这时主动方处于CLOSED 状态 ,就会响应rst 而不是ack。所以主动方要处于TIME_WAIT 状态,而不能是CLOSED 。
      TIME_WAIT 并不会占用很大资源的,除非受到攻击。还有,如果一方send 或recv 超时,就会直接进入CLOSED 状态
      [http://blog.csdn.net/acs713/article/details/28427181]
原创粉丝点击