网络编程套接字---tcp简单通信程序详解

本文详细介绍了TCP网络编程中套接字的使用,包括服务端的创建、监听、接受连接和数据通信,以及客户端的连接、发送和接收数据。讨论了如何判断TCP断开连接,并提出了单线程和多进程版本的解决方案,以应对多个客户端的并发通信问题。
摘要由CSDN通过智能技术生成


本篇博客的目的是用socket套接字及接口实现一个简单的tcp聊天程序。

实现步骤

服务端:

  1. 创建套接字listen_sock
  2. 绑定地址信息(IP地址,端口号)
  3. 监听套接字listen_sock
  4. 与服务端建立链接
  5. 接收客户端发来的数据
  6. 向客户端发送数据
  7. 关闭套接字

客户端:
8. 创建套接字sockfd
9. 绑定地址信息(内部完成,不手动绑定)
10. 申请与客户端建立连接
11. 建立连接成功
12. 向服务端发送数据
13. 接收服务端发来的数据
14. 关闭套接字

服务端:

1.创建套接字socket

  //1.创建监听套接字listen_sock,参数依次为版本IPv4,流式套接字,默认tcp
  int listen_sock = socket(AF_INET,SOCK_STREAM,0);
  if (listen_sock < 0)
  {
    perror("create socket failed!");
    return -1;
  }

2.绑定地址信息
 //2.为服务器绑定地址信息,以后凡是向服务器发起连接请求,都会首先让listen_sock处理。

  struct sockaddr_in server_addr; //sockaddr_in仅用于IPv4
  socklen_t size = sizeof(server_addr);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(atoi(argv[2]));//先将命令行第二个参数(端口号)转为整形再转为网络字节序。
  server_addr.sin_addr.s_addr = inet_addr(argv[1]);//将命令行第一个参数(ip地址)转为网络字节序。
   int ret = bind(listen_sock,(struct sockaddr*)&server_addr,size);

这里创建一个地址信息块,存储了服务端的IP地址和端口号,在后面的bind函数中传入作为第二个参数,bind成功,就等同于向操作系统说明,只要是向这个IP地址和端口发送的数据,都先经过我listen_sock这里。

这里同时也要注意网络字节序的转换,我们先来看一看sockaddr_in的结构:

typedef uint16_t in_port_t;
typedef uint32_t in_addr_t;


struct in_addr
 {                                                                                                                                 
      in_addr_t s_addr;            
 };     

struct sockaddr_in                                                                                                                   
{ 
		   __SOCKADDR_COMMON (sin_);                                                                                                                                    
 		  in_port_t sin_port;     /* Port number.  */
		  struct in_addr sin_addr;    /* Internet address.  */                                                 
 }; 

可以看出,port是无符号16位的整型,sin_addr则是4个字节的整型,
而在网络字节序的函数,我们应该选择的就是htons(host to net small )和inet_addr。

  #include <arpa/inet.h>

       uint32_t htonl(uint32_t hostlong);(对无符号long4个字节转换为网络字节序的顺序)

       uint16_t htons(uint16_t hostshort);(对无符号short2个字节转换为网络字节序的数据)

       uint32_t ntohl(uint32_t netlong);(将4个字节的网络字节序数据转换为当前的主机字节序数据)

       uint16_t ntohs(uint16_t netshort);将一个16位(2个字节)由网络字节序转变为主机字节序

    	in_addr_t inet_addr(const char *cp);:将一个十进制的字符串转换为互联网标准转换成,
    
      int inet_pton(int af, const char *src, void *dst);//效果同inet_ntoa
3.开始监听socket
 //3.监听listen_sock套接字
  if (listen(listen_sock,5))
  {
    perror("listen error!");
    return -1;
  }

一旦开始监听,操作系统就会分配一块儿缓冲区作为连接成功队列,listen的第二个参数决定这这个队列的大小,也叫最大并发连接数
这个队列非常重要,当一个链接到来时,listen_socket检查队列是否还有位置,有就将任务置入队列,创建一个新的socket与该客户端进行通信。

为什么要建立一个新的newsocket,而不直接用监听socket与客户端通信?
原因其实很简单,我们举个例子,我们去中介哪儿租房,中间见到我们,说,行,现在里屋还有位置,你进去里屋,我安排一个想出租房的人和你聊吧。在这里,我们对应着客户,中介对应着监听socket,里屋对应着链接成功队列,出租房子的人对应newsocket。如果我们和socket直接通信,那就等于和中介谈租房的事情,显然不合适。所以在程序中,监听套接字只处理和我们的建立连接的事务,而不亲自和我们通信,一旦链接建立成功,他就会创建一个新的套接字,也就是newsocket和我们通信。每个不同的客户对应不同的newsocket,大家互不打扰,这也侧面说明每条tcp通信都是1对1的。

为什么每个链接的目的ip和目的端口号都是相同的,服务器还是能每个都处理好呢?
答:注意!我们说的和客户端建立链接用的socket是监听socket,他只负责将这个任务注册进任务队列,并不去确定客户的原ip和原端口&#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值