TCP编程

TCP(即传输控制协议):

是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)

注意

对于TCP是先运行服务器,客户端才能运行。

流程:

图片流程

在这里插入图片描述

代码流程

服务器:
  socket:创建一个用与链接的套接字
  bind:绑定自己的ip地址和端口
  listen:监听,将主动套接字转为被动套接字
  accept:阻塞等待客户端链接,链接成功返回一个用于通信套接字
  recv:接收消息
  send:发送消息
  close:关闭文件描述符    
    
客户端:
   socket:创建一个套接字
   填充结构体:填充服务器的ip和端口
   connect:阻塞等待链接服务器
   recv/send:接收/发送消息
   close:关闭  

函数接口

socket创建套接字

 #include <sys/types.h>          
 #include <sys/socket.h>
 int socket(int domain, int type, int protocol);
功能:创建套接字
参数:
   domain:协议族
       AF_UNIX, AF_LOCAL  本地通信协议   unix(7)
       AF_INET             IPv4          ip(7)
       AF_INET6            IPv6        ipv6(7)
   type:套接字类型
     SOCK_STREAM  流式套接字
     SOCK_DGRAM  数据报套接字
     SOCK_RAW   原始套接字 
   	 protocol(协议):填0,自动匹配底层TCP或UDP等协议。根据type匹配
    系统默认自动帮助匹配对应协议
     传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
     网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
 返回值:成功文件描述符 
    	失败-1 更新errno

举例

int sockfe = socket(AF_INET, SOCK_STREAM, 0);//创建套接字,用于链接
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }

bind绑定套接字

#include <sys/types.h>          
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
 功能:绑定套接字 (绑定ip和端口)
 参数:
   sockfd:套接字文件描述符
   addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信结构体由socket第一个参数确定)   

  addrlen:结构体大小
返回值:  成功0
        失败:-1 更新errno
     
 通用结构体:  
    struct sockaddr 
    {
     sa_family_t sa_family;
     char        sa_data[14];
 	};
 
 ipv4的通信结构体:
     #include <sys/socket.h>
	 #include <netinet/in.h>
 	 #include <netinet/ip.h>
   struct sockaddr_in 
   {
      sa_family_t    sin_family; /*AF_INET(协议) */
      in_port_t      sin_port;   /* 端口 */
      struct in_addr sin_addr;   /* ip地址 */
   };
   struct in_addr 
   {
      uint32_t       s_addr;   
   };

本地通信结构体:
   struct sockaddr_un 
   {
       sa_family_t sun_family;               /* AF_UNIX */
       char        sun_path[108];            /* 套接字文件 */
   };

举例

    //填充ipv4的通信结构体
    struct sockaddr_in serveraddr, caddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[1]));
    //serveraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //0.0.0.0 自动获取主机ip
    // serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_addr.s_addr = INADDR_ANY;

    socklen_t len = sizeof(caddr);

    //&serveraddr -->struct sockaddr_in *

    //2.绑定套接字  bind  (绑定自己的ip和端口,便于别人找到自己)
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }

listen监听套接字

#include <sys/types.h>          
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
 sockfd:套接字
 backlog:同时响应客户端请求链接的最大个数,不能写0.
  不同平台可同时链接的数不同,一般写6-8(队列1:保存正在连接)
    (队列2,连接上的客户端)
   返回值:成功 0   失败-1,更新errno  

举例

    //3.listen  监听 将主动套接子变为被动等待
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err.");
        return -1;
    }

accept阻塞函数

#include <sys/types.h>          
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
								
阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,则accept()函数返回,返回一个用于通信的套接字文件;		//accept(sockfd,NULL,NULL);
参数:
   Sockfd :套接字
   addr: 链接客户端的ip和端口号//建立新的结构体,保存客户端ip和端口
      如果不需要关心具体是哪一个客户端,那么可以填NULL;
   addrlen:结构体的大小
     如果不需要关心具体是哪一个客户端,那么可以填NULL;
  返回值: 
     成功:文件描述符; //用于通信
失败:-1,更新errno

举例

//4.阻塞等待客户端链接,链接成功返回一个通信文件描述符 accept
acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
if (acceptfd < 0)
{
	perror("accept err.");
	return -1;
}

recv接收数据

#include <sys/types.h>          
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据 
参数: 
    sockfd: acceptfd ;
    buf  存放位置
    len  大小
    flags  一般填0,相当于read()函数
    MSG_DONTWAIT  非阻塞
返回值: 
   < 0  失败出错  更新errno
   ==0  表示客户端退出
   >0   成功接收的字节个数

举例

recvbyte = recv(acceptfd, buf, sizeof(buf),0);
if (recvbyte < 0)
{
	perror("recv err.");
	return -1;
}
else if (recvbyte == 0)
{
	printf("client exit.\n");
	break;
}
else
{
	printf("buf:%s\n", buf);
}		//注意最后关闭套接字close(acceptfd);

send发送数据

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
功能:发送数据
参数:
    sockfd:socket函数的返回值
    buf:发送内容存放的地址
    len:发送内存的长度
    flags:如果填0,相当于write();
返回值: 
   < 0  失败出错  更新errno
   >0   成功发送的字节个数

举例

char buf[128]={0};
send(sockfd,buf,sizeof(buf),0);

connect连接服务器

#include <sys/types.h>          
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:用于连接服务器;
参数:
     sockfd:socket函数的返回值
     addr:填充的结构体是服务器端的;
     addrlen:结构体的大小
返回值 
      -1 失败,更新errno
      正确 0 

举例

 //填充ipv4的通信结构体  服务器端ip和端口
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    //&serveraddr -->struct sockaddr_in *

    //2.请求链接  connect  
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("connect err.");
        return -1;
    }
    printf("connect ok.\n");

总代码

服务器

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

int main(int argc, char const *argv[])
{
    if(argc != 3)
    {
        printf("please input %s <ip> <port>\n",argv[0]);
        return -1;
    }
    //1.创建套接字 socket
    int sockfd,acceptfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    printf("socket ok %d\n", sockfd);

    //填充ipv4的通信结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    //&serveraddr -->struct sockaddr_in *

    //2.绑定套接字  bind  (绑定自己的ip和端口,便于别人找到自己)
    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("bind err.");
        return -1;
    }
    printf("bind ok.\n");

    //3.listen  监听 将主动套接子变为被动等待
    if (listen(sockfd, 5) < 0)
    {
        perror("listen err.");
        return -1;
    }
    printf("listen ok.\n");

    //4.阻塞等待客户端链接,链接成功返回一个通信文件描述符 accept 
   acceptfd=accept(sockfd,NULL,NULL);

   if(acceptfd < 0)
   {
       perror("accept err.");
       return -1;
   }
   printf("accept ok.\n");
   
   //5.循环收发消息
   char buf[128];
   int recvbyte;
   while(1)
   {
       //接收消息
       recvbyte=recv(acceptfd,buf,sizeof(buf),0);
       if(recvbyte < 0)
       {
           perror("recv err.");
           return  -1;
       }else if(recvbyte == 0)
       {
           printf("client exit.\n");
           break;
       }else 
       {
           printf("buf:%s\n",buf);
       }
   }
   close(acceptfd);
   close(sockfd);
    return 0;
}

客户端

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

int main(int argc, char const *argv[])
{
    if(argc != 3)
    {
        printf("please input %s <ip> <port>\n",argv[0]);
        return -1;
    }
    //1.创建套接字 socket
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err.");
        return -1;
    }
    printf("socket ok %d\n", sockfd);

    //填充ipv4的通信结构体  服务器端ip和端口
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    //&serveraddr -->struct sockaddr_in *

    //2.请求链接  connect  
    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
    {
        perror("connect err.");
        return -1;
    }
    printf("connect ok.\n");

   //3.循环收发消息
   char buf[128];
   int recvbyte;
   while(1)
   {
       fgets(buf,sizeof(buf),stdin);
       if(buf[strlen(buf)-1]=='\n')
         buf[strlen(buf)-1]='\0';
       //发送消息
       send(sockfd,buf,sizeof(buf),0);
   }
   close(sockfd);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值