08 TCP套接字之通信


前面介绍了与套接字相关的函数, 本节就来运用这些函数实现一个简单的网络通信吧.


回射思路

回射, 将客户端发送过来的所有数据全部原封不动的发送回去. 可以使用writeread函数来实现, 客户端使用write函数向套接字中写数据, 对端用read函数读数据并调用write函数又将数据写回套接字, 客户端最后使用read读即可.


recv和send函数

回射可以用上面分析到的readwrite函数, 也可以使用recvsend函数实现.

函数原型

#include <sys/socket.h>
int recv(int sockfd, void *buf, size_t nbytes, int flags);
int send(int sockfd, const void *buf, size_t nbytes, int flags);

成功 : 返回读/写的字节数

失败 : 返回-1.

recv跟read功能基本一样, 前者只支持套接字并多了一个flags选项, 通常都设置为0.

send跟write功能基本一样, 前者只支持套接字并多了一个flags选项, 通常都设置为0.


深度理解

send的功能是拷贝指定长度的数据到发送缓冲区, 只有当数据被全部拷贝完成后函数才会正确返回, 否则进入阻塞状态或等待超时.

recv的功能是从接收缓冲区读取(其实就是拷贝)指定长度的数据. 如果将接收缓冲区大小设为0, recv将直接从协议缓冲区(滑动窗口区)读取数据.


函数flags参数

flags值描述
MSG_OOB发送或接收外来数据(紧急数据)
MSG_DONTROUTE绕过路由表查找
MSG_DONTWAIT仅操作非阻塞
MSG_PEEK窥看外来数据
MSG_WAITALL等待达到nbytes字节数后才返回
MSG_NOSIGNAL往读端关闭的管道或者socket中写数据不产生SIGPIPE信号

回射实现

先看服务端完整代码 1.0_service.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>

#define EXIT(msg) do{	\
   perror(msg); \
   exit(-1); \
}while(0)

int main(int argc, char *argv[])
{
   int sockfd;
   struct sockaddr_in serviced_addr;
   serviced_addr.sin_family = AF_INET;
   serviced_addr.sin_addr.s_addr = inet_addr("192.168.1.16");	// 为本机IP地址
   serviced_addr.sin_port = htons(8080);

    // 创建套接字
   sockfd = socket(AF_INET, SOCK_STREAM, 0);
   if(sockfd < 0)
      EXIT("socket");

   socklen_t service_len = sizeof(serviced_addr);
   int ret;
    // 服务端端口绑定
   ret = bind(sockfd, (struct sockaddr *)&serviced_addr, service_len);
	if(ret < 0) 
    	if(errno == EADDRINUSE)
        	EXIT("port impropriate");
		else
        	EXIT("bind");

    // 服务端开始监听
   ret = listen(sockfd, 5);
   if(ret < 0)
      EXIT("listen");

   int clientfd;
   struct sockaddr_in clientAddr;
   socklen_t clientLen;
    // 接收连接
   if((clientfd = accept(sockfd, (struct sockaddr *)&clientAddr, &clientLen)) < 0)
      EXIT("accept");

   char buf[1024];
   int n;
    // 回射
   while(1)
   {
      n = recv(clientfd, buf, sizeof(buf), 0);
      if(n == 0)
	  	break;
      send(clientfd, buf, n, 0);
   }


   exit(EXIT_SUCCESS);
}

再来看客户端完整的代码1.0_client.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

#define EXIT(msg) do {\
    perror(msg);	\
    exit(-1);	\
}while(0)

int main(int argc, char *argv[])
{
    int sockfd;
    struct sockaddr_in service_addr;

    service_addr.sin_port = htons(8080);	// 服务端熟知端口
    service_addr.sin_addr.s_addr = inet_addr("192.168.1.16");// 服务端IP地址
    service_addr.sin_family = AF_INET;

    // 获取套接字
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		EXIT("socket");

    // 建立连接
    if(connect(sockfd, (struct sockaddr *)&service_addr, sizeof(service_addr)) != 0)
		EXIT("connect");

    char buf[1024];
    int n;
    // 发送
    while(1)
    {
		n = read(STDIN_FILENO, buf, sizeof(buf));
		send(sockfd, buf, n, 0);
		n = recv(sockfd, buf, sizeof(buf), 0);
		write(STDOUT_FILENO, buf, n);
    }
    

    exit(EXIT_SUCCESS);
}

编译运行

./service
./client

在这里插入图片描述
问题

现在我们完成的简单的socket编程, 但是有很多的不足, 如: 只能够连接一个客户端; 服务端关闭客户端并没有退出等等. 接下来准备用几篇文张来实现一个正确的客户端/服务端.

总结
  • 掌握recv和send函数
  • 实现回射C/S程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值