socket笔记TCPUDP通信总结

Posted 嵌入式大杂烩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket笔记TCPUDP通信总结相关的知识,希望对你有一定的参考价值。

点击上方「嵌入式大杂烩」,选择「置顶公众号」第一时间阅读编程笔记!

【温馨提示】这篇笔记比较长,可在后台回复关键词:TCP,获取这篇笔记的PDF及文中代码的下载链接。


一、什么是socket?

Socket的英文原义是“孔”或“插座”。在编程中,Socket被称做 套接字,是网络通信中的一种约定。Socket编程的应用无处不在,我们平时用的QQ、微信、浏览器等程序,都与Socket编程有关。我们平时使用浏览器查资料,这个过程的技术原理是怎样的呢?

我们平时使用浏览器,大致就是这样的一个过程。这里有两个重要的名词: 服务端与 客户端

Socket编程的目的就是如何实现这两端之间的通信。

1、Socket编程在嵌入式中也很重要

Socket编程不仅仅在互联网方面很重要,在我们的嵌入式方面也是非常的重要,因为现在很多电子设备都趋向于联网。比如很多嵌入式工作的招聘要求都会有这一条要求:

【socket笔记】TCP、UDP通信总结

【socket笔记】TCP、UDP通信总结

说一点题外话,还在学校的朋友,如果感觉到很迷茫,不知道学什么的时候,可以上招聘网站上看看自己未来工作相关的职位的任职要求,这样就可以总结自己的一些不足、比较有针对性的去学习。

二、Socket编程中的几个重要概念

1、什么是IP地址?

2、什么是TCP/IP端口?

3、什么是协议?

协议(Protocol)是通信双方进行数据交互的一种约定。如TCP、UDP协议:

(1)TCP协议

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,数据可以准确发送,数据丢失会重发。TCP协议常用于web应用中。

TCP连接(三次握手)

TCP传输起始时,客户端、服务端要完成三次数据交互工作才能建立连接,常称为三次握手。可形象比喻为如下对话:

客户端:服务端您好,我有数据要发给你,请求您开通访问权限。

服务端:客户端您好,已给您开通权限,您可以发送数据了。

客户端:收到,谢谢。

具体示意图为:

【socket笔记】TCP、UDP通信总结

这里的SYN和ACK是都是标志位,其中SYN代表新建一个连接,ACK代表确认。其中m、n都是随机数。具体说明如:

  • 第一次握手:SYN标志位被置位,客户端向服务端发送一个随机数m。

  • 第二次握手:ACK、SYN标志位被置位。服务端向客户端发送m+1表示确认刚才收到的数据,同时向客户端发送一个随机数n。

  • 第三次握手:ACK标志被置位。客户端向服务端发送n+1表示确认收到数据。

TCP断开(四次挥手)

TCP断开连接时,客户端、服务端要完成四次数据交互工作才能建立连接,常称为四次挥手。可形象比喻为如下对话:

客户端:服务端您好,我发送数据完毕了,即将和您断开连接。

服务端:客户端您好,我稍稍准备一下,再给您断开

服务端:客户端您好,我准备好了,您可以断开连接了。

客户端:好的,合作愉快!

具体示意图为:

【socket笔记】TCP、UDP通信总结

这里的FIN也是一个标志位,代表断开连接。具体说明类似三次握手。

为什么建立连接只需要三次数据交互,而断开连接需要四次呢?

建立连接时,服务端在监听状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。

而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。

(2)UDP协议

UDP(User Datagram Protocol, 用户数据报协议)是一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,可以保证通讯效率,传输延时小。例如视频聊天应用中用的就是UDP协议,这样可以保证及时丢失少量数据,视频的显示也不受很大影响。

4、什么是协议族?

协议族是多个协议的统称。比如我们的TCP/IP协议族,其不仅仅是TCP协议、IP协议,而是多个协议的集合,其包含IP、TCP、UDP、FTP、SMTP等协议。

三、socket编程的API接口

1、Linux下的socket API接口

(1)创建socket:socket()函数

函数原型:

 
   
   
 
  1. int socket(int af, int type, int protocol);

  • type参数:type 为数据传输方式,常用的有 面向连接( SOCK_STREAM)方式(即TCP) 和 无连接( SOCK_DGRAM)的方式(即UDP)。

  • protocol参数:protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。

使用示例:

创建TCP套接字:

 
   
   
 
  1. int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

创建UDP套接字:

 
   
   
 
  1. int udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

(2)绑定套接字:bind()函数

函数原型:

 
   
   
 
  1. int bind(int sock, struct sockaddr *addr, socklen_t addrlen);

  • sock参数:sock 为 socket 文件描述符。

  • addr参数:addr 为 sockaddr 结构体变量的指针。

  • addrlen参数:addrlen 为 addr 变量的大小,可由 sizeof() 计算得出。

使用示例:

将创建的套接字 ServerSock与本地IP 127.0.0.1、端口 1314进行绑定:

 
   
   
 
  1. /* 创建服务端socket */

  2. int ServerSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);


  3. /* 设置服务端信息 */

  4. struct sockaddr_in ServerSockAddr;

  5. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // 给结构体ServerSockAddr清零

  6. ServerSockAddr.sin_port = htons(1314); // 端口


  7. /* 绑定套接字 */

  8. bind(ServerSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR));

其中 structsockaddr_in类型的结构体变量用于保存IPv4的IP信息。若是IPv6,则有对应的结构体:

 
   
   
 
  1. struct sockaddr_in6

  2. {

  3. in_port_t sin6_port; // 16位端口号

  4. uint32_t sin6_flowinfo; // IPv6流信息

  5. uint32_t sin6_scope_id; // 接口范围ID

  6. };

(3)建立连接:connect()函数

函数原型:

 
   
   
 
  1. int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);

参数与 bind()的参数类似。

使用示例:

 
   
   
 
  1. int ClientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  2. connect(ClientSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR));

(4)监听:listen()函数

函数原型:

 
   
   
 
  1. int listen(int sock, int backlog);

  • sock参数:sock 为需要进入监听状态的套接字。

  • backlog参数:backlog 为请求队列的最大长度。

使用示例:

 
   
   
 
  1. /* 进入监听状态 */

  2. listen(ServerSock, 10);

(5)接收请求:accept()函数

函数原型:

 
   
   
 
  1. int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);

  • sock参数:sock 为服务器端套接字。

  • addr参数:addr 为 sockaddr_in 结构体变量。

  • addrlen参数:addrlen 为参数 addr 的长度,可由 sizeof() 求得。

  • 返回值:一个新的套接字,用于与客户端通信。

使用示例:

 
   
   
 
  1. /* 监听客户端请求,accept函数返回一个新的套接字,发送和接收都是用这个套接字 */

  2. int ClientSock = accept(ServerSock, (SOCKADDR*)&ClientAddr, &len);

(6)关闭:close()函数

函数原型:

 
   
   
 
  1. int close(int fd);

  • fd:要关闭的文件描述符。

使用示例:

 
   
   
 
  1. close(ServerSock);

(7)数据的接收和发送

数据收发函数有几组:

  • read()/write()

  • recv()/send()

  • readv()/writev()

  • recvmsg()/sendmsg()

  • recvfrom()/sendto()

函数原型:

 
   
   
 
  1. ssize_t read(int fd, void *buf, size_t count);

  2. ssize_t write(int fd, const void *buf, size_t count);

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

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

  5. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,

  6. const struct sockaddr *dest_addr, socklen_t addrlen);

  7. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,

  8. struct sockaddr *src_addr, socklen_t *addrlen);

  9. ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

  10. ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

这里介绍一下recv()/send()、recvfrom()/sendto()。

recv()函数:

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

  • sockfd参数:sockfd为要接收数据的套接字。

  • len参数:len 为要接收的数据的字节数。

  • flags参数:flags 为接收数据时的选项,常设为0。

send()函数:

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

  • sockfd参数:sockfd为要发送数据的套接字。

  • len参数:len 为要发送的数据的字节数。

  • flags参数:flags 为发送数据时的选项,常设为0。

recvfrom()函数:

 
   
   
 
  1. ssize_t recvfrom(int sock, void *buf, size_t nbytes, int flags, struct sockadr *from, socklen_t *addrlen);

  • sock:用于接收UDP数据的套接字;

  • nbytes:可接收的最大字节数(不能超过buf缓冲区的大小);

  • flags:可选项参数,若没有可传递0;

sendto()函数:

 
   
   
 
  1. ssize_t sendto(int sock, void *buf, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);

  • sock:用于传输UDP数据的套接字;

  • nbytes:带传输数据的长度(以字节计);

  • flags:可选项参数,若没有可传递0;

2、windows下的socket API接口

跟Linux下的差不多:

 
   
   
 
  1. SOCKET socket(int af, int type, int protocol);

  2. int bind(SOCKET sock, const struct sockaddr *addr, int addrlen);

  3. int connect(SOCKET sock, const struct sockaddr *serv_addr, int addrlen);

  4. int listen(SOCKET sock, int backlog);

  5. SOCKET accept(SOCKET sock, struct sockaddr *addr, int *addrlen);

  6. int closesocket( SOCKET s);

  7. int send(SOCKET sock, const char *buf, int len, int flags);

  8. int recv(SOCKET sock, char *buf, int len, int flags);

  9. int recvfrom(SOCKET sock, char *buf, int nbytes, int flags, const struct sockaddr *from, int *addrlen);

  10. int sendto(SOCKET sock, const char *buf, int nbytes, int flags, const struct sockadr *to, int addrlen);

3、TCP、UDP通信的socket编程过程图

(1)TCP通信socket编程过程

【socket笔记】TCP、UDP通信总结

(2)UDP通信socket编程过程

【socket笔记】TCP、UDP通信总结

四、socket的应用实例

1、基于TCP的本地客户端、服务端信息交互实例

本例的例子实现的功能为:本地TCP客户端往本地TCP服务端发送数据,TCP服务端收到数据则会打印输出,同时把原数据返回给TCP客户端。这个例子类似于我们在做单片机的串口实验时,串口上位机往我们的单片机发送数据,单片机收到数据则把该数据原样返回给上位机。

(1)windows的程序:

服务端程序tcp_server.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <winsock2.h>


  3. #define BUF_LEN 100


  4. int main(void)

  5. {

  6. WSADATA wd;

  7. SOCKET ServerSock, ClientSock;

  8. char Buf[BUF_LEN] = {0};

  9. SOCKADDR ClientAddr;

  10. SOCKADDR_IN ServerSockAddr;

  11. int addr_size = 0, recv_len = 0;


  12. /* 初始化操作sock需要的DLL */

  13. WSAStartup(MAKEWORD(2,2),&wd);


  14. /* 创建服务端socket */

  15. if (-1 == (ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))

  16. {

  17. printf("socket error!\n");

  18. exit(1);

  19. }


  20. /* 设置服务端信息 */

  21. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // 给结构体ServerSockAddr清零

  22. ServerSockAddr.sin_port = htons(1314); // 端口


  23. /* 绑定套接字 */

  24. if (-1 == bind(ServerSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR)))

  25. {

  26. printf("bind error!\n");

  27. exit(1);

  28. }


  29. /* 进入监听状态 */

  30. if (-1 == listen(ServerSock, 10))

  31. {

  32. printf("listen error!\n");

  33. exit(1);

  34. }


  35. addr_size = sizeof(SOCKADDR);


  36. while (1)

  37. {

  38. /* 监听客户端请求,accept函数返回一个新的套接字,发送和接收都是用这个套接字 */

  39. if (-1 == (ClientSock = accept(ServerSock, (SOCKADDR*)&ClientAddr, &addr_size)))

  40. {

  41. printf("socket error!\n");

  42. exit(1);

  43. }


  44. /* 接受客户端的返回数据 */

  45. int recv_len = recv(ClientSock, Buf, BUF_LEN, 0);

  46. printf("客户端发送过来的数据为:%s\n", Buf);


  47. /* 发送数据到客户端 */

  48. send(ClientSock, Buf, recv_len, 0);


  49. /* 关闭客户端套接字 */

  50. closesocket(ClientSock);


  51. /* 清空缓冲区 */

  52. memset(Buf, 0, BUF_LEN);

  53. }


  54. /*如果有退出循环的条件,这里还需要清除对socket库的使用*/

  55. /* 关闭服务端套接字 */

  56. //closesocket(ServerSock);

  57. /* WSACleanup();*/


  58. return 0;

  59. }

客户端程序tcp_client.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <winsock2.h>


  3. #define BUF_LEN 100


  4. int main(void)

  5. {

  6. WSADATA wd;

  7. SOCKET ClientSock;

  8. char Buf[BUF_LEN] = {0};

  9. SOCKADDR_IN ServerSockAddr;


  10. /* 初始化操作sock需要的DLL */

  11. WSAStartup(MAKEWORD(2,2),&wd);


  12. /* 向服务器发起请求 */

  13. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr));

  14. ServerSockAddr.sin_family = AF_INET;

  15. ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

  16. ServerSockAddr.sin_port = htons(1314);


  17. while (1)

  18. {

  19. /* 创建客户端socket */

  20. if (-1 == (ClientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))

  21. {

  22. printf("socket error!\n");

  23. exit(1);

  24. }

  25. if (-1 == connect(ClientSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR)))

  26. {

  27. printf("connect error!\n");

  28. exit(1);

  29. }

  30. printf("请输入一个字符串,发送给服务端:");

  31. gets(Buf);

  32. /* 发送数据到服务端 */

  33. send(ClientSock, Buf, strlen(Buf), 0);


  34. /* 接受服务端的返回数据 */

  35. recv(ClientSock, Buf, BUF_LEN, 0);

  36. printf("服务端发送过来的数据为:%s\n", Buf);


  37. memset(Buf, 0, BUF_LEN); // 重置缓冲区

  38. closesocket(ClientSock); // 关闭套接字

  39. }


  40. // WSACleanup(); /*如果有退出循环的条件,这里还需要清除对socket库的使用*/

  41. return 0;

  42. }

本文使用的是gcc编译器编译(关于gcc编译器的相关介绍可查看往期笔记:及),编译命令如下:

 
   
   
 
  1. gcc tcp_client.c -o tcp_client.exe -lwsock32

  2. gcc tcp_server.c -o tcp_server.exe -lwsock32

这里必须要加 -lwsock32这个参数用于链接windows下socket编程必须的winsock2这个库。若是使用集成开发环境,则需要把 wsock32.lib放在工程目录下,并在我们代码中 #include<winsock2.h> 下面加上一行 #pragmacomment(lib,"ws2_32.lib")代码(这种情况本人未验证,有兴趣的朋友可尝试)。

实验现象:

先启动服务端程序 tcp_server.exe,再启动客户端程序 tcp_client.exe,并在客户端中输入字符串,则当服务端会接收到字符串时会打印输出,与此同时也会往客户端返回相同的数据:

【socket笔记】TCP、UDP通信总结

动图:

【socket笔记】TCP、UDP通信总结

(2)Linux的程序:

在linux下,“一切都是文件”,所以这里我们的套接字也当做文件来看待。

服务端程序linux_tcp_server.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <string.h>

  3. #include <stdlib.h>

  4. #include <unistd.h>

  5. #include <arpa/inet.h>

  6. #include <sys/socket.h>

  7. #include <netinet/in.h>


  8. #define BUF_LEN 100


  9. int main(void)

  10. {

  11. int ServerFd, ClientFd;

  12. char Buf[BUF_LEN] = {0};

  13. struct sockaddr ClientAddr;

  14. int addr_len = 0, recv_len = 0;

  15. struct sockaddr_in ServerSockAddr;

  16. int optval = 1;


  17. /* 创建服务端文件描述符 */

  18. if (-1 == (ServerFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))

  19. {

  20. printf("socket error!\n");

  21. exit(1);

  22. }


  23. /* 设置服务端信息 */


  24. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // 给结构体ServerSockAddr清零

  25. ServerSockAddr.sin_port = htons(6666); // 端口



  26. if (setsockopt(ServerFd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)

  27. {

  28. printf("setsockopt error!\n");

  29. exit(1);

  30. }


  31. if (-1 == bind(ServerFd, (struct sockaddr*)&ServerSockAddr, sizeof(struct sockaddr)))

  32. {

  33. printf("bind error!\n");

  34. exit(1);

  35. }


  36. /* 进入监听状态 */

  37. if (-1 == (listen(ServerFd, 10)))

  38. {

  39. printf("listen error!\n");

  40. exit(1);

  41. }


  42. addr_len = sizeof(struct sockaddr);


  43. while (1)

  44. {

  45. /* 监听客户端请求,accept函数返回一个新的套接字,发送和接收都是用这个套接字 */

  46. if (-1 == (ClientFd = accept(ServerFd, (struct sockaddr*)&ClientAddr, &addr_len)))

  47. {

  48. printf("accept error!\n");

  49. exit(1);

  50. }


  51. /* 接受客户端的返回数据 */

  52. if ((recv_len = recv(ClientFd, Buf, BUF_LEN, 0)) < 0)

  53. {

  54. printf("recv error!\n");

  55. exit(1);

  56. }


  57. printf("客户端发送过来的数据为:%s\n", Buf);


  58. /* 发送数据到客户端 */

  59. send(ClientFd, Buf, recv_len, 0);


  60. /* 关闭客户端套接字 */

  61. close(ClientFd);


  62. /* 清空缓冲区 */

  63. memset(Buf, 0, BUF_LEN);

  64. }


  65. return 0;

  66. }

客户端程序linux_tcp_client.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <string.h>

  3. #include <stdlib.h>

  4. #include <unistd.h>

  5. #include <arpa/inet.h>

  6. #include <sys/socket.h>


  7. #define BUF_LEN 100


  8. int main(void)

  9. {

  10. int ClientFd;

  11. char Buf[BUF_LEN] = {0};

  12. struct sockaddr_in ServerSockAddr;



  13. /* 向服务器发起请求 */

  14. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr));

  15. ServerSockAddr.sin_family = AF_INET;

  16. ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

  17. ServerSockAddr.sin_port = htons(6666);


  18. while (1)

  19. {

  20. /* 创建客户端socket */

  21. if (-1 == (ClientFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))

  22. {

  23. printf("socket error!\n");

  24. exit(1);

  25. }


  26. /* 连接 */

  27. if (-1 == connect(ClientFd, (struct sockaddr*)&ServerSockAddr, sizeof(ServerSockAddr)))

  28. {

  29. printf("connect error!\n");

  30. exit(1);

  31. }


  32. printf("请输入一个字符串,发送给服务端:");

  33. gets(Buf);

  34. /* 发送数据到服务端 */

  35. send(ClientFd, Buf, strlen(Buf), 0);

  36. memset(Buf, 0, BUF_LEN); // 重置缓冲区


  37. /* 接受服务端的返回数据 */

  38. recv(ClientFd, Buf, BUF_LEN, 0);

  39. printf("服务端发送过来的数据为:%s\n", Buf);


  40. memset(Buf, 0, BUF_LEN); // 重置缓冲区

  41. close(ClientFd); // 关闭套接字

  42. }


  43. return 0;

  44. }

Linux下编译就不需要添加 -lwsock32参数:

 
   
   
 
  1. gcc linux_tcp_server.c -o linux_tcp_server

  2. gcc linux_tcp_client.c -o linux_tcp_client

实验现象:

【socket笔记】TCP、UDP通信总结

在调试这份程序时,出现了绑定错误:

【socket笔记】TCP、UDP通信总结

经上网查询发现是端口重复使用,可以在调用 bind()函数之前调用 setsockopt()函数以解决端口重复使用的问题:

【socket笔记】TCP、UDP通信总结

2、基于UDP的本地客户端、服务端信息交互实例

(1)windows的程序

服务端程序udp_server.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <winsock2.h>


  3. #define BUF_LEN 100


  4. int main(void)

  5. {

  6. WSADATA wd;

  7. SOCKET ServerSock;

  8. char Buf[BUF_LEN] = {0};

  9. SOCKADDR ClientAddr;

  10. SOCKADDR_IN ServerSockAddr;

  11. int addr_size = 0;



  12. /* 初始化操作sock需要的DLL */

  13. WSAStartup(MAKEWORD(2,2),&wd);


  14. /* 创建服务端socket */

  15. if(-1 == (ServerSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)))

  16. {

  17. printf("socket error!\n");

  18. exit(1);

  19. }


  20. /* 设置服务端信息 */

  21. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // 给结构体ServerSockAddr清零

  22. ServerSockAddr.sin_port = htons(1314); // 端口


  23. /* 绑定套接字 */


  24. if (-1 == (bind(ServerSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR))))

  25. {

  26. printf("bind error!\n");

  27. exit(1);

  28. }


  29. addr_size = sizeof(SOCKADDR);


  30. while (1)

  31. {

  32. /* 接受客户端的返回数据 */

  33. int str_len = recvfrom(ServerSock, Buf, BUF_LEN, 0, &ClientAddr, &addr_size);


  34. printf("客户端发送过来的数据为:%s\n", Buf);


  35. /* 发送数据到客户端 */

  36. sendto(ServerSock, Buf, str_len, 0, &ClientAddr, addr_size);


  37. /* 清空缓冲区 */

  38. memset(Buf, 0, BUF_LEN);

  39. }


  40. /*如果有退出循环的条件,这里还需要清除对socket库的使用*/

  41. /* 关闭服务端套接字 */

  42. //closesocket(ServerSock);

  43. /* WSACleanup();*/


  44. return 0;

  45. }

客户端程序udp_client.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <winsock2.h>


  3. #define BUF_LEN 100


  4. int main(void)

  5. {

  6. WSADATA wd;

  7. SOCKET ClientSock;

  8. char Buf[BUF_LEN] = {0};

  9. SOCKADDR ServerAddr;

  10. SOCKADDR_IN ServerSockAddr;

  11. int ServerAddrLen = 0;


  12. /* 初始化操作sock需要的DLL */

  13. WSAStartup(MAKEWORD(2,2),&wd);


  14. /* 创建客户端socket */

  15. if (-1 == (ClientSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)))

  16. {

  17. printf("socket error!\n");

  18. exit(1);

  19. }


  20. /* 向服务器发起请求 */

  21. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr));

  22. ServerSockAddr.sin_family = PF_INET;

  23. ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

  24. ServerSockAddr.sin_port = htons(1314);


  25. ServerAddrLen = sizeof(ServerAddr);


  26. while (1)

  27. {

  28. printf("请输入一个字符串,发送给服务端:");

  29. gets(Buf);

  30. /* 发送数据到服务端 */

  31. sendto(ClientSock, Buf, strlen(Buf), 0, (struct sockaddr*)&ServerSockAddr, sizeof(ServerSockAddr));


  32. /* 接受服务端的返回数据 */

  33. recvfrom(ClientSock, Buf, BUF_LEN, 0, &ServerAddr, &ServerAddrLen);

  34. printf("服务端发送过来的数据为:%s\n", Buf);


  35. memset(Buf, 0, BUF_LEN); // 重置缓冲区

  36. }


  37. closesocket(ClientSock); // 关闭套接字

  38. // WSACleanup(); /*如果有退出循环的条件,这里还需要清除对socket库的使用*/

  39. return 0;

  40. }

(2)Linux下的程序

服务端程序linux_udp_server.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <string.h>

  3. #include <stdlib.h>

  4. #include <unistd.h>

  5. #include <arpa/inet.h>

  6. #include <sys/socket.h>

  7. #include <netinet/in.h>


  8. #define BUF_LEN 100


  9. int main(void)

  10. {

  11. int ServerFd;

  12. char Buf[BUF_LEN] = {0};

  13. struct sockaddr ClientAddr;

  14. struct sockaddr_in ServerSockAddr;

  15. int addr_size = 0;

  16. int optval = 1;


  17. /* 创建服务端socket */

  18. if ( -1 == (ServerFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)))

  19. {

  20. printf("socket error!\n");

  21. exit(1);

  22. }


  23. /* 设置服务端信息 */

  24. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // 给结构体ServerSockAddr清零

  25. ServerSockAddr.sin_port = htons(1314); // 端口


  26. if (setsockopt(ServerFd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)

  27. {

  28. printf("setsockopt error!\n");

  29. exit(1);

  30. }


  31. if (-1 == bind(ServerFd, (struct sockaddr*)&ServerSockAddr, sizeof(ServerSockAddr)))

  32. {

  33. printf("bind error!\n");

  34. exit(1);

  35. }


  36. addr_size = sizeof(ClientAddr);


  37. while (1)

  38. {

  39. /* 接受客户端的返回数据 */

  40. int str_len = recvfrom(ServerFd, Buf, BUF_LEN, 0, &ClientAddr, &addr_size);


  41. printf("客户端发送过来的数据为:%s\n", Buf);


  42. /* 发送数据到客户端 */

  43. sendto(ServerFd, Buf, str_len, 0, &ClientAddr, addr_size);


  44. /* 清空缓冲区 */

  45. memset(Buf, 0, BUF_LEN);

  46. }


  47. close(ServerFd);


  48. return 0;

  49. }

客户端程序linux_udp_client.c:

 
   
   
 
  1. #include <stdio.h>

  2. #include <string.h>

  3. #include <stdlib.h>

  4. #include <unistd.h>

  5. #include <arpa/inet.h>

  6. #include <sys/socket.h>


  7. #define BUF_LEN 100


  8. int main(void)

  9. {

  10. int ClientFd;

  11. char Buf[BUF_LEN] = {0};

  12. struct sockaddr ServerAddr;

  13. int addr_size = 0;

  14. struct sockaddr_in ServerSockAddr;


  15. /* 创建客户端socket */

  16. if (-1 == (ClientFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)))

  17. {

  18. printf("socket error!\n");

  19. exit(1);

  20. }


  21. /* 向服务器发起请求 */

  22. memset(&ServerSockAddr, 0, sizeof(ServerSockAddr));

  23. ServerSockAddr.sin_family = PF_INET;

  24. ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

  25. ServerSockAddr.sin_port = htons(1314);


  26. addr_size = sizeof(ServerAddr);


  27. while (1)

  28. {

  29. printf("请输入一个字符串,发送给服务端:");

  30. gets(Buf);

  31. /* 发送数据到服务端 */

  32. sendto(ClientFd, Buf, strlen(Buf), 0, (struct sockaddr*)&ServerSockAddr, sizeof(ServerSockAddr));


  33. /* 接受服务端的返回数据 */

  34. recvfrom(ClientFd, Buf, BUF_LEN, 0, &ServerAddr, &addr_size);

  35. printf("服务端发送过来的数据为:%s\n", Buf);


  36. memset(Buf, 0, BUF_LEN); // 重置缓冲区

  37. }


  38. close(ClientFd); // 关闭套接字


  39. return 0;

  40. }

实验现象:

实验现象如实例1。

五、总结

下一篇分享一个基于TCP的HTTP客户端的实例,欢迎持续关注!



我的博客

zhengnianli.github.io

猜你喜欢







以上是关于socket笔记TCPUDP通信总结的主要内容,如果未能解决你的问题,请参考以下文章

socket编程TCPUDP通信总结

VC++使用socket进行TCPUDP通信实例总结

socket编程TCPUDP通信你会了吗?

socket编程TCPUDP通信你会了吗?

java中进行socket编程实现tcpudp协议总结

java中进行socket编程实现tcpudp协议总结