网络LinuxLinux网络编程-TCP,UDP套接字编程及代码示范
Posted zhaocx111222333
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络LinuxLinux网络编程-TCP,UDP套接字编程及代码示范相关的知识,希望对你有一定的参考价值。
本文我们分为TCP和UDP协议网络程序的编写:
他们的区别如下:
项目 | 特点 |
---|---|
UDP | 用户数据报协议,无需连接,不可靠,面向数据报,其实时性>可靠性(典型的就是视频传输) |
TCP | 传输控制协议,面向连接,可靠,面向字节流,其可靠性>实时性(文件传输) |
UDP类
1 #include <cstdio>
2 #include <iostream>
3 #include <string>
4 #include <unistd.h>
5 #include <arpa/inet.h>
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8 //dup网络协议类
9 //需要实现5个接口,
10 //1.创建套接字
11 //2.绑定地址信息(一般是服务器端需要)
12 //3.发送数据
13 //4.接受数据
14 class UDPSocket{
15 //套接字的成员变量就是套接字操作句柄
16 private:
17 int _sockfd;
18
19 public:
20
21 UDPSocket()
22 :_sockfd(-1){}
23 //创建套接字
24 //socket(地址域类型,套接字类型,本次通信协议)
25 bool Socket(){
26 _sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
27 if(_sockfd<0){
28 perror("socket error");
29 return false;
30 }
31 return true;
32 }
33
34
35 //绑定地址信息(套接字操作句柄,addr结构体地址信息指针,地址信息长度)
36 //不同的通讯协议地址信息大小不同
37 //统一的接口是sockaddr(为了接口规范)16字节
38 //ipv4是sockaddr_in 16字节
39 //封装的接口需要ip地址和port值
40 bool Bind(const std::string &ip,uint16_t port){
41 struct sockaddr_in addr;
42 addr.sin_family=AF_INET;
43 addr.sin_port=htons(port);
44 addr.sin_addr.s_addr=inet_addr(ip.c_str());
45 socklen_t len=sizeof(struct sockaddr_in);
46 int ret;
47 //第二参数要穿地址!!
48 ret=bind(_sockfd,(struct sockaddr*)&addr,len);
49 if(ret<0){
50 perror("bind error");
51 return false;
52 }
53 return true;
54 }
55
56
57 //发送数据接口
58 //(套接字操作句柄,发送空间首地址,数据长度,0阻塞,对端地址信息,对端地址结构长度)
59 //封装的接口要数据的地址信息,对端ip和port
60 //注意ip是const,可以直接以"192.0.0.0"格式发送,否则只能是string类型
61 //因为"192.0.0.0"是常量字符串
62 bool Send(std::string& data,const std::string &ip,u_int16_t port){
63 struct sockaddr_in addr;
64 addr.sin_family=AF_INET;
65 addr.sin_port=htons(port);
66 addr.sin_addr.s_addr=inet_addr(ip.c_str());
67 socklen_t len=sizeof(struct sockaddr_in);
68 int ret=sendto(_sockfd,data.c_str(),data.size(),0,(sockaddr*)&addr,len);
69 if(ret<0){
70 perror("sendto error");
71 return false;
72 }
73 return true;
74 }
75
76 //接受数据(操作句柄,接受空间地址,接收数据长度,0阻塞,(输出参数)发送源端地址信息,(输入输出型参数)指定接收的地质结构大小,返回实际的大小)
77 ///封装的接口是接受地址,接收发送方ip和port的地址(一般情况可以不接受,指定为NULL表示不需要直到发送方的信息)
78 bool Recv(std::string * buf,std::string* ip=NULL,int * port=NULL){
79 struct sockaddr_in addr;
80 socklen_t len=sizeof(struct sockaddr_in);
81 char tmp[1024]={0};
82 int ret=recvfrom(_sockfd,tmp,1024,0,(sockaddr*)&addr,&len);
83 if(ret<0){
84 perror("sendto error");
85 return false;
86 }
87 //ret为世界接收到的数据长度
88 buf->assign(tmp,ret);
89
90 if(ip!=NULL){
91 *ip=inet_ntoa(addr.sin_addr);
92 }
93 if(port!=NULL){
94 *port=ntohs(addr.sin_port);
95 }
96 return true;
97 }
98
99 //关闭套接字
100 bool Close(){
101 if(_sockfd!=-1){
102 close(_sockfd);
103 }
104 return true;
105 }
106
107 };
UDP服务端单执行流
1 #include"classudp.hpp"
2
3 #define CHECK_RET(q) if((q)==false){return -1;}
4 //服务端需要做的步骤:
5 //1.创建套接字
6 //2.绑定地址信息
7 //3.接收数据,(如果需要反馈给客户端则需要记录地址信息)
8 //4.发送数据
9 int main(){
10 UDPSocket ssock;
11 CHECK_RET(ssock.Socket());
12 CHECK_RET(ssock.Bind("192.168.107.128",9000));
13 while(1){
14 std::string buf;
15 int cliport;
16 std::string clip;
17 CHECK_RET(ssock.Recv(&buf,&clip,&cliport));
18 std::cout<<"client say: "<<buf<<std::endl;
19 buf.clear();//清空缓冲区
20 std::cout<<"server say: ";
21 std::cin>>buf;
22 CHECK_RET(ssock.Send(buf,clip,cliport));
23 }
24 ssock.Close();
25 return 0;
26 }
UDP客户端
1 #include "classudp.hpp"
2
3 #define CHECK_RET(q) if((q)==false){return -1;}
4 //客户端的任务流程
5 //1.创建套接字
6 //2.绑定地址结构
7 //3.发送数据
8 //4.接受数据
9 //5.关闭套接字
10 int main(){
11 //1.
12 UDPSocket sock;
13 CHECK_RET(sock.Socket());
14 //2.
15 //3.
16 while(1){
17 std::cout<<"client saY:";
18 std::string buf;
19 std::cin>>buf;
20 CHECK_RET(sock.Send(buf,"192.168.107.128",9000));
21
22
23 //4.
24 buf.clear();//不用的缓冲区清空
25 CHECK_RET(sock.Recv(&buf));//用发送缓冲区接收,不接收源端信息就不写
26 std::cout<<"server say: "<<buf<<std::endl;
27 }
28 //5.
29 sock.Close();
30 return 0;
31 }
TCP类
1 #include <cstdio>
2 #include <iostream>
3 #include <string>
4 #include <unistd.h>
5 #include <arpa/inet.h>
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8
9 #define LISTEN_MAX 5
10 #define CHECK_RET(q) if((q)==false){return -1;}
11 //tcp的流程大致如下“
12 //客户端:创建套接字,不推荐绑定地址信息,向服务器发送链接请求,收发数据,关闭套接字
13 //服务端:创建套接字,绑定地址信息,开始监听,获取新建连接,收发数据,关闭套接字
14 class TCPSocket{
15 private:
16 int _sockfd;
17 public:
18 TCPSocket():_sockfd(-1){}
19 //创建套接字
20 //socket(地址域类型,套接字类型,通信协议)
21 //封装的接口需要获取操作句柄
22 bool Socket(){
23 _sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
24 if(_sockfd<0){
25 perror("socket error");
26 return false;
27 }
28 return true;
29 }
30
31 //绑定地址信息
32 //接口(句柄,规范接口struct结构体,结构体大小)、
33 //封装的接口(ip地址,port信息)
34 bool Bind(const std::string &ip,const uint16_t port){
35 sockaddr_in addr;
36 addr.sin_family = AF_INET;//16位地址类型
37 addr.sin_port = htons(port);
38
39 //这两种都能拿到ip内容(str的首地址)
40 addr.sin_addr.s_addr = inet_addr(&ip[0]);
41 //addr.sin_addr.s_addr = inet_addr(ip.c_str());
42
43 socklen_t len = sizeof(sockaddr_in);
44 int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
45
46 if (ret < 0) {
47 perror("bind error");
48 return false;
49 }
50 return true;
51
52 }
53
54
55 //监听接口(操作句柄,最大连接数)
56 //封装接口(最大连接数)
W> 57 bool Listen(int big =LISTEN_MAX){
58 int ret=listen(_sockfd,LISTEN_MAX);
59 if(ret<0){
60 return false;
61 }
62 return true;
63 }
64
65
66
67 //申请链接(操作句柄,服务端的地址,地址长度)
68 //封装的接口(服务器ip,服务器port)
69 bool Connect(const std::string &ip,uint16_t port){
70 sockaddr_in addr;
71 addr.sin_family = AF_INET;
72 addr.sin_port = htons(port);
73 addr.sin_addr.s_addr = inet_addr(&ip[0]);
74 socklen_t len = sizeof(sockaddr_in);
75 int ret = connect(_sockfd, (sockaddr*)&addr, len);
76 if (ret < 0) {
77 perror("connect error");
78 return false;
79 }
80 return true;
81 }
82
83 //封装的接口:
84 //注意三个都是输出型参数(因为是处理请求的接口,所以不需要输入什么信息,只需要获得)
85 //服务器同意链接接口(新的套接字地址,客户端的地址结构指针,地址结构大小指针)
86 //注意这个的前提是客户端发送请求了,所以后两个数输出结构,用来获取请求的客户端的地址信息,当然可以不获取
87
88 //原始的的接口(监听套接字,获取客户端的ip,客户端的port地址)
89 //返回值是一个新的套接字,专门用来服务客户端,所以监听套接字相当于门迎~
90 //将返回值放到封装的接口参数1,原始接口2是输出参数,分开封装到封装接口的23
91 bool Accept(TCPSocket *sock,std::string *ip=NULL,uint16_t *port=NULL){
92 //建立一个结构体用来存客户端的地址信息
93 sockaddr_in addr;
94 socklen_t len = sizeof(sockaddr_in);
95
96 //注意这个len是输出型参数,所以是取地址!!!!
97 int newfd=accept(_sockfd,(sockaddr*)&addr,&len);
98 if (newfd < 0) {
99 perror("accept error");
100 return false;
101 }
102
103 //将输出型参数1填复制的句柄
104 sock->_sockfd = newfd;
105 if (ip != NULL) {
106 *ip = inet_ntoa(addr.sin_addr);
107 }
108 if (port != NULL) {
109 *port = ntohs(addr.sin_port);
110 }
111 return true;
112
113 }
114
115
116 //接收数据(操作句柄,接收缓冲区,接收数据长度,0阻塞)
117 //封装的接口(接收缓冲区)
118 //注意返回值小于0错误
119 //等于0连接断开
120 //大于0,实际大小
121 bool Recv(std::string *buf){
122 char tmp[1024]={0};
123 int ret=recv(_sockfd,tmp,1024,0);
124 if (ret < 0) {
125 perror("recv error");
126 return false;
127 }
128 else if (ret == 0) {
129
130 printf("peer shutdown");
131 return false;
132 }
133 //把缓冲区的内容放入真正的缓冲区
134 //因为原始接口需要指定一次接受的数据大小
135 //而直接用缓冲区接收不能确定大小
136 buf->assign(tmp, ret);
137 return true;
138 }
139
140 //发送数据(描述符,数据,长度,标志位0阻塞)
141 //封装的接口(要发送数据)
142 //返回实际的长度
143 //这里需要考虑一次发送不完全部数据该怎么办
144 bool Send(const std::string &data){
145 int total;
W>146 while(total < data.size())以上是关于网络LinuxLinux网络编程-TCP,UDP套接字编程及代码示范的主要内容,如果未能解决你的问题,请参考以下文章