TCP编程

  • TCP是一种面向连接的、可靠的、基于字节流的传输协议。
  • TCP具有以下特点:
  • 每一次完整的数据传输都要经过建立连接、使用连接、终止连接的过程
  • 可靠、出错传输、且没收到一个数据都要给出相应的确认,保证数据传输的可靠性

TCP的网络编程开发分为服务器端和客户端两部分,常见的步骤如下:
在这里插入图片描述
注意:通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来连接服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合,这就是为什么服务器在listen之前会调用bind(),而客户端就不需要调用,而是在connect()时由系统随机生成一个
TCP客户端编程(几个函数的介绍):
所需头文件:

#include<sys/socket.h>

1.函数:

int socket(int family,int type,int protocol);//创建一个用于网路通信的socket套接字(描述符)

2.参数:

  • family:这里用AF_INET,代表IPv4
  • type:这里用SOCK_STREAM,代表TCP数据流
  • protocol:这里写0,设置0表示使用默认协议

3.返回值:

  • 成功:套接字
  • 失败:<0的值

1.函数:

int connect(int sockfd,const struct sockaddr* addr,socklen_t len);//主动跟服务器建立连接

2.参数:

  • sockfd:sock()函数返回的套接字
  • addr:连接的服务器地址结构
  • len:地址结构体长度

3.返回值:

  • 成功:0
  • 失败:-1

1.函数:

size_t send(int sockfd,const void* buf,size_t nbytes,int flag);//发送数据,最后一个参数为0时,可以用write()替代。不能用TCP协议发送0长度的数据包,数据没有发送成功,内核会自动重发。

2.参数:

  • sockfd:已建立连接的套接字
  • buf:发送数据的地址
  • nbytes:发送数据的大小(单位为字节)
  • flags:套接字标志(常为0)

3.返回值:

  • 成功:成功发送的字节数
  • 失败:<0的值

对于客户端,也可以接收数据,前提是客户端先给服务端发送数据。
1.函数:

ssize_t recv(int sockfd,void *buf,size_t nbytes,int flags);//接收网络数据,在默认的情况下,如果没有接收到数据,函数会阻塞,直到有数据接收

2.参数:

  • sockfd:套接字
  • buf:接受网络数据的缓冲区的地址
  • nbytes:接收缓冲区的大小(单位为字节)
  • flags:套接字标志(常为0)

3.返回值:

  • 成功:接收的字节数
  • 失败:<0的值

TCP服务器编程(函数的介绍):
作为TCP服务器需要具备的条件

  • 具备一个可以确定的地址用来bind();
  • 让操作系统知道这是一个服务器,而不是客户端,状态(listen);
  • 等待连接的到来(accept);

接收端使用bind()函数,来完成地址结构与socket套接字的绑定。
1.函数:

int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen);//将本地协议地址与sockfd绑定,这样ip、port就固定了

2.参数:

  • sockfd:socket套接字
  • myaddr:指向特定协议的地址结构指针
  • addrlen:该地址结构的长度

3.返回值:

  • 成功:返回0
  • 失败:-1

1.函数:

int listen(int sockfd,int backlog);//将套接字由主动修改为被动,使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的链接。

2.参数:

  • sockfd:socket套接字
  • backlog:连接队列的长度

3.返回值:

  • 成功:返回0
  • 失败:其他

1.函数:

int accept(int sockfd,struct sockaddr* cliaddr,socklen_t* addrlen);//从已连接队列取出一个已经建立的连接,如果没有任何连接可用,则进入谁灭等待(阻塞)

2.参数:

  • sockfd:socket套接字
  • cliaddr:用于存放客户端套接字地址结构
  • addrlen:套接字地址结构体长度的地址

3.返回值:

  • 成功:已连接套接字
  • 失败:<0的值

关闭连接:close()
使用close()函数即可关闭套接字,关闭一个代表已连接套接字将导致另一端收到一个0长度的数据包。
简单实践:
server.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

int startup(int _port,const char* _ip)
{
  int sockfd = socket(AF_INET,SOCK_STREAM,0);
  if(sockfd < 0)
  {
    perror("socket error");
    exit(1);
  }

  struct sockaddr_in local;
  local.sin_family = AF_INET;
  local.sin_port = htons(_port);
  local.sin_addr.s_addr = inet_addr(_ip);

  int err_log = bind(sockfd,(struct sockaddr*)&local,sizeof(sockaddr_in));
  if(err_log < 0)
  {
    perror("bind");
    exit(2);
  }

  if(listen(sockfd,5) < 0)
  {
    perror("listen");
    exit(3);
  }
  return sockfd;
}

int main(int argc,char** argv)
{
  if(argc != 3)
  {
    printf("Usage:%s[local_ip][local_port]\n",argv[0]);
    return 1;
  }
  int listen_sockfd = startup(atoi(argv[2]),argv[1]);
  
  struct sockaddr_in remote;
  socklen_t len = sizeof(struct sockaddr_in);
  while(1)
  {
    int sockfd = accept(listen_sockfd,(struct sockaddr*)&remote,&len);
    if(sockfd < 0)
    {
      perror("accept");
      continue;
    }
    printf("get a client,ip:%s,port:%d\n",inet_ntoa(remote.sin_addr),htons(remote.sin_port));

    char buf[512];
    while(1)
    {
      ssize_t _s = read(sockfd,buf,sizeof(buf)-1);
      if(_s > 0)
      {
        buf[_s] = 0;
        printf("client:%s\n",buf);
      }
      else 
      {
        printf("client is quit!\n");
        break;
      }
    }
  }
  return 0;
}

client.c

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc,char** argv)
{

  if(argc != 3)//更改服务器的ip地址
  {
    printf("Usage:%s [ip][port]\n",argv[0]);
    return 0;
  }

  int sockfd;
  sockfd = socket(AF_INET,SOCK_STREAM,0);
  if(sockfd < 0)
  {
    perror("socket error");
    return -1;
  }
  
  struct sockaddr_in server_addr;
  bzero(&server_addr,sizeof(server_addr));
  server_addr.sin_family = AF_INET; //IPv4
  server_addr.sin_port = htons(atoi(argv[2]));//端口
  server_addr.sin_addr.s_addr = inet_addr(argv[1]);

  //inet_pton(AF_INET,server_ip,&server_addr.sin_addr);
  
  int err_log = connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr));
  if(err_log != 0)
  {
    perror("connect error");
    close(sockfd);
    exit(-1);
  }
  
  char buf[512];
  while(1)
  {
    printf("send###");
    fflush(stdout);
    ssize_t _s = read(0,buf,sizeof(buf)-1);
    buf[_s] = 0;
    write(sockfd,buf,_s);
  }
  close(sockfd);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值