基于UDP 实现客户端之间通信【2】
2018-01-04 14:26
591 查看
在写面向UDP连接的 socket 的通信程序时,我先总结归纳一些关于面向TCP和UDP连接的socket 通信程序的相关知识:
面向TCP连接的 socket 通信程序:
服务端:创建套接字,指定协议族(sockaddr_in),绑定,监听,接受连接,发送或接受数据,关闭连接;
客户端:创建套接字,指定协议族,连接(connect),发送或接受数据,关闭连接;
TCP在接受数据时:write/send/sendto, read/recv/recvfrom都可以用, 通常会用send, recv;
但在面向UDP的socket程序中,发送数据时用sendto的话,就不用connect了,但是在面向TCP的程序中
,在发送数据时,即使用sendto,也必须用connect
面向UDP连接的socket通信程序:
服务端:创建套接字,指定协议族(sockaddr_in),绑定(不需要listen和accept),发送或接收数据;
客户端:创建套接字,指定协议族,连接(和TCP的客户端步骤一样),发送或接受数据;
UDP常用sendto,recvfrom; 注意:用sendto时,就不用connect了(用了也没事),其他的(write, send)
必须用connect;
补充:1、无论是TCP还是UDP,默认情况下创建的都是阻塞模式的套接字,执行到(accept,connect, write/send/sento,read/recv/recvfrom)
等语句时,会一直等待(connect)有点列外,它连接一段时间,如果连接不成功,会以错误形式返回,不会一直等待
2、可以把socket设置成非阻塞模式, Linux下用fcntl函数,TCP和UDP设置成非阻塞模式以后,效果是一样的,都不再等待
而是立即返回
3、TCP面向连接, UDP面向无连接
TCP:客户端退出程序时或断开连接时,TCP的这个函数会立即返回不在阻塞(因为服务端自己知道客户端已经退出或断开连接,证明它是面向连接的)
UDP:始终保持阻塞(服务端不知道客户端已经退出或断开连接,证明它是面向无连接的)
4、TCP无边界,UDP有边界
TCP:客户端连续发送数据,只要服务端的这个函数的缓冲区足够大,会一次性接收过来
(客户端是分好几次发过来,是有边界的,而服务端却一次性接收过来,所以证明是无边界的)
UDP:客户端连续发送数据,即使服务端的这个函数的缓冲区足够大,也只会一次一次的接收,客户端分
几次发送过来,服务端就必须按几次接收
补充:
1、socket()的参数不同
2、UDP Server不需要调用listen和accept
3、UDP收发数据用sendto/recvfromhanshu
4、UDP:shutdown函数无效
5、TCP:地址信息在connect/accept时确定 UDP:在sendto/recvfrom函数中每次均需指定地址信息
Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器
进行连接,所以在发送数据时应指明目的地址
下面就是我写的利用UDP连接和多线程实现的客户端之间的通信代码:
服务器端:UdpServer.c
面向TCP连接的 socket 通信程序:
服务端:创建套接字,指定协议族(sockaddr_in),绑定,监听,接受连接,发送或接受数据,关闭连接;
客户端:创建套接字,指定协议族,连接(connect),发送或接受数据,关闭连接;
TCP在接受数据时:write/send/sendto, read/recv/recvfrom都可以用, 通常会用send, recv;
但在面向UDP的socket程序中,发送数据时用sendto的话,就不用connect了,但是在面向TCP的程序中
,在发送数据时,即使用sendto,也必须用connect
面向UDP连接的socket通信程序:
服务端:创建套接字,指定协议族(sockaddr_in),绑定(不需要listen和accept),发送或接收数据;
客户端:创建套接字,指定协议族,连接(和TCP的客户端步骤一样),发送或接受数据;
UDP常用sendto,recvfrom; 注意:用sendto时,就不用connect了(用了也没事),其他的(write, send)
必须用connect;
补充:1、无论是TCP还是UDP,默认情况下创建的都是阻塞模式的套接字,执行到(accept,connect, write/send/sento,read/recv/recvfrom)
等语句时,会一直等待(connect)有点列外,它连接一段时间,如果连接不成功,会以错误形式返回,不会一直等待
2、可以把socket设置成非阻塞模式, Linux下用fcntl函数,TCP和UDP设置成非阻塞模式以后,效果是一样的,都不再等待
而是立即返回
3、TCP面向连接, UDP面向无连接
TCP:客户端退出程序时或断开连接时,TCP的这个函数会立即返回不在阻塞(因为服务端自己知道客户端已经退出或断开连接,证明它是面向连接的)
UDP:始终保持阻塞(服务端不知道客户端已经退出或断开连接,证明它是面向无连接的)
4、TCP无边界,UDP有边界
TCP:客户端连续发送数据,只要服务端的这个函数的缓冲区足够大,会一次性接收过来
(客户端是分好几次发过来,是有边界的,而服务端却一次性接收过来,所以证明是无边界的)
UDP:客户端连续发送数据,即使服务端的这个函数的缓冲区足够大,也只会一次一次的接收,客户端分
几次发送过来,服务端就必须按几次接收
补充:
1、socket()的参数不同
2、UDP Server不需要调用listen和accept
3、UDP收发数据用sendto/recvfromhanshu
4、UDP:shutdown函数无效
5、TCP:地址信息在connect/accept时确定 UDP:在sendto/recvfrom函数中每次均需指定地址信息
Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。由于本地socket并没有与远端机器
进行连接,所以在发送数据时应指明目的地址
下面就是我写的利用UDP连接和多线程实现的客户端之间的通信代码:
服务器端:UdpServer.c
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #define PORT 8888 struct info { char buf[100]; int port; }; int main() { int sockfd, length, ret, j, i = 0; struct sockaddr_in server_addr; struct sockaddr_in client_addr[10] = {0}; struct sockaddr_in tmp_addr; struct info RecvBuf = {0}; sockfd = socket(PF_INET, SOCK_DGRAM, 0); if (-1 == sockfd) { perror("socket"); exit(1); } bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = PORT; server_addr.sin_addr.s_addr = inet_addr("192.168.0.128"); ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (ret < 0) { perror("bind"); exit(1); } while (1) { length = sizeof(client_addr[0]); ret = recvfrom(sockfd, &RecvBuf, sizeof(RecvBuf), 0, (struct sockaddr *)&tmp_addr, &length); if (ret < 0) { perror("recvfrom"); exit(1); } printf("Recv From Client %d : %s\n", tmp_addr.sin_port, RecvBuf.buf); if (0 == i) { client_addr[0].sin_family = tmp_addr.sin_family; client_addr[0].sin_port = tmp_addr.sin_port; client_addr[0].sin_addr.s_addr = tmp_addr.sin_addr.s_addr; i++; } else { for (j = 0; j < i; j++) { if (tmp_addr.sin_port == client_addr[j].sin_port) { break; } if (j == i - 1) { client_addr[i].sin_family = tmp_addr.sin_family; client_addr[i].sin_port = tmp_addr.sin_port; client_addr[i].sin_addr.s_addr = tmp_addr.sin_addr.s_addr; i++; } } } if (!strcmp(RecvBuf.buf, "bye")) { break; } strcat(RecvBuf.buf, "-server"); for(j = 0; j < i; j++) { if (RecvBuf.port == client_addr[j].sin_port) { break; } if (j == i - 1) { break; } } ret = sendto(sockfd, &RecvBuf, sizeof(RecvBuf), 0, (struct sockaddr *)&client_addr[j], sizeof(client_addr[0])); if (ret < 0) { perror("sendto"); exit(1); } memset(&RecvBuf, 0, sizeof(RecvBuf)); } close(sockfd); return 0; }客户端:UdpClient.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define PORT 8888 struct info { char buf[100]; int port; }; void *Send(void *arg) { struct info SendBuf = {0}; struct sockaddr_in server_addr; int ret; bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = PF_INET; server_addr.sin_port = PORT; server_addr.sin_addr.s_addr = inet_addr("192.168.0.128"); while(1) { scanf("%s %d", SendBuf.buf, &SendBuf.port); ret = sendto(*(int *)arg, &SendBuf, sizeof(SendBuf), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)); if (ret < 0) { perror("sendto"); exit(1); } if (!strcmp(SendBuf.buf, "bye")) { break; } bzero(&SendBuf, sizeof(SendBuf)); } } void *Recv(void *arg) { struct info RecvBuf = {0}; int length; struct sockaddr_in server_addr; int ret; while (1) { length = sizeof(server_addr); ret = recvfrom(*(int *)arg, &RecvBuf, sizeof(RecvBuf), 0, (struct sockaddr *)&server_addr, &length); if (ret < 0) { perror("recvfrom"); exit(1); } printf("Recv From Server : %s\n", RecvBuf.buf); } } int main() { int sockfd, ret, length; struct info SendBuf = {0}; pthread_t tid[2]; sockfd = socket(PF_INET, SOCK_DGRAM, 0); if (-1 == sockfd) { perror("sockt"); exit(1); } ret = pthread_create(&tid[0], NULL, Send, (void *)&sockfd); if (ret < 0) { perror("pthread_create"); exit(1); } ret = pthread_create(&tid[1], NULL, Recv, (void *)&sockfd); if (ret < 0) { perror("pthread_create"); exit(1); } pthread_join(tid[0], NULL); pthread_join(tid[1], NULL); close(sockfd); return 0; }
相关文章推荐
- C#基于TCP&UDP实现服务器与多个客户端之间的通信(客户端之间直接通信,不靠服务器端转发消息)
- Java基于UDP实现服务器和多客户端之间的通信
- linux网络编程之用多线程实现客户端到服务端的通信(基于udp)
- Java 基于 UDP 实现 Socket中的多客户端通信
- Java 基于 UDP 实现 Socket中的多客户端通信
- linux网络编程之用socket实现简单客户端和服务端的通信(基于UDP)
- Java基于TCP实现服务器和多客户端之间的通信
- 基于UDP协议下的客户端与服务器之间的通信
- 基于TCP 实现客户端之间通信【1】
- 基于UDP的服务器和客户端之间的通信
- python网络编程-使用socket实现S/C之间的UDP通信
- linux网络编程之用socket实现简单客户端和服务端的通信(基于TCP)
- 如何实现基于UDP的socket的通信
- JAVA实现NIO非阻塞UDP通信--客户端
- Linux中使用C语言实现基于UDP协议的Socket通信示例
- java多线程实现服务器端与多客户端之间的通信
- udp客户端与服务端之间的通信实例
- tornado实现高性能无阻塞udp通信(2)——实现异步udp客户端
- [置顶] java 服务端,实现服务端与客户端之间的通信以及客户端之间的通信
- Node.js权威指南 (7) - 实现基于TCP与UDP的数据通信