快捷搜索:  汽车  科技

tcp网络编程(网络编程-2TCPUDP编程)

tcp网络编程(网络编程-2TCPUDP编程)特点套接字type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等返回值:

1、UDP编程1.1、UDP编程-创建套接字

#include <sys/socket.h> int socket(int family int type int protocol);

功能

创建一个用于网络通信的socket套接字(描述符)

参数

family:协议族(AF_INET、AF_INET6、PF_PACKET等)

type:套接字类(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)

protocol:协议类别(0、IPPROTO_TCP、IPPROTO_UDP等

返回值:

套接字

特点

创建套接字时,系统不会分配端口

创建的套接字默认属性是主动的,即主动发起服务的请求;当作为服务器时,往往需要修改为被动的

1.2、UDP编程-发送数据

ssize_t sendto(int sockfd,const void *buf size_t nbytes int flags const struct sockaddr *to socklen_t addrlen);

功能:

向to结构体指针中指定的ip,发送UDP数据

参数:

sockfd:套接字

buf: 发送数据缓冲区

nbytes: 发送数据缓冲区的大小

flags:一般为0

to: 指向目的主机地址结构体的指针

addrlen:to所指向内容的长度

返回值:

成功:发送数据的字符数

失败: -1

注意:

通过to和addrlen确定目的地址

可以发送0长度的UDP数据包

1.3、UDP编程-绑定端口

int bind(int sockfd const struct sockaddr *myaddr,socklen_t addrlen);

功能:将本地协议地址与sockfd绑定

参数:

sockfd: socket套接字

myaddr: 指向特定协议的地址结构指针

addrlen:该地址结构的长度

返回值 :

成功:返回0

失败:其他

1.4、UDP编程-接收数据

ssize_t recvfrom(int sockfd void *buf size_t nbytes int flags struct sockaddr *from socklen_t *addrlen);

功能 :

接收UDP数据,并将源地址信息保存在from指向的结构中

参数 :

sockfd:套接字

buf: 接收数据缓冲区

nbytes:接收数据缓冲区的大小

flags: 套接字标志(常为0)

from: 源地址结构体指针,用来保存数据的来源

addrlen: from所指内容的长度

返回值:

成功:接收到的字符数

失败: -1

注意:

通过from和addrlen参数存放数据来源信息

from和addrlen可以为NULL 表示不保存数据来源

1.5、UDP编程-客户端示例

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <ARPa/inet.h> #define SERVER_IP "192.168.0.108" #define SERVER_PORT 8080 int main(int argc char *argv[]) { int sockfd; sockfd = socket(AF_INET SOCK_DGRAM 0); //创建UDP套接字 if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in dest_addr; bzero(&dest_addr sizeof(dest_addr)); dest_addr.sin_family = AF_INET; dest_addr.sin_port = htons(SERVER_PORT); inet_pton(AF_INET SERVER_IP &dest_addr.sin_addr); printf("UDP server %s:%d\n" SERVER_IP SERVER_PORT); while(1) { char send_buf[512] = ""; fgets(send_buf sizeof(send_buf) stdin);//获取输入 send_buf[strlen(send_buf)-1] = '\0'; sendto(sockfd send_buf strlen(send_buf) 0 (struct sockaddr*)&dest_addr sizeof(dest_addr));//发送数据 } close(sockfd); return 0; } 1.6、UDP编程-服务端示例

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_IP "192.168.0.104" #define SERVER_PORT 8080 int main(int argc char *argv[]) { int sockfd; sockfd = socket(AF_INET SOCK_DGRAM 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in my_addr; bzero(&my_addr sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(SERVER_PORT); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); printf("server bind port: %d\n" SERVER_PORT); int ret = bind(sockfd (struct sockaddr*)&my_addr sizeof(my_addr)); if(ret != 0) { perror("bind"); close(sockfd); exit(-1); } printf("receive data:\n"); while(1) { int recv_len; char recv_buf[512] = ""; struct sockaddr_in client_addr; char client_ip[INET_ADDRSTRLEN] = "";//INET_ADDRSTRLEN=16 socklen_t cliaddr_len = sizeof(client_addr); recv_len = recvfrom(sockfd recv_buf sizeof(recv_buf) 0 (struct sockaddr*)&client_addr &cliaddr_len); inet_ntop(AF_INET &client_addr.sin_addr client_ip INET_ADDRSTRLEN); //char *client_ip_pr=inet_ntoa(client_addr.sin_addr); printf("client_ip:%s client_port:%d\n" client_ip ntohs(client_addr.sin_port)); printf("data(%d):%s\n" recv_len recv_buf); } close(sockfd); return 0; } 1.7、UDP广播

广播:由一台主机向该主机所在局域网内的所有主机发送数据的方式 ,广播只能用UDP或原始IP实现,不能用TCP

广播的用途:

单个服务器与多个客户主机通信时减少分组流通

地址解析协议(ARP)

动态主机配置协议(DHCP)

网络时间协议(NTP)

广播的特点:

处于同一子网的所有主机都必须处理数据

UDP数据包会沿协议栈向上一直到UDP层

运行音视频等较高速率工作的应用,会带来大负

局限于局域网内使用

UDP广播地址

{网络ID,主机ID}

网络ID表示由子网掩码中1覆盖的连续位

主机ID表示由子网掩码中0覆盖的连续位

定向广播地址:主机ID全1

例:对于192.168.220.0/24,其定向广播地址为 192.168.220.255

通常路由器不会转发该广播

受限广播地址:255.255.255.255

路由器从不转发该广播

套接字选项

int setsockopt(int sockfd int level int optname const void *optval socklen_t optlen);

功能:

设置套接字选项值

参数:

sockfd:套接字

level: 被设置的选项的级别,如果想要在套接字级别上设置选项,就必须把level设置为 SOL_SOCKET

optname:准备设置的选项,option_name可以有哪些取值,这取决于level

optval:类型

optlen:

返回值:

成功:0

失败: -1

示例

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_IP "192.168.0.104" #define SERVER_PORT 8080 int main(int argc char *argv[]) { int sockfd=0; sockfd = socket(AF_INET SOCK_DGRAM 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in server_addr; bzero(&server_addr sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); printf("server bind port: %d\n" SERVER_PORT); int opt=1; setsockopt(sockfd SOL_SOCKET SO_BROADCAST &opt sizeof(opt)); char send_buf[1024]={0}; strcpy(send_buf "this is broadbast msg\n"); int len = sendto(sockfd send_buf strlen(send_buf) 0 (struct sockaddr *)&server_addr sizeof(server_addr)); if(len<0) { printf("send error\n"); close(sockfd); return -1; } printf("send success\n"); close(sockfd); return 0; } 1.8、UDP多播(组播)

多播: 数据的收发仅仅在同一分组中进行多播的特点:

多播地址标示一组接口

多播可以用于广域网使用

在IPv4中,多播是可选的

UDP多播地址

IPv4的D类地址是多播地址

十进制:224.0.0.1 - 239.255.255.254

十六进制:E0.00.00.01 - EF.FF.FF.FE 特殊的IP地址多播地址:224.0.0.1:是所有主机组,子网上所有具有多播能力的节点(主机、路由器、打印机)必须在所有具有多播能力的接口上加入该组。224.0.0.2:是所有路由器组,子网上所有多播路由器必须在所有具有多播能力的接口上加入该组。

多播地址分类

224.0.0.0 -- 224.0.255 链路局部的多播地址,是低级拓扑和维护协议保留的,多播路由器不转发以这些地址为目的地址的数据报

224.0.1.0 -- 224.0.1.255: 为用户可用的组播地址(临时组地址),可以用于 Internet 上的。224.0.2.0 -- 238.255.255.255: 用户可用的组播地址(临时组地址),全网范围内有效239.0.0.0 -- 239.255.255.255: 为本地管理组播地址,仅在特定的本地范围内有效

  • 在IPv4因特网域(AF_INET)中,多播地址结构体用如下结构体ip_mreq表示

struct in_addr{ in_addr_t s_addr; }; struct ip_mreq{ struct in_addr imr_multiaddr;//多播组ip struct in_addr imr_interface;//将要添加到的多播组ip };

套接口选项

int setsockopt(int sockfd int level int optname const void *optval socklen_t optlen);

功能:

设置套接字选项值

参数:

sockfd:套接字

level: 被设置的选项的级别,IPPROTO_IP

optname: IP_ADD_MEMBERSHIP :加入多播组 ,IP_DROP_MEMBERSHIP :离开多播组

optval:类型 ip_mreq{}

optlen:

返回值:

成功:0

失败: -1

为什么要用组播

单播和组播是寻址方案的两个极端(要么单个、要么全部),多播则是两者之间的一种折中的方案,多播数据报只应该由对它感兴趣的接口接收。另外,广播一般局限于局域网内使用,多播则既可用于局域网,也可跨广域网使用。

2、TCP编程2.1、TCP介绍

tcp网络编程(网络编程-2TCPUDP编程)(1)

  • 作为客户端需要具备的条件

(1) 知道服务器的ip、port
(2) 主动连接 服务器

  • 需要用到的函数

socket : 创建TCP套接字(主动)

connect:连接服务器

send:发送数据到服务器

recv: 接受服务器的响应

close:关闭连接

2.2、TCP客户端相关函数2.2.1 创建TCP套接字函数

int sockfd; sockfd = socket(AF_INET SOCK_STREAM 0);// 创建通信端点:套接字2.2.2 连接服务器函数

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

功能:主动跟服务器建立链接
参数:

sockfd:socket套接字
addr: 连接的服务器地址结构
len: 地址结构体长度

返回值:

成功:0 失败:其他

注意:

• connect建立连接之后不会产生新的套接字
• 连接成功后才可以开始传输TCP数据

2.2.3 TCP发送数据

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

功能:用于发送数据
参数:

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

注意:不能用TCP协议发送0长度的数据包

2.2.4 TCP接收数据

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

功能:用于接收网络数据
参数:

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

返回值:成功接收到字节数

2.2.5 客户端代码

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_IP "192.168.0.107" #define SERVER_PORT 8080 int main(int argc char *argv[]) { int sockfd; sockfd = socket(AF_INET SOCK_STREAM 0);// 创建通信端点:套接字 if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in server_addr; bzero(&server_addr sizeof(server_addr)); // 初始化服务器地址 server_addr.sin_family = AF_INET; server_addr.sin_port = htons(SERVER_PORT); 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"); close(sockfd); exit(-1); } char send_buf[1024] = ""; printf("send data to [%s:%d]\n" SERVER_IP SERVER_PORT); while(1) { printf("send:"); fgets(send_buf sizeof(send_buf) stdin); send_buf[strlen(send_buf)-1]='\0'; send(sockfd send_buf strlen(send_buf) 0); // 向服务器发送信息 } close(sockfd); return 0; } 2.3、TCP服务端相关函数

做为TCP服务器需要具备的条件

• 具备一个可以确知的地址
• 让操作系统知道是一个服务器,而不是客户端
• 等待连接的到来

对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始

2.3.1、bind函数

bind用法和UDP服务端使用一样,

struct sockaddr_in my_addr; bzero(&my_addr sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(port); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); int err_log = bind(sockfd (struct sockaddr*)&my_addr sizeof(my_addr)); if( err_log != 0) { perror("binding"); close(sockfd); exit(-1); }2.3.2、listen函数

#include <sys/socket.h> int listen(int sockfd int backlog);

功能:

• 将套接字由主动修改为被动
• 使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接

参数:

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

返回值:

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

2.3.3、accept 函数

#include <sys/socket.h> int accept(int sockfd struct sockaddr *cliaddr socklen_t *addrlen);

功能:从已连接队列中取出一个已经建立的连接,如果没有任何连接可用,则进入睡眠等待(阻塞)

参数:

• sockfd: socket监听套接字

• cliaddr: 用于存放客户端套接字地址结构

• addrlen:套接字地址结构体长度的地址

返回值:已连接套接字

注意:返回的是一个已连接套接字,这个套接字代表当前这个连接

2.3.4、服务端的代码

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define SERVER_PORT 8080 int main(int argc char *argv[]) { //创建套接字 int sockfd = socket(AF_INET SOCK_STREAM 0); if(sockfd < 0) { perror("socket"); exit(-1); } struct sockaddr_in my_addr; bzero(&my_addr sizeof(my_addr)); my_addr.sin_family = AF_INET; my_addr.sin_port = htons(SERVER_PORT); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); int err_log = bind(sockfd (struct sockaddr*)&my_addr sizeof(my_addr)); if( err_log != 0) { perror("binding"); close(sockfd); exit(-1); } err_log = listen(sockfd 10); if(err_log != 0) { perror("listen"); close(sockfd); exit(-1); } printf("listen client port=%d...\n" SERVER_PORT); while(1) { struct sockaddr_in client_addr; char cli_ip[INET_ADDRSTRLEN] = ""; socklen_t cliaddr_len = sizeof(client_addr); int connfd; connfd = accept(sockfd (struct sockaddr*)&client_addr &cliaddr_len); if(connfd < 0) { perror("accept"); continue; } inet_ntop(AF_INET &client_addr.sin_addr cli_ip INET_ADDRSTRLEN); printf("----------------------------------------------\n"); printf("client ip=%s port=%d\n" cli_ip ntohs(client_addr.sin_port)); char recv_buf[2048] = ""; while( recv(connfd recv_buf sizeof(recv_buf) 0) > 0 ) { printf("\nrecv data:\n"); printf("%s\n" recv_buf); } close(connfd); //关闭已连接套接字 printf("client closed!\n"); } close(sockfd); //关闭监听套接字 return 0; }

猜您喜欢: