当前位置:   article > 正文

Unix高级编程:网络基础、基于TCP以及UDP的编程模型、TCP高并发编程模型_unix网络高级编程

unix网络高级编程
"ipcs" //man ipcs
功能:显示共享内存段、信号量数组、消息队列
命令行:ipcs


"ipcrm" 
ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] ...
功能:移除消息队列
命令行:ipcrm -q id


"ipcmk"
ipcmk [-M size] [-S nsems] [-Q] [-p mode]
功能:创建一个不同的ipc资源


一、网络基础
1. 客户端和服务器端的架构
"client" 客户端
"server" 服务器端


2. 什么是协议? "协议就是规则"


3. 网络协议采用的是 "TCP/IP协议族" (4(2~5)|5(1~5) 层)
TCP/IP协议的分层:/** 软件分层 **/
5>"应用层":Telnet、FTP和e-mail等
4>"传输层":TCP和UDP
3>"网络层":IP、ICMP和IGMP
2>"链路层":设备驱动程序及接口卡
1>"物理层":————第5层,规定了电气协议


4. 物理层规范了电气规则(电气协议)


补充:
1)"网络架构"
C/S架构 - 客户端
B/S架构 - 浏览器
"B/S是C/S的一种"


2)网线:跑的是电流
模电 / "数电"(高电压是1,低电压是0) 


3)"以太网数据帧的分用过程图 - 英文注解"
ARP 地址解析协议,把ipv4地址映射到硬件地址
RARP 反向地址解析协议,把硬件地址映射到ipv4地址
IP 网络协议
TCP 传输控制协议
UDP 用户数据报协议
ICMP Internet报文控制协议
IGMP 组管理协议
ICMPv6 网际控制消息协议版本6,综合了ICMP/IGMP/ARP的功能
BPF BSD分组过滤器,为应用程序提供访问数据链路层的接口,由源自BSD的系统内核提供
DLPI 数据链路提供者接口,为应用程序提供访问数据链路层的接口,由源自SVR4的系统内核提供


5. 客户端和服务器端需要有一个传输的基本单元,需要知道这个数据单元的起始标记和结束标记。这个单元就是"帧"。(以太网对应的是"以太网帧")
比如:农村家里接自来水,用水桶,接满一桶放到水缸。
/** 自来水 - 网络数据包;一水桶的水 - 帧;水缸 - 内存。 **/


6. 实现客户端和服务器端的通信,需要知道"对方的地址"和"端口号"。
不同的网络应用会使用不同的端口号。使用IP地址定位主机,再使用端口号定位定位运行在这台主机上的一个具体的网络应用。
0~65535 其中习惯上 0~5000 为系统所用,一般应用选择5000以上端口。
21 端口用于ftp服务,23 端口用于telnet服务,80 端口用于www服务等。
(IP地址和端口号:socket pair "通讯对")


7. 地址:逻辑地址/网络地址("IP地址")、物理地址("MAC地址")
每一块网卡都有自己的身份证号,即物理MAC地址(目的地址 - 6 个字节)
网络通信中最终认识的就是此MAC地址。
查看计算机的网络地址/物理地址:"sudo ifconfig"
网络/逻辑地址——inet地址,物理地址——硬件地址,"client一对一绑定"。
机器需要将逻辑地址和物理地址做绑定。


8. ip地址分为2种:ipv4("4个字节的32位地址")
 ipv6("16个字节的128位地址")
ip地址包括两部分内容,"网络号和主机号"。
"A类":0~127.0~255.0~255.0~255 以0为首的8位网络地址+24位本地地址
0.0.0.0  "主机号全0不能使用",主机号全0代表的是网络号。
0.255.255.255 "主机号全1不能用",这代表的是该网络的广播地址。
127.0.0.0  127.255.255.255
"B类":128~191.0~255.0~255.0~255 以10为首的16位网络地址+16位本地
"C类":192~223.0~255.0~255.0~255 以110为首的24位网络地址+8位本地
"D类":以 1110 为首的32位多播地址(28位多播组号)
"E类":以 11110 为首的32位多播地址(27位留后待用)
将ip地址分为私有ip和共有ip。


9. 子网掩码:有的时候需要将一个大的网络划分为更小的几个网络,这时候需要到子网掩码的技术。
将ip地址和子网掩码做 "与&" 操作,就可以得到这个ip属于哪个子网段。
格式:"IP地址 & 子网掩码 == 子网地址"
举例1:ip(192.168.1.56) & 子网掩码(255.255.255.0) = 192.168.1.0
说明:ip为192.168.1.56的电脑属于192.168.1.0网段的一台机器。
举例2:ip(192.168.129/24, 前面24位全1,即子网掩码255.255.255.0)
  ip(192.168.129/25, 子网掩码255.255.255.128)
192.168.1.129/25 & 255.255.255.128 == 192.168.1.128网段的局域网
192.168.1.123/25 & 255.255.255.128 == 192.168.1.0网段的局域网
以上两个局域网的主机号只有7位了,127-2(全0和全1) = 125 台机器。


10. 三种网络设备:
"集线器"hub,电流的放大和分流,属于电气层/一层交换。工作在物理层。
单个电脑占用网线数据时,其他电脑不可通过网线访问,也不可相互访问。
"交换机",交换的是网帧,帧属于链路层,属于二层交换。
单个电脑占用网线数据时,其他局域网内电脑互相访问不影响。
"路由器",交换的是ip报文,报文属于网络层,所以是三层交换。
实现了无限制访问。


11. 局域网内数据包的传输
从这台例如192.168.1.11的机器传输数据包给192.168.1.12的流程步骤:
第一步:
先判断192.168.1.12的网段和192.168.1.11是否相同,都与255.255.255.0做与&运算,均为192.168.1.0网段,说明是局域网内的传输。所以路由器不会将该数据包路由到外网。
第二步:
查看192.168.1.11机器上的arp表,如果arp表里面有192.168.1.12这条ip数据,那么将这条数据的对应的MAC地址添加到以太网帧的目的地址,这样数据包就被传送过去了,如果arp表中没有这条数据,192.168.1.11这台机器会发送arp请求广播,这时候192.168.1.12收到广播信息,查看与自己的ip地址是否一致。如果不是,不做任何反应;如果是,那就将自己的MAC地址通过arp应答回送给192.168.1.11,这时候192.168.1.11将帧的目的地址设置为该MAC地址,并且在arp表中添加该条记录。以此实现局域网内的数据包传输过程。


路由表 "sudo route -v" //查看路由表
arp表 "sudo arp -a" //查看arp表,ping后可查看对应ip的机器
"ping ip地址" //检测局域网是否连通


12. 三次握手


13. 四次分手


二、基于TCP的编程模型
TCP是面向连接的。客户端和服务器端通讯需要建立连接。
1. 基于TCP的"服务器端编程模型"
<1> 创建socket通讯端(socket) -> 初始化服务器(sockaddr_in成员)
<2> 将通讯端和服务器ip地址和端口号绑定(bind)
<3> 监听通讯端(listen)
<4> 等待客户端连接的到来,返回一个连接描述符(accept)
<5> 从连接描述符中读写数据(read/write)
<6> 对数据进行加工
<7> 关闭socket通讯端-连接描述符(close(参数);)
/** 整体是框架,用户自定的地方主要是<5><6>对数据的读写和处理 **/


2. 基于TCP的"客户端编程模型"
<1> 创建socket通讯端(socket) -> 初始化服务器(sockaddr_in成员)
<2> (inet_pton转换)-> 使用socket通讯端连接服务器
<3> 连接成功,通过socket通讯端向服务器发送数据,或从服务器获取数据
<4> 处理数据
<5> 关闭socket通讯端-连接描述符
/** 整体是框架,用户自定的地方主要是<4>对数据的读写和处理 **/


【服务器端编程模型】
/** 1.<1> 创建socket通讯端 **/
"socket"(2)
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
功能:创建一个通讯端点(返回一个文件描述符)
参数:
"domain"
AF_INET 用于ipv4的通讯
AF_INET6 用于ipv6的通讯
"type"
SOCK_STREAM 传输层选用的是TCP协议,面向数据流
SOCK_DGRAM 传输层选用的是UDP协议,面向数据包
"protocol" 0
返回值:
成功 - 返回新的文件描述符
失败 - 返回 -1,errno被设置


/** 1.<2> 将通讯端和服务器的ip地址和端口号绑定 **/
"bind"(2)
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
功能:将一个名字绑定到一个socket(第一个参数绑定到第二个参数)
参数:
"sockfd" socket(2)的返回值
"addr" socket pair通讯对,通用协议族 //ip地址和端口号构成的一个结构体
"addrlen" addr的长度
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置


补充:"man in.h"头文件下包含以下3个结构体:
"<netinet/in.h>"
#include <netinet/in.h>
struct sockaddr 通用协议族结构体
struct sockaddr_in ipv4协议族的结构体
struct sockaddr_in6 ipv6协议族的结构体
struct sockaddr {
   sa_family_t sa_family;
   char        sa_data[14];
};
struct sockaddr_in {
sa_family_t sin_family; /* 取值:AF_INET */
in_port_t sin_port; /* Port number:5000以下留给系统*/
struct in_addrsin_addr;/* IP address */
};
struct in_addr {
in_addr_t s_addr;/** INADDR_ANY 表示任意IP均可与服务器通信 **/
}


需要将ip地址的"点分十进制(ddd.ddd.ddd.ddd)"的字符串和无符号整形两种类型互相转换。
/** 点分十进制字符串 -> 网络字节序(无符号整数) **/ 转换
"inet_pton"(3)
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
功能:将ipv4或ipv6的字符串形势的ip地址转换为struct in_addr二元形式
参数:
"af" 
AF_INET IPv4网络
AF_INET6 IPv6网络
"src" 点分十进制的字符串格式
"dst" struct in_addr类型的内容
返回值:
成功 - 返回 1
失败 - 返回 0,代表src是无效地址
失败 - 返回 -1,errno被设置


/** 网络字节序(无符号整数) -> 点分十进制字符串 **/
"inet_ntop"(3)
#include <arpa/inet.h>
const char *inet_ntop(int af,const void *src,char *dst,socklen_t size);
功能:将ipv4或ipv6的地址从binary(二进制)到text(字符串)
参数:
"af" 
AF_INET IPv4网络
AF_INET6 IPv6网络
"src" struct in_addr类型的内容
"dst" 指定的字符串空间
"size" 拷贝到字符串的大小
返回值:
成功 - 返回字符串的首地址
失败 - 返回 NULL,errno被设置


"inet_ntoa"(3) //扩充
"inet_aton"(3) //扩充


一般情况下计算机配置的是小端,但是网络中使用的是大端。不管计算机使用的是大端还是小端,到"网络中必须使用大端"。
"htonl"(3)系列函数 /** 大小端的字节顺序byte order互相转换 **/
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
"h" 开头的:host主机字节序
"n" 开头的:net网络字节序
"l" 结尾的:long (长整型 32 位) - 可用于IP
"s" 结尾的:short (短整型 16 位) - 可用于端口


/** 1.<3> 监听通讯端 **/
"listen"(2)
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:监听socket连接
参数:
"sockfd" socket(2)返回值(type参数需指定SOCK_STREAM | SOCK_SEQPACKET)
"backlog" 允许的最大的未决连接数
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置


重点小结:
1、TCP/IP协议的分层(4层或5层,5层包含物理层,7层是模型非实际分层)
2、子网掩码(划分局域网的网段)
3、IP地址的分类
4、三次握手


/** 1.<4> 等待客户端连接的到来,返回一个连接描述符 **/
"accept"(2)
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:创建一个新的连接socket(监听到连接)
参数:
"sockfd" socket(2)的返回值
"addr" 获取客户端的socket pairs(通讯对:ip地址和端口号)
  如果addr为 NULL,addrlen需要置为 NULL
"addrlen" addr结构体变量的长度
返回值:
成功 - 返回 一个新的非负整数文件描述符
失败 - 返回 -1,errno被设置


网络编程


  1. "server"
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <unistd.h>
  7. #include <arpa/inet.h>
  8. #include <ctype.h>
  9. #include <string.h>
  10. typedef struct sockaddr SA;
  11. typedef struct sockaddr_in SA4;
  12. int main (void)
  13. {
  14. SA4 serv, cli; // 定义服务器地址变量,客户端地址变量
  15. int s_fd, conn_fd;
  16. int ret = 0;
  17. char buf[1024] = {0}, buf1[1024] = {0};
  18. char ip[128] = {0}; // 存放ip
  19. int port; // 存放端口
  20. /* 1. 创建socket通讯端 */
  21. s_fd = socket (AF_INET, SOCK_STREAM, 0);
  22. if (-1 == s_fd) {
  23. perror ("socket");
  24. return 1;
  25. }
  26. /* 2. 初始化服务器信息 */
  27. serv.sin_family = AF_INET; // ipv4
  28. serv.sin_port = htons (7778); // port
  29. serv.sin_addr.s_addr = htonl (INADDR_ANY); // 32bit long
  30. /* 3. 将服务器ip地址和端口号与s_fd绑定 */
  31. ret = bind (s_fd, (SA *)&serv, sizeof (serv));
  32. if (-1 == ret) {
  33. perror ("bind");
  34. return 2;
  35. }
  36. /* 4. 监听s_fd */
  37. listen (s_fd, 10); // 5个设备同时
  38. /* 5. 阻塞等待客户端的连接 */
  39. while (1) {
  40. /* 将客户端的通讯对保存在了cli指向的地址里 */
  41. int cli_len = sizeof (cli);
  42. conn_fd = accept (s_fd, (SA*)&cli, &cli_len);
  43. /* 将二进制ip转换为字符串ip地址打印 */
  44. char *p = (char*)inet_ntop (AF_INET, &cli.sin_addr, ip, 128);
  45. printf ("客户端ip:%s\n", p);
  46. printf ("端口号:%d\n", ntohs (cli.sin_port));
  47. if (-1 == conn_fd) {
  48. perror ("accept");
  49. return 3;
  50. }
  51. /* 6. 读取客户端发送过来的信息 */
  52. int r = read (conn_fd, buf, sizeof (buf)-1);
  53. /* 7. 数据处理/显示 */
  54. write (1, buf, r);
  55. // write (1, "\n", 2);
  56. memset (buf, 0, sizeof (buf));
  57. /* 8. 给客户端响应 */
  58. fgets (buf1, sizeof (buf1), stdin);
  59. write (conn_fd, buf1, sizeof (buf1));
  60. memset (buf1, 0, sizeof (buf1));
  61. }
  62. /* 9. 连接结束,关闭连接 */
  63. close (conn_fd);
  64. return 0;
  65. }

【客户端编程模型】
/** 1.<1> 创建socket通讯端 **/
同服务器端函数。
/** 1.<2> 使用socket通讯端连接服务器 **/
同服务器端函数。
/** 1.<3> 连接成功,通过socket通讯端向服务器发送数据,或从服务器获取数据 **/
"connect"(2)
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, \
socklen_t addrlen);
功能:将sockfd连接到addr指定的地址空间
参数:
"sockfd" socket(2)的返回值
"addr" 要连接到的目的地址(struct sockaddr通讯对类型的)
"addrlen" addr的长度
返回值:
成功 - 返回 0 (连接或绑定成功)
失败 - 返回 -1,errno被设置

  1. "client"
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <sys/socket.h>
  5. #include <netinet/in.h>
  6. #include <unistd.h>
  7. #include <arpa/inet.h>
  8. #include <string.h>
  9. typedef struct sockaddr SA;
  10. typedef struct sockaddr_in SA4;
  11. int main (int argc, char *argv[])
  12. {
  13. SA4 server;
  14. int s_fd;
  15. int ret;
  16. char buf[1024] = {0}, buf1[1024] = {0};
  17. /* 1. 创建通讯端 */
  18. s_fd = socket (AF_INET, SOCK_STREAM, 0);
  19. if (-1 == s_fd) {
  20. perror ("socket");
  21. return 1;
  22. }
  23. /* 2. 初始化服务器端信息 */
  24. server.sin_family = AF_INET; // ipv4
  25. server.sin_port = htons (7778); // port
  26. inet_pton (AF_INET, argv[1], &server.sin_addr); // copy ip 到服务器
  27. /* 3. 将s_fd和目的地址连接 */
  28. ret = connect (s_fd, (SA*)&server, sizeof (server));
  29. if (-1 == ret) {
  30. perror ("connect");
  31. return 2;
  32. }
  33. while (1) {
  34. /* 4. 向服务器发送数据 */
  35. fgets (buf, sizeof (buf), stdin);
  36. write (s_fd, buf, sizeof (buf));
  37. memset (buf, 0, sizeof (buf));
  38. /* 5. 等待获取服务器响应信息 */
  39. int r = read (s_fd, buf1, sizeof (buf1));
  40. /* 6. 将服务器的响应信息输出到显示器 */
  41. write (1, buf1, r); // 1==STDOUT
  42. // write (1, "\n", 2);
  43. memset (buf1, 0, sizeof (buf1));
  44. }
  45. /* 7. 关闭连接描述符 */
  46. close (s_fd);
  47. return 0;
  48. }

"127.0.0.1" 客户机连接本机服务器的本地环回地址。

三、"实现服务器的并发":使用多进程实现服务器的并发。
在accept之后,调用fork函数产生子进程:
"父进程"关闭连接描述符(关闭conn_fd),继续监听socket是否有客户端连接的到来。
"子进程"关闭socket连接(关闭s_fd),负责和客户端的通讯。
  1. "server - 支持多用户并发"
  2. /** 代码如下 server.c **/
  3. #include <stdio.h>
  4. #include <sys/types.h>
  5. #include <sys/socket.h>
  6. #include <netinet/in.h>
  7. #include <unistd.h>
  8. #include <arpa/inet.h>
  9. #include <ctype.h>
  10. #include <string.h>
  11. typedef struct sockaddr SA;
  12. typedef struct sockaddr_in SA4;
  13. int main(void) { /** 新增变量cli **/
  14. SA4 serv, cli; //定义服务器的地址变量
  15. int s_fd, conn_fd;
  16. int ret;
  17. char buf[128] = {0};
  18. char ip[128] = {0}; /** 存放ip地址字符串 **/
  19. int port; /** 存放端口号 **/
  20. //<1> 创建socket通讯端
  21. s_fd = socket(AF_INET, SOCK_STREAM, 0);
  22. if(-1 == s_fd) {
  23. perror("socket");
  24. return 1;
  25. }
  26. //补充:初始化服务器的信息
  27. serv.sin_family = AF_INET;
  28. serv.sin_port = htons(7777); //端口5000以下留给系,可选5000以上
  29. serv.sin_addr.s_addr = htonl(INADDR_ANY);//长整型主机号(ip)
  30. //<2> 将服务器的IP地址和端口号与s_fd绑定
  31. ret = bind(s_fd, (SA *)&serv, sizeof(SA4)); //SA4或serv
  32. if(-1 == ret) {
  33. perror("bind");
  34. return 2;
  35. }
  36. //<3> 监听s_fd
  37. listen(s_fd, 5);
  38. //<4> 阻塞等待客户端的连接
  39. while(1) {
  40. /** 将客户端的socket pairs保存在了cli指向的地址里 **/
  41. int cli_len = sizeof(cli);
  42. conn_fd = accept(s_fd, (SA *)&cli, &cli_len);
  43. if(-1 == conn_fd) {
  44. perror("accept");
  45. return 3;
  46. }
  47. pid_t pid = fork();
  48. if(pid < 0) {
  49. perror("fork");
  50. return 4;
  51. }
  52. if(pid > 0) { /** 父进程:负责监听客户端的连接 **/
  53. close(conn_fd);
  54. continue;
  55. } else { /** 子进程:负责和客户端通信 **/
  56. close(s_fd); /** 取消关闭s_fd就不会报accept **/
  57. /** 将二进制ip转换为字符串ip地址打印 **/
  58. char *p = (char *)inet_ntop(AF_INET, &cli.sin_addr, ip, 128);
  59. printf("客户端ip:%s\n", p);
  60. printf("用户姓名:");
  61. if(!strcmp(p, "176.135.11.138")) {
  62. printf("唐发洪\n");
  63. }
  64. /** 从cli结构体中打印端口号 **/
  65. printf("端口号:%d\n", ntohs(cli.sin_port));
  66. //<5> 读取客户端发送过来的信息
  67. int r = read(conn_fd, buf, 127);
  68. //<6> 加工处理信息
  69. for(int i = 0; i < r; i++) {
  70. buf[i] = toupper(buf[i]);
  71. }
  72. //给客户端响应
  73. write(conn_fd, buf, r);
  74. printf("接收到信息:%s\n", buf);
  75. //<7> 和这个客户端业务结束,关闭连接
  76. sleep(10);
  77. exit(0); //关闭子进程
  78. close(conn_fd);
  79. }
  80. }
  81. return 0;
  82. }

"select"(2) /** 待扩充,nigix使用的就是此多进程方式 **/

四、基于UDP的通讯
"UDP和TCP的区别":
UDP是面向数据包的,而TCP是面向数据流的。
TCP是面向连接的,相对与UDP传输比较"安全,不会丢包"。(如:QQ传文件)
UDP的"传输效率比较高",但是安全性比较差,"容易丢包"。(如:QQ视频)


1. 基于UDP的服务器端模型
<1> 创建socket通讯端
<2> 绑定socket描述符和服务器的socket pairs
<3> recvfrom等待客户端数据的到来
<4> 处理数据
<5> sendto响应客户端


2. 基于TCP的客户端模型
<1> 创建socket通讯端
<2> sendto发送信息给服务器端
<3> recvfrom等待服务器端的响应信息
<4> 处理数据
<5> 关闭socket通讯端-连接描述符(close(参数);)


/** 1.<3> recvfrom等待客户端数据的到来 **/
"recvfrom"(2) "其他的相关函数自行扩充"
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                struct sockaddr *src_addr, socklen_t *addrlen);
功能:从socket接收消息
参数:
"sockfd" socket(2)的返回值
"buf" 接收消息的内存空间地址
"len" 接收消息的长度
"flags" 0
"src_addr" 源的socket pairs;如果为 NULL,addrlen也就为 NULL
"addrlen" src_addr的长度
返回值:
成功 - 返回接收到的字节数
失败 - 返回 -1,errno被设置


/** 1.<5> sendto响应客户端 **/
"sendto"(2)
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
              const struct sockaddr *dest_addr, socklen_t addrlen);
功能:通过socket发送消息
参数:
"sockfd" socket(2)的返回值
"buf" 将buf指定的内存空间里的数据发送出去
"len" buf的长度
"flags" 0
"dest_addr" 目标地址
"addrlen" 目标地址的长度
返回值:
成功 - 返回成功发送的字节数
失败 - 返回 -1,errno被设置


  1. /** 举例验证:
  2. 编写程序,实现UDP的通讯,客户端发送字符串,服务器端将客户端的发送过来的字符串转换为大写,响应给客户端。客户端收到服务器端的响应,将响应信息输出到屏幕上。
  3. 服务器端 userver.c
  4. 客户端 uclient.c **/
  5. "userver.c"
  6. #include <stdio.h>
  7. #include <sys/types.h>
  8. #include <sys/socket.h>
  9. #include <netinet/in.h>
  10. #include <unistd.h>
  11. #include <ctype.h>
  12. typedef struct sockaddr SA;
  13. typedef struct sockaddr_in SA4;
  14. int main(void) {
  15. int s_fd;
  16. SA4 server, client;
  17. char buf[128] = {0};
  18. //<1> 创建socket
  19. s_fd = socket(AF_INET, SOCK_DGRAM, 0);
  20. if(-1 == s_fd) {
  21. perror("socket");
  22. return 1;
  23. }
  24. //初始化server
  25. server.sin_family = AF_INET;
  26. server.sin_port = htons(7779);
  27. server.sin_addr.s_addr = htonl(INADDR_ANY);
  28. //<2> 绑定s_fd和服务器的socket pair
  29. int b = bind(s_fd, (SA *)&server, sizeof(server));
  30. if(-1 == b) {
  31. perror("bind");
  32. return 2;
  33. }
  34. while(1) {
  35. //<3> 接收数据
  36. int cli_len = sizeof(client);
  37. int r = recvfrom(s_fd, buf, 128, 0, (SA *)&client, &cli_len);
  38. printf("接收到信息:%s\n", buf);
  39. //<4> 数据处理
  40. for(int i = 0; i < r; i++) {
  41. buf[i] = toupper(buf[i]);
  42. }
  43. //<5> 发送给客户端
  44. sendto(s_fd, buf, r, 0, (SA *)&client, sizeof(client));
  45. printf("发出的信息:%s\n", buf);
  46. }
  47. close(s_fd);
  48. return 0;
  49. }

  1. "uclient.c"
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <strings.h>
  5. #include <netinet/in.h>
  6. #include <unistd.h>
  7. #include <sys/types.h>
  8. #include <sys/socket.h>
  9. int main(void) {
  10. int s_fd;
  11. char buf[20] = "hello,world!";
  12. //<1> 创建socket通讯端
  13. s_fd = socket(AF_INET, SOCK_DGRAM, 0);
  14. if(-1 == s_fd) {
  15. perror("socket");
  16. return 1;
  17. }
  18. struct sockaddr_in server;
  19. //初始化server的信息
  20. server.sin_family = AF_INET;
  21. server.sin_port = htons(7779);
  22. inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
  23. //<2> 发送信息给服务器
  24. sendto(s_fd, buf, strlen(buf)+1, 0, (struct sockaddr *)&server, sizeof(server));
  25. //<3> 从服务器端等待响应信息
  26. bzero(buf, 20);
  27. int r = recvfrom(s_fd, buf, 20, 0, NULL, NULL);
  28. //<4> 处理数据
  29. write(1, buf, r);
  30. write(1, "\n", 2);
  31. close(s_fd);
  32. return 0;
  33. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/158766
推荐阅读
相关标签
  

闽ICP备14008679号