700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > tcp与udp的socket编程 udp代码实现客户端服务端 tcp代码实现客户端服务端(单线程

tcp与udp的socket编程 udp代码实现客户端服务端 tcp代码实现客户端服务端(单线程

时间:2021-08-16 01:19:44

相关推荐

tcp与udp的socket编程 udp代码实现客户端服务端 tcp代码实现客户端服务端(单线程

socket编程

网络字节序定义为什么网络数据需要转换为网络字节序?Udp协议Tcp协议Udp的socket编程编程流程接口创建套接字绑定地址信息发送接收关闭主机字节序转换成为网络字节序将ip地址从点分十进制转化为无符号32位整数并转换为网络字节序将网络字节序转换为主机字节序并转换为点分十进制的ip地址代码实现绑定地址udp程序服务端客户端运行udp程序Tcp的socket编程Tcp的编程流程(三次握手四次挥手)Tcp socket编程的接口创建套接字绑定地址信息监听发起连接建立新连接发送数据接收数据关闭套接字Tcp代码实现判断端口是否在监听单线程实现Tcp代码服务端客户端多进程实现Tcp代码服务端多线程实现Tcp代码

网络字节序

定义

什么是字节序:CPU对内存数据的存取顺序大端字节序&小端字节序大端:低位保存在高地址小端:低位保存在低地址7812563434561278网络字节序:采用大端字节序传输,防止出现大小端机器解读数据出错的情况主机字节序:主机字节序指的是机器具体的字节序主机是大端:认为主机字节序是大端主机是小端:认为主机字节序是小端网络设计需要继续转发之前都要将主机字节序转换成网络字节序网络数据接收之前都需要将网络字节序转换成主机字节序

为什么网络数据需要转换为网络字节序?

1、网络规定采用大端字节序作为网络字节序2、路由设备或者交换机需要对网络数据进行分用到网络层,以获得“目的ip地址”,而这些设备在继续分用的时候默认是按照网络字节序进行分用的

Udp协议

无连接:当udp的客户端想要给udp服务端发送数据的时候,只需要知道服务端的ip和端口,就可以直接发送引申含义:在发送数据之前是不知道服务端的状态信息的不可靠:不报证udp的数据一定到达对端机器面向数据报:udp数据是整条数据接收和发送的

Tcp协议

有连接:连接双方在发送数据之前,需要进行连接(指需要提前告知,沟通连接的信息)可靠传输:保证数据是有序且可靠到达对端面向字节流:上一次和下一次的数据是没有明显的数据边界的

Udp的socket编程

编程流程

cs模型(一般使用):client(客户端)-server(服务端)bs模型:浏览器-服务器客户端服务端1、创建套接字1、创建套接字2、绑定地址信息(不推荐自己绑定)2、绑定地址信息:ip地址、端口信息3、先由客户端向服务端发送数据3、再由服务端向客户端回复数据4、关闭套接字4、关闭套接字

接口

创建套接字

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

domain:指定当前的地址域,相当于指定网络层到底使用什么协议AF_INET:ipv4网络(默认选择)AF_INET6:ipv6网络AF_UNIX:本地域套接字type:创建套接字的类型SOCK_DGRAM:用户数据报套接字 =》udp协议SOCK_STREAM:流式套接字 =》tcp协议protocol:0:表示使用套接字类型默认的协议IPPROTO_UDP:17IPPROTO_TCP:6返回值:返回桃姐字描述符,套接字描述符本质上就是一个文件描述符成功:大于等于0失败:小于0创建套接字的原因在于想在内核当中创建一块与协议相对应的缓冲区需要包含一个头文件

#include <sys/socket.h>

如果protocol一栏中不使用默认的0则需要包含一个头文件

#include <netinet/in.h>

1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/socket.h>4 #include <netinet/in.h>5 6 int main()7 {8 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);9 if(sockfd < 0)10 {11perror("socket fail\n");12return 0;13 }14 15 printf("sockfd : %d\n", sockfd);16 while(1)17 {18sleep(1);19 }20 return 0;21 }

会发现在闪烁,此时闪烁的原因是它指向了内核中的缓冲区

绑定地址信息

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

sockfd:socket函数返回的套接字描述符将创建出来的套接字和网卡、端口进行绑定addr:地址信息结构struct sockaddr---》通用地址信息结构struct sockaddr {sa_family_t sa_family;//地址域信息,占用两个字节//地址域信息决定了当前使用寿命网络层协议char sa_data[14]; //本质上任何网络程序都不会直接填充这个14个字节的字符组,//定义第二个参数总共占用14个字节}———————————————|2字节地址域信息 ||一一 || ↑||14字节地址数据|| ↓||一一|———————————————ipv4:vim /usr/include/netinet/in.hstruct sockaddr_in{__SOCKADDR_COMMON (sin_);//地址域信息:2字节in_port_t sin_port; /* Port number. *///端口信息:2字节struct in_addr sin_addr; /* Internet address. *///ipv4版本的ip地址:4字节/* Pad to size of `struct sockaddr'. *///填充剩下的部分为0:8字节unsigned char sin_zero[sizeof (struct sockaddr) -__SOCKADDR_COMMON_SIZE -sizeof (in_port_t) -sizeof (struct in_addr)];};———————————————|2字节地址域信息|| 2字节端口信息||4字节|| ip地址||一一|| ↑|| 8字节填充为0|| ↓||一一|———————————————地址域信息:网络协议栈通过该信息,来判断传递进来的是哪一个数据结构,判断出来之后,就知道后面的字节该如何解析本地域套接字:进行本地进程间通信的一种手段struct sockaddr_un———————————————|2字节地址域信息||一一|| ↑||108字节路径名|| ↓||一一|———————————————addrlen:地址信息结构的长度,防止越界

发送

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

spckfd:套接字描述符buf:想要发送的数据len:要发送数据的长度flags:0:阻塞发送dest_addr:目标主机的地址信息结构(ip,port(端口))addrlen:目标主机地址信息结构的长度返回值:成功:返回具体发送的字节数量失败:返回-1客户端为什么不推荐绑定地址信息?本质上是不想让那个客户端程序将端口写死即不想让客户端在启动的时候都是绑定一个端口(一个端口只能被一个协议当中的一个进程所绑定)客户端1绑定了端口,本机在启动客户端2的时候会绑定失败,因为端口已经被客户端1拿走了客户端没有主动的绑定端口,udp客户端在调用sendto的时候,会自动绑定(操作系统分配)一个空闲的端口

接收

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

sockfd:套接字描述符buf:将数据接收到buf当中len:最大可以接收多大的数据flags:0:阻塞接收src_addr:接收的数据来源的主机地址信息结构addrlen:输入输出型参数输入:在接收之前准备的对端地址信息结构的长度输出:实际接收回来的地址信息长度返回值:接收到:接收的字节数量未接收:-1

eg:char* buf[1024] = {0};<--数据接收的位置struct sockaddr_in src_addr; <--定义一个具体结构体例如ipv4recvfrom(sockfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&src_addr, )↑预留\0↑传递结构体的地址,需要强转为(struct sockaddr*)接收完后src_addr中有ip和port,即发送数据的主机的ip地址和端口

关闭

close(int sockfd);

主机字节序转换成为网络字节序

接口1:h(ost)ton(et)l(long) --> htonl 4字节uint32_t htonl(uint32_t hostlong);如果主机是大端机调用接口则什么都不干,如果主机是小端机则会将数据转换成小端再返回接口2:htons(hort) --> htons 2字节 uint16_t htons(uint16_t hostshort);网络字节序转换成为主机字节序接口1:ntohl 4字节uint32_t ntohl(uint32_t netlong);接口2:ntohs 2字节 uint16_t ntohs(uint16_t netshort);

将ip地址从点分十进制转化为无符号32位整数并转换为网络字节序

in_addr_t inet_addr(const char *cp);

将网络字节序转换为主机字节序并转换为点分十进制的ip地址

char *inet_ntoa(struct in_addr in);

代码实现

绑定地址

首先测试一下绑定地址信息

1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/socket.h> //包含了各种数据结构4 #include <netinet/in.h>5 #include <arpa/inet.h>6 7 int main()8 {9 //创建套接字10 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);11 if(sockfd < 0)12 {13perror("socket failed\n");14return 0;15 }16 //绑定17 struct sockaddr_in addr;//定义一个ipv4的端口18 addr.sin_family = AF_INET;//地址域,告诉操作系统内核传递的是哪种结构体19 addr.sin_port = htons(12345);//传递需要监听的端口,需要转换成为网络字节序20 addr.sin_addr.s_addr = inet_addr("0.0.0.0");//ip地址保存的变量21 //0.0.0.0表示当前机器任一一块网卡的ip地址 22 //127.0.0.1表示当前机器本地回环网卡地址23 24 int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));25 if(ret < 0)26 {27perror("bind failed\n");28return 0;29 }30 31 while(1)32 {33sleep(1);34 }35 return 0;36 }

此时没有任何输出,说明while(1)之前的步骤都成功了

可以看到确实是创建成功了,可以使用 netstat -anp | grep [端口] 来查看端口使用情况

udp 表示当前12345端口使用的是udp协议0.0.0.0:12345 代表当前监听的ip地址与端口0.0.0.0:* 表示接收从任一ip和任一端口来的数据如果此时再启动一次程序,会报错,因为端口已经被占用了

udp程序

服务端

1 #include <stdio.h>2 #include <string.h>3 #include <unistd.h>4 #include <sys/socket.h> //包含了各种数据结构5 #include <netinet/in.h>6 #include <arpa/inet.h>7 8 int main()9 {10 //创建套接字11 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);12 if(sockfd < 0)13 {14perror("socket failed\n");15return 0;16 }17 //绑定18 struct sockaddr_in addr;//定义一个ipv4的端口19 addr.sin_family = AF_INET;//地址域,告诉操作系统内核传递的是哪种结构体20 addr.sin_port = htons(12345);//传递需要监听的端口,需要转换成为网络字节序21 addr.sin_addr.s_addr = inet_addr("10.0.8.14");//ip地址保存的变量22 //0.0.0.0表示当前机器任一一块网卡的ip地址23 //127.0.0.1表示当前机器本地回环网卡地址24 25 int ret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));26 if(ret < 0)27 {28perror("bind failed\n");29return 0;30 }31 32 while(1)33 {34//接收部分35 36char buf[1024] = {0};37struct sockaddr_in peer_addr;38socklen_t peer_addrlen = sizeof(peer_addr);39ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, (struct sockaddr*)&peer_addr, &peer_addrlen);40if(recv_size < 0)41{42 continue;//接收失败继续接收43}44//成功45printf("i am server, i recv %s, from %s:%d\n", buf, inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));46 47//发送部分48 49memset(buf, '\0', sizeof(buf));//清空buf50sprintf(buf, "hello, client %s:%d, i am server\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));51//写入到buf中52//发送53sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&peer_addr, sizeof(peer_addr));54 }55 return 0;56 }

客户端

1 #include <stdio.h> 2 #include <string.h>3 #include <unistd.h>4 #include <sys/socket.h> //包含了各种数据结构5 #include <netinet/in.h>6 #include <arpa/inet.h>7 8 int main()9 {10 //创建套接字11 int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);12 if(sockfd < 0)13 {14perror("socket failed\n");15return 0;16 }17 18 //不用绑定19 20 while(1)21 {22//发送数据23char buf[1024] = "i am client1";24 25struct sockaddr_in dest_addr;26dest_addr.sin_family = AF_INET;27dest_addr.sin_port = htons(12345);//需要发送到的端口28dest_addr.sin_addr.s_addr = inet_addr("10.0.8.14");29int se = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));30if(se < 0)31{32 continue;33}34 35//接收数据36memset(buf, '\0', sizeof(buf));37ssize_t recv_size = recvfrom(sockfd, buf, sizeof(buf) - 1, 0, NULL, NULL);//发送时已经获得了服务端的信息38if(recv_size < 0)39{40 perror("recvfrom failed\n");41 continue;42}43 44printf("server say: %s\n", buf);45 }46 return 0;47 }

运行udp程序

运行一下两个程序

Tcp的socket编程

Tcp的编程流程(三次握手四次挥手)

客户端服务端1、创建套接字1、创建套接字流式套接字创建流式套接字(SOCK_STREAM)2、不推荐绑定地址信息2、绑定地址信息ip/port3、发起连接3、监听①告诉操作系统内核,可以接收客户端发起的连接请求②监听新连接的到来三次握手建立连接4、接收新连接。。。互相发送数据。。。四次挥手断开连接完成三次握手后即建立连接完成三次握手的过程是在内核(网络协议栈)中完成的,建立连接的过程不需要程序员参与第一次:客户端给服务端发送连接请求第二次:服务端回复客户端的连接请求并发送连接请求第三次:客户端回复服务端的连接请求,连接建立断开连接需要四次挥手

Tcp socket编程的接口

创建套接字

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

绑定地址信息

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

监听

int listen(int sockfd, int backlog);

sockfd:套接字描述符backlog:已完成连接队列的大小三次握手中:客户端向服务端发送一个名为SYN的数据包,表示发起连接服务端收到请求后向客户端发送SYN+ACK两个数据包,其中SYN代表服务端回复也想建立连接,ACK是回复上一个SYN的客户端向服务端回复一个ACK数据包,用于回复服务端发送的SYN数据包此时内核中有两个队列,一个叫未完成连接队列,一个叫已完成连接队列已完成连接队列:1、已经完成三次握手等待被accept的连接2、连接状态为“连接建立”的连接为完成连接队列:没有完成三次握手的连接backlog影响了服务端并发接收连接的能力假设backlog是1,此时未完成队列中的数量为2,即此时只能有一个能进入已完成连接队列,在进入已完成连接队列后该新建立的连接被拿走,又有一个连接可以进入已完成连接队列,即吞吐量为1

发起连接

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

sockfd:套接字描述符addr:服务端的地址信息结构(服务端的ip和服务端的端口)addrlen:服务端地址信息结构长度返回值:成功:0失败:-1

建立新连接

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

作用:从已完成连接队列当中获取已经完成三次握手的连接sockfd:套接字描述符addr:客户端的地址信息结构(客户端的ip和客户端的端口)addrlen:客户端地址信息结构的长度返回值:成功:新连接的套接字描述符,也是个文件描述符失败:-1①TCP服务端在通信之前创建套接字A,套接字当中有发送缓冲区和接收缓冲区,在绑定地址信息和监听后便可以接收新连接②此时应该客户端发起连接,发起连接后与TCP服务端创建的套接字A产生了关联,即三次握手的数据是在套接字A中处理的③调用accept后会在服务端中再创建一个套接字B,也就是新连接的套接字,随后客户端与套接字B进行数据的收发④套接字A为监听套接字即“listen sock”,套接字B为“new sock”⑤若此时再有一个客户端想建立连接,发起连接后同样是与套接字A进行三次握手建立连接,在建立连接后客户端会再创建一个新套接字C与新的客户端进行数据收发如果没有连接的时候,调用accept也会阻塞

发送数据

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

sockfd:套接字描述符accept的返回值即新连接的套接字buf:待要发送的数据len:发送数据的长度flags:0:阻塞发送MSG_OBB:发送带外数据返回值:大于0:返回发送的字节数量-1:发送失败

接收数据

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

sockfd:套接字描述符accept的返回值即新连接的套接字buf:将接收的数据放到哪里去len:buf的最大接收能力flags:0 阻塞接收返回值:大于0:正常接收了多少字节数据等于0:对端将连接关闭了小于0:接收失败如果连接没有数据,则调用recv会阻塞

关闭套接字

int close(int fd);

Tcp代码实现

判断端口是否在监听

1 #include <stdio.h>2 #include <unistd.h>3 #include <sys/socket.h>4 #include <netinet/in.h>5 #include <arpa/inet.h>6 7 int main()8 {9 //建立套接字10 int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);11 if(listen_sock < 0)12 {13perror("listen_sock failed\n");14return 0;15 }16 17 //绑定18 struct sockaddr_in addr;19 addr.sin_family = AF_INET;20 addr.sin_port = htons(12345);21 addr.sin_addr.s_addr = inet_addr("10.0.8.14");//使用ifconfig邦定内网地址22 int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));23 if(ret < 0)24 {25perror("bind failed\n");26return 0;27 }28 29 //监听30 ret = listen(listen_sock, 1);31 if(ret < 0)32 {33perror("listen failed\n");34return 0;35 }36 37 while(1)38 {39sleep(1);40 }41 return 0;42 }

可以看到端口12345现在正在处于LISTEN状态

单线程实现Tcp代码

服务端

1 #include <stdio.h>2 #include <string.h>3 #include <unistd.h>4 #include <sys/socket.h>5 #include <netinet/in.h>6 #include <arpa/inet.h>7 8 int main()9 {10 //创建套接字11 int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);12 if(listen_sock < 0)13 {14perror("listen_sock failed\n");15return 0;16 }17 18 //绑定19 struct sockaddr_in addr;20 addr.sin_family = AF_INET;21 addr.sin_port = htons(12345);22 addr.sin_addr.s_addr = inet_addr("10.0.8.14");//使用ifconfig邦定内网地址23 int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));24 if(ret < 0)25 {26perror("bind failed\n");27return 0;28 }29 30 //监听31 ret = listen(listen_sock, 1);32 if(ret < 0)33 {34perror("listen failed\n");35return 0;36 }37 38 while(1)39 {40//建立新连接41struct sockaddr_in peer_addr;42socklen_t peer_addrlen = sizeof(peer_addr);43int new_sock = accept(listen_sock, (struct sockaddr*)&peer_addr, &peer_addrlen);44if(new_sock < 0)45{46 perror("accept failed\n");47 return 0;48}49 50//接收成功51printf("accept %s:%d, create new_sock %d\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port), new_sock);52 53 54//先接收55char buf[1024] = {0};56ssize_t recv_size = recv(new_sock, buf, sizeof(buf) - 1, 0);57if(recv_size < 0)58{59 perror("recv failed\n");60 continue;61}62else if(recv_size == 0)//对端关闭63{64 perror("peer shutdown\n");65 close(new_sock);//对端关闭则关闭自己66 67 close(listen_sock);68 return 0;69}70 71//接收成功72printf("client %d say: %s\n", new_sock, buf);73 74//发送75memset(buf, '\0', sizeof(buf));//清空buf76sprintf(buf, "i am server, hello client %d\n", new_sock);77 78ssize_t send_size = send(new_sock, buf, strlen(buf), 0);79if(send_size < 0)80{81 perror("send failed\n");82 continue;83}84 }85 86 close(listen_sock);87 return 0;88 }

客户端

1 #include <stdio.h>2 #include <string.h>3 #include <unistd.h>4 #include <sys/socket.h>5 #include <netinet/in.h>6 #include <arpa/inet.h>7 8 int main()9 {10 int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);11 if(sockfd < 0)12 {13perror("sockfd failed\n");14return 0;15 }16 17 //连接18 struct sockaddr_in dest_addr;19 dest_addr.sin_family = AF_INET;20 dest_addr.sin_port = htons(12345);21 dest_addr.sin_addr.s_addr = inet_addr("159.75.12.23");22 int ret = connect(sockfd, (struct sockaddr*)&dest_addr, sizeof(dest_addr));23 if(ret < 0)24 {25perror("connect failed\n");26return 0;27 }28 29 while(1)30 {31//发送32char buf[1024] = "i am tcp client 1";33ssize_t send_size = send(sockfd, buf, strlen(buf), 0);34if(send_size < 0)35{36 perror("send failed\n");37 continue;38}39 40//接收41memset(buf, '\0', sizeof(buf));42ssize_t recv_size = recv(sockfd, buf, sizeof(buf) - 1, 0);43if(recv_size < 0)44{45 perror("recv fail\n");46 continue;47}48else if(recv_size == 0)49{50 printf("server shutdown\n");51 close(sockfd);52 return 0; 53}54 55printf("server say: %s\n", buf);56 }57 58 return 0;59 }

多进程实现Tcp代码

客户端同单线程

服务端

1 #include <stdio.h>2 #include <string.h>3 #include <unistd.h>4 #include <sys/socket.h>5 #include <netinet/in.h>6 #include <arpa/inet.h>7 #include <signal.h>8 #include <sys/wait.h>9 W> 10 void sigcallback(int signo)11 {12 wait(NULL);13 }14 15 int main()16 {17 signal(SIGCHLD, sigcallback);18 19 //创建套接字20 int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);21 if(listen_sock < 0)22 {23perror("listen_sock failed\n");24return 0;25 }26 27 //绑定28 struct sockaddr_in addr;29 addr.sin_family = AF_INET;30 addr.sin_port = htons(12345);31 addr.sin_addr.s_addr = inet_addr("10.0.8.14");//使用ifconfig邦定内网地址32 int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));33 if(ret < 0)34 {35perror("bind failed\n");36return 0;37 }38 39 //监听40 ret = listen(listen_sock, 1);41 if(ret < 0)42 {43perror("listen failed\n");44return 0;45 }46 47 while(1)48 {49//建立新连接50struct sockaddr_in peer_addr;51socklen_t peer_addrlen = sizeof(peer_addr);52int new_sock = accept(listen_sock, (struct sockaddr*)&peer_addr, &peer_addrlen);53if(new_sock < 0)54{55 perror("accept failed\n");56 return 0;57}58 59//接收成功60printf("accept %s:%d, create new_sock %d\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port), new_sock);61 62pid_t pid = fork();//创建子进程63if(pid < 0)64{65 perror("fork failed\n");66 close(new_sock);67 continue;68}69else if(pid == 0)70{71 //child72 //recv73 close(listen_sock);//防止子进程受父进程中套接字的影响 74 while(1)75 {76 //先接收77 char buf[1024] = {0};78 ssize_t recv_size = recv(new_sock, buf, sizeof(buf) - 1, 0);79 if(recv_size < 0)80 {81 perror("recv failed\n");82 continue;83 }84 else if(recv_size == 0)//对端关闭85 {86 perror("peer shutdown\n");87 close(new_sock);//对端关闭则关闭自己88 89 close(listen_sock);90 return 0;91 }92 93 //接收成功94 printf("client %d say: %s\n", new_sock, buf);95 96 //发送97 memset(buf, '\0', sizeof(buf));//清空buf98 sprintf(buf, "i am server, hello client %d\n", new_sock);99 100 ssize_t send_size = send(new_sock, buf, strlen(buf), 0);101 if(send_size < 0)102 {103 perror("send failed\n");104 continue;105 }106 }107}108else109{110 //father111 close(new_sock);112 continue;113}114 }115 116 close(listen_sock);117 return 0;118 }

多线程实现Tcp代码

#pragma once//防止头文件重复包含 2 3 #include <stdio.h>4 #include <string.h>5 #include <unistd.h>6 #include <pthread.h>7 #include <sys/socket.h>8 #include <netinet/in.h>9 #include <arpa/inet.h>10 #include <string>11 12 using namespace std;13 14 class Tcp_Svr15 {16 public:17Tcp_Svr()18{19 sockfd_ = -1;20}21 22~Tcp_Svr()23{24 //close(sockfd_);25 //可在此close或新建一个close接口26}27 28int CreatSocket()//创建套接字29{30 //sockfd31 sockfd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);32 if(sockfd_ < 0)33 {34 perror("sockfd_ failed");35 return -1;36 }37 return sockfd_;38}39 40int Bind(const string& ip = "0.0.0.0", uint16_t port = 12345)//绑定地址信息41{42 //使用sockfd43 struct sockaddr_in addr;44 addr.sin_family = AF_INET;45 addr.sin_port = htons(port);46 addr.sin_addr.s_addr = inet_addr(ip.c_str());47 int ret = bind(sockfd_, (struct sockaddr*)&addr, sizeof(addr));48 if(ret < 0)49 {50 perror("bind failed");51 return -1;52 }53 return 0;54}55 56int Listen(int backlog = 5)//监听57{58 //使用sockfd59 int ret = listen(sockfd_, backlog);60 if(ret < 0)61 {62 perror("listen failed");63 return -1;64 }65 return 0;66}67 68int Accept(struct sockaddr_in* peer_addr)//建立连接69{70 //使用sockfd71 socklen_t peer_addrlen = sizeof(struct sockaddr_in);72 //如果直接给peer_addr到sizeof中只会传递一个指针的大小,传递一个类型的大小才是正确的73 int ret = accept(sockfd_, (struct sockaddr*)peer_addr, &peer_addrlen);74 if(ret < 0)75 {76 peer_addr = NULL;77 return -1;78 }79 80 return ret;81}82 83void Close()84{85 close(sockfd_);86}87 88void SetSockFd(int sockfd)89{90 sockfd_ = sockfd;91}9293int Send(const string& data)//发送94{95 ssize_t send_size = send(sockfd_, data.c_str(), data.size(), 0);96 if(send_size < 0)97 {98 perror("send failed");99 return -1;100 }101 return send_size;102}103 104int Recv(string* data)105{106 data->clear();//防止有脏数据107 char buf[1024] = {0};108 ssize_t recv_size = recv(sockfd_, buf, sizeof(buf) - 1, 0);109 if(recv_size < 0)110 {111 perror("recv failed");112 return -1;113 }114 else if(recv_size == 0)115 {116 printf("peer shutdown\n");117 return -100;118 }119 120 data->assign(buf, strlen(buf));121 return recv_size;122}123 124 private:125int sockfd_;126 };

1 #include "tcp_thread.hpp" 2 3 #define CHECK_RET(v) if(v < 0){return -1;}4 5 void* tcp_thread_start(void* arg)6 {7 pthread_detach(pthread_self());8 Tcp_Svr* new_ts = (Tcp_Svr*)arg;9 10 while(1)11 {12//接收13string data = "";14int ret = new_ts->Recv(&data);15if(ret == -100)16{17 new_ts->Close();18 delete new_ts;//释放19 pthread_exit(NULL);20}21else if(ret <= -1)22{23 continue;24}25else if(ret > 0)//大于0代表接收到了内容26{27 printf("client say:%s\n", data.c_str());28}2930//发送31data.clear();32 33data.assign("i am server");34new_ts->Send(data);35 }36 37 return NULL;38 }39 40 int main()41 {42 Tcp_Svr ts;43 CHECK_RET(ts.CreatSocket());44 CHECK_RET(ts.Bind());45 CHECK_RET(ts.Listen());46 47 while(1)48 {49struct sockaddr_in peer_addr;50int new_sockfd = ts.Accept(&peer_addr);51if(new_sockfd < 0)52{53 continue;//接收失败继续接收54}55 56//代码执行到这里,说明接收了一个新的客户端57//1、创建线程(在线程内部让创建出来的线程自己把自己分离)58//2、给线程传递的参数怎么传递5960Tcp_Svr* new_ts = new Tcp_Svr();61if(new_ts == NULL)62{63 //关闭套接字64 close(new_sockfd);65 continue;66}67 68new_ts->SetSockFd(new_sockfd);69 70pthread_t tid;71int ret = pthread_create(&tid, NULL, tcp_thread_start, (void*)new_ts);72if(ret < 0)73{74 perror("pthread_create failed");75 //关闭新连接套接字,以触发客户端感知到服务端有误了76 new_ts->Close();77 delete new_ts;78 continue;79}80 81//主线程再次while(1)循环,进行accept82 }83 return 0;84 }

tcp与udp的socket编程 udp代码实现客户端服务端 tcp代码实现客户端服务端(单线程 多线程 多进程分别实现) 三次握手

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。