第24课-TCP通讯程序设计

24.1 TCP编程模型函数化

这里面我们用到的框架就是上一节课的框架。即我们需要有服务器的搭建还要有客户机的搭建。执行的步骤如下:

服务器:创建socket(socket),绑定地址(bind),监听端口(listen),等待连接(accept),发/ 收数据(send/receive),结束连接(close)。

客户机:创建socket(socket),连接服务器(connet),发/收数据(send/receive),结束连接(close)。

以上一共有有8个函数,下面一一介绍:

1. 创建套接字

(1)函数名

socket()

(2)函数原型

int socket(int domain, int type, int protocol);

(3)函数功能

创建一个套接字

(4)包含的头文件

#include<sys/socket.h>

(5)返回值

成功:返回文件(套接字)描述符;

失败:-1

(6)参数说明

domain:确定通讯特性。

AF_INET(IPv4因特网域);

AF_INET6(IPv6因特网域);

AF_UNIX(UNIX域);

AF_UNSPEC(未指定)。

type:确定套接字的类型。

SOCK_DGRAM(长度固定的、无连接的不可靠报文传递);

SOCK_RAW(IP协议的数据报接口,POSIX.1中为可选);

SOCK_SEQPACKET(长度固定、有序、可靠的面向连接报文传递);

SOCK_STREAM(有序、可靠、双向的面向连接字节流)。

protocol:通常是0,表示按照参数domain和type确定通讯协议。

2. 绑定地址

(1)函数名

bind()

(2)函数原型

int bind(int sockfd, const struct sockaddr *addr, socklen_t len);

(3)函数功能

降地址绑定到一个套接字。

(4)包含的头文件

#include<sys/socket.h>

(5)返回值

成功:0;

失败:-1

(6)参数说明

sockfd:套接字的fd

addr:绑定的地址

len:绑定的地址长度

我们将结构体列出来:

struct sockaddr

{

sa_family_t   sa_family;

char         sa_data[14];

}

这是一个通用的地址类型(ipv4和ipv6),第一个成员表示协议族的地址类型,第二个成员表示具体的地址的值。但是我们平时用到的地址都是ipv4的,要在上述的结构上进行一定的演化:

struct sockaddr_in

{

short int            sin_family;

unsigned short int    sin_port;

struct in_addr        sin_addr;

unsigned char        sin_zero[8];

}

struct in_addr

{

unsigned long s_addr;

}

为了区分这两种方式的区别,我们用表格表示:

协议族

协议族(AF_INET)

地址(14字节)

端口号(2字节)

IP地址(4字节)

填充(8字节)

注:我们生活中用到的ip地址是类似于192.168.1.1这样的,但是我们我们程序中的ip地址却是整型的,所以需要一定的转换。我们提供下面两个函数:

l  in_addr_t inet_addr(const char *cp)

功能:将字符串形式的IP地址转化为整数型的IP地址(网络字节序)

范例:in_addr.saddr = inet_addr(“192.168.1.1”);

l  char *inet_ntoa(struct in_addr)

功能:将整数形式的Ip地址转化为字符串形式的IP地址。

3. 设置监听端口

(1)函数名

listen()

(2)函数原型

int listen(int sockfd, int backlog);

(3)函数功能

宣告可以接受连接请求。

(4)包含的头文件

#include<sys/socket.h>

(5)返回值

成功:0;

失败:-1

(6)参数说明

sockfd:套接字的fd

backlog:提供一个提示,用于表示该进程所需要入队的连接请求数量。其实际值由系统决定。

4. 等待连接

(1)函数名

accept()

(2)函数原型

int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);

(3)函数功能

一旦服务器调用了listen,套接字就能就收连接请求。使用函数accept获得连接请求并建立连接。没有客户机连接会是服务器发生阻塞。

(4)包含的头文件

#include<sys/socket.h>

(5)返回值

成功:文件(连接字)描述符;一个新的文件描述符,以后操作用的都是它。

失败:-1

(6)参数说明

sockfd:文件(连接字)描述符;

addr:将客户机的地址记录在这里;

len:地址的长度

5. 发送数据

(1)函数名

send()

(2)函数原型

ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);

(3)函数功能

它和write很像,但是可以指定标志来改变处理传输数据的方式。使用send时套接字必须是已经连接的。

(4)包含的头文件

#include<sys/socket.h>

(5)返回值

成功:返回发送的字节数;

失败:-1

(6)参数说明

sockfd:套接字的fd,是使用了accept建立连接后返回的fd;

buf:发送的数据存放的位置

nbytes:发送数据的长度

flags:标志,若是不用设为0;包括如下的符号:

MSG_DONTROUTE:勿将数据路由出本地网络

MSG_DONTWAIT:允许非阻塞操作(等价于O_NONBLOCK)

MSG_EOR:如果协议支持,此为记录结束

MSG_OOB:如果协议支持,发送带外数据

6. 接收数据

(1)函数名

recv()

(2)函数原型

ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);

(3)函数功能

它和read很像,但是允许指定选项来控制如何接收数据。

(4)包含的头文件

#include<sys/socket.h>

(5)返回值

成功:以字节计数的消息长度,若无可用消息或对方已经按序结束则返回0;

失败:-1

(6)参数说明

sockfd:套接字的fd,是使用了accept建立连接后返回的fd;

buf:接收的数据存放的位置

nbytes:接收数据的长度

flags:标志;

7. 关闭连接

close(sockfd),和文件的关闭函数是一样的。

8. 连接服务器

(1)函数名

connect()

(2)函数原型

int connect(int sockfd, const struct sockaddr *addr, socklen_t len);

(3)函数功能

如果处理的是面向连接的网络服务(SOCK_STREAM或SOCK_SEQPACKET),在开始交换数据以前,需要在请求服务的进程套接字(客户端)和提供服务的进程套接字(服务器)之间建立一个连接。这就是connect函数的作用。

(4)包含的头文件

#include<sys/socket.h>

(5)返回值

成功:0;

失败:-1

(6)参数说明、

sockfd:客户机上创建的套接字的fd;

addr:服务器的地址;

len:地址的长度。

24.2 网络字节序

大端字节序(big-endian):不按照内存的增长方向,高位数据存储于低位内存中。

小端字节序(little-endian):按照内存的增长方向,高位数据存储于高位内存中。

在两个主机A和B之间通讯是,若是两个主机一个是大端通讯一个是小端通讯,就会使得发送的字节顺序颠倒。这就需要网络字节序的使用,它实际就是一个在通讯过程中对主机大小端的一个规定。网络字节序对应的都是大端模式的,发送端若是小端模式的我们就要先将它转化成大端模式。

但是对于接收端,我们对它的存储就要看接收方到底是什么模式的。若是大端模式,就按大端模式存储;若是小端模式,就按小端模式存储。

在linux系统中,提供了四个函数进行网络字节序的转换,如下:

1. uint32_t htonl(uint32_t hostlong);

将32位的数据从主机字节序转换为网络字

in_addr.saddr = htonl(INADDR_AN

2. uint16_t htons(uint16_t hostshort);

将16位的数据从主机字节序转换为网络字节序

3. uint32_t ntohl(uint32_t netlong);

将32位的数据从网络字节序转换为主机字节序

  1. 4.  uint16_t ntohs(uint16_t netshort);

将16位的数据从网络字节序转换为主机字节序

24.3 实例编写

TCP通讯程序设计

tcp_server.c

#include<sys/socket.h>

#include<stdio.h>

#include<string.h>  //字符串头文件

#include<netinet/in.h>  //地址的头文件

#define portnum 3333

int main()

{

int sockfd;

int new_fd;

char buffer[128];//定义存储数据的字节

int nbyte;//接收到的字符串的长度

int sin_size;

struct sockaddr_in server_addr;

struct sockaddr_in client_addr;

//1.创建套接字

if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

printf("creat socket error!\n");

exit(1);

}

//2.1 设置要绑定的地址

bzero(&server_addr, sizeof(struct sockaddr_in));//清零

server_addr.sin_family = AF_INET;  //网络协议

server_addr.sin_port = htons(portnum);  //端口,超过两个字节就要转换

server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//表示任意地址,超过两个字节就要转换

//2.2 绑定地址

bind(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr));//表示指针的强制转换

//3.监听端口

listen(sockfd, 5);

while(1)

{

//4.等待连接

sin_size = sizeof(struct sockaddr);

new_fd = accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size);

printf("server get connection from %s\n", inet_ntoa(client_addr.sin_addr));//取客户机的ip地址,并且将整型地址转换成字符串地址

//5.接收数据

nbyte = recv(new_fd,buffer,128,0);

buffer[nbyte] = '\0';//字符串的结束符

printf("server received: %s \n",buffer);

//6.结束连接

close(new_fd);

}

close(sockfd);

return 0;

}

tcp_client.c

#include<sys/socket.h>

#include<stdio.h>

#include<string.h>  //字符串头文件

#include<netinet/in.h>  //地址的头文件

#define portnum 333  //不用分号

int main()

{

int sockfd;

char buffer[128];//保存我们输入的数据

struct sockaddr_in server_addr;

//1.创建套接字

if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

printf("creat socket error!\n");

exit(1);

}

//2.1 设置要连接的服务器的地址

bzero(&server_addr, sizeof(struct sockaddr_in));//清零

server_addr.sin_family = AF_INET;  //网络协议

server_addr.sin_port = htons(portnum);  //端口,超过两个字节就要转换

server_addr.sin_addr.s_addr = inet_addr("192.168.153.129");

//服务其地址理论应该是通过main函数传进来的,但是这里为了方便将它写死。

//2.2 连接服务器

if(connect(sockfd, (struct sockaddr *)(&server_addr), sizeof(struct sockaddr)) == -1)

{

printf("connect error!\n");

exit(1);

}

//3. 发送数据到服务器

printf("please input char:\n");

fgets(buffer,128,stdin);

send(sockfd, buffer, strlen(buffer),0);

//4. 关键套接字

close(sockfd);

return 0;

}

运行:

用gcc编译好文件后,打开两个相同的终端,在一个中端里面运行./tcp_server,在另一个终端运行./tcp_client。在tcp_client终端输入的字符,在tcp_server能显示。

转载于:https://www.cnblogs.com/free-1122/p/11357245.html

第三季-第24课-TCP通讯程序设计相关推荐

  1. 《 Python程序设计项目案例》— 用Python开发的基于TCP通讯协议的私人聊天室 (期末大作业、结课作业、课程设计、毕业设计)

    基于Python与TCP协议的私人聊天室(GUI交互界面,用户注册.用户登录.实时聊天,文件上传与下载) 用Python开发的基于TCP通讯协议的实时聊天通讯和文件共享应用 目录 基于Python与T ...

  2. 基于QTcpSocket和QTcpServer的Tcp通讯以及QDataStream序列化数据

    为什么80%的码农都做不了架构师?>>>    最近要在QT下开发Tcp通讯,发送序列化数据以便于接收. 这里涉及到几个问题: 1.QTcpSocket.QTcpServer的通讯 ...

  3. TCP通讯处理粘包详解

    一般所谓的TCP粘包是在一次接收数据不能完全地体现一个完整的消息数据.TCP通讯为何存在粘包呢?主要原因是TCP是以流的方式来处理数据,再加上网络上MTU的往往小于在应用处理的消息数据,所以就会引发一 ...

  4. 欧姆龙OMRON PLC之HostLink通讯协议(五)- CP1H以太网FINS/TCP通讯实例

    //写在前面: 自2010年起,本人陆续在新浪博客上面发了几篇OMRON PLC的应用笔记,曾经很疑惑阅读量异常增加,后来发现原来是这里有人转载.现将原文照发在这里,希望能帮到做工控的同行朋友们. C ...

  5. 嵌入式linux学习笔记--TCP通讯整理

    嵌入式linux学习笔记–TCP通讯整理 之前的项目中使用到了比较多的tcp 通讯相关的知识,一直也没有进行整理,今天准备拿出时间好好的整理一下TCP通讯的整个过程.预计会整理linux和window ...

  6. tcpdump和tcp通讯

    tcpdump抓包分析工具 还是很好用的,针对Tcp通讯过程做一个记录,对照tcp通讯过程 介绍tcpdump man一下,介绍的很清楚在进行网络测试的时候,我们经常需要进行抓包的工作,当然有许多测试 ...

  7. 【转】欧姆龙OMRON PLC之 CP1H 以太网FINS/TCP通讯实例

    原文:http://blog.sina.com.cn/s/blog_539cee190102wr4t.html CP1H上面的选件板插槽可以插入1-2块CP1W-CIF41模块,系统即具有了以太网功能 ...

  8. c++ char4个字节_西门子PLC的TCP通讯(不同项目下)①--TSEND_C指令

    西门子PLC的TCP通讯(不同项目下)①--TSEND_C指令 本期说一下,不同项目下的,连个西门子1200的TCP通讯,这次我们用TSEND_C和TRCV_C组合使用,这次先了解下TSEND_C指令 ...

  9. java网络编程之TCP通讯

    java中的网络编程之TCP协议的详细介绍,以及如何使用,同时我在下面举2例说明如何搭配IO流进行操作, 1 /* 2 *TCP 3 *建立连接,形成传输数据的通道: 4 *在连接中进行大数据量传输: ...

  10. boost asio 异步实现tcp通讯

    一.前言 boost asio可算是一个简单易用,功能又强大可跨平台的C++通讯库,效率也表现的不错,linux环境是epoll实现的,而windows环境是iocp实现的.而tcp通讯是项目当中经常 ...

最新文章

  1. PyTorch核心贡献者开源书:《使用PyTorch进行深度学习》完整版现已发布!
  2. Swift基础--方法
  3. solidity modifier函数修改器 智能合约开发知识浅学(三)
  4. mysql中如何将默认用户名root改成其他?
  5. Oracle数据库的Sequence(序列)
  6. java 反射 proper_JAVA提高四:反射基本应用
  7. 升级 webpack4 变化之处
  8. 2020考研计算机专业课,2020考研:计算机考这4个科目,各科该这样备考
  9. java并发包源码分析
  10. 20210422-微信刷脸支付获取调用凭证authinfo的时候,提示 rawdata无效
  11. excel if判断单元格是否为空否求和_Excel基础函数IF的7个使用技巧,绝不是简单的判断哦!...
  12. Mujoco入门教程1-xml文件学习
  13. 设计一个RC高通滤波器或低通滤波器
  14. 山大计算机学院副院长屠长河,孟祥旭(山东大学教授,博士生导师)_百度百科...
  15. 哥尼斯堡的“七桥问题”
  16. muma很可能在陪你玩游戏
  17. 农场阳光 (simpson)
  18. 深入理解金融交易报文Iso8583协议
  19. 树莓派蓝牙连接就断开
  20. 写CSDN文章时,生成下标、上标、竖线的方法

热门文章

  1. Python爬虫基础-01-带有请求参数的爬虫
  2. ImageLoader的简单分析(五)
  3. 不显示藏宝阁试穿服务器,梦幻西游:藏宝阁试穿功能的妙用,无限回档测试
  4. 双亲委派模型与 Flink 的类加载策略
  5. 服务器云端设置怎么退出_换新手机时,3个地方要及时清除退出,别让旧手机成隐私“内鬼”...
  6. mysql 1236错误_MySQL主主同步环境出现1236错误
  7. mysql 主从 日志_mysql主从复制基于日志复制
  8. 修改Linux文件的读写权限
  9. win7动态壁纸_电脑桌面美化,高清动态壁纸
  10. 设置表格表头字体_Excel双栏和三栏斜线表头制作技巧