Linux-C网络编程
简介
基础是TCP/IP协议,网上资料很多不再赘述。
推荐《图解TCP/IP》
socket编程
网络字节序
发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,
接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存
因此,网络数据流的地址规定:先发出的数据是低地址,后发出的数据是高地址
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
例如UDP段格式,地址0-1是16位的源端口号,如果这个端口号是1000(0x3e8)
则地址0是0x03,地址1是0xe8,也就是先发0x03,再发0xe8
这16位在发送主机的缓冲区中也应该是低地址存0x03,高地址存0xe8。
但是,如果发送主机是小端字节序的,这16位被解释成0xe803,而不是1000。
因此,发送主机把1000填到发送缓冲区之前需要做字节序的转换。
同样地,接收主机如果是小端字节序的,接到16位的源端口号也要做字节序的转换。
如果主机是大端字节序的,发送和接收都不需要做转换。
同理,32位的IP地址也要考虑网络字节序和主机字节序的问题。
网络字节序转换
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行
可以调用以下库函数做网络字节序和主机字节序的转换
#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表示network,l表示32位长整数,s表示16位短整数;
htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回;
IP地址格式转换
通常用户在表达地址时采用的是点分十进制表示的数值(或者是为冒号分开的十进制Ipv6地址)
而在通常使用的socket编程中使用的则是32位的网络字节序的二进制值,这就需要将这两个数值进行转换。
这里在 Ipv4 中用到的函数有inet_aton()
、inet_addr()
和inet_ntoa()
而 IPV4 和 Ipv6 兼容的函数有inet_pton()
和inet_ntop()
。
IPv4的函数原型:
#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int inet_aton(const char *straddr, struct in_addr *addrptr); char *inet_ntoa(struct in_addr inaddr); in_addr_t inet_addr(const char *straddr);
例子:函数inet_aton():将点分十进制数的IP地址转换成为网络字节序的32位二进制数值
- 返回值:成功,则返回1,不成功返回0
- 参数straddr:存放输入的点分十进制数IP地址字符串
- 参数addrptr:传出参数,保存网络字节序的32位二进制数值
函数inet_ntoa():将网络字节序的32位二进制数值转换为点分十进制的IP地址
- 函数inet_addr():功能与inet_aton相同,但是结果传递的方式不同。
- inet_addr()若成功则返回32位二进制的网络字节序地址
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> int main()
{
char ip[] = "192.168.0.101";
struct in_addr myaddr; /* inet_aton 32字节序*/
int iRet = inet_aton(ip, &myaddr);
printf("%x\n", myaddr.s_addr); /* inet_addr 32字节序*/
printf("%x\n", inet_addr(ip)); /* inet_pton 32字节序*/
iRet = inet_pton(AF_INET, ip, &myaddr);
printf("%x\n", myaddr.s_addr);
myaddr.s_addr = 0xac100ac4; /* inet_ntoa 输出点分十进制IP*/
printf("%s\n", inet_ntoa(myaddr)); /* inet_ntop 输出点分十进制IP*/
inet_ntop(AF_INET, &myaddr, ip, );
puts(ip);
return ;
}
域名和IP地址转换
Linux中,gethostbyname()是将主机名转化为IP地址,gethostbyaddr()则是逆操作,是将IP地址转化为主机名。
socket地址的数据类型及相关函
socket API是一层抽象的网络编程接口,适用于各种底层网络协议
如IPv4、IPv6,UNIX Domain Socket。
基于TCP协议的网络程序
TCP程序通信流程图
服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,
处于监听端口的状态,客户端调用socket()初始化后,调用connect()发出SYN段并阻塞等待服务器应答,
服务器应答一个SYN-ACK段,客户端收到后从connect()返回
同时应答一个ACK段,服务器收到后从accept()返回
TCP程序实例
/*server.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h> #define MYPORT 8887
#define QUEUE 20
#define BUFFER_SIZE 1024 int main()
{
///定义sockfd
int server_sockfd = socket(AF_INET,SOCK_STREAM, ); //定义sockaddr_in
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(MYPORT);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY); ///bind,成功返回0,出错返回-1
if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-)
{
perror("bind");
exit();
} printf("监听%d端口\n",MYPORT);
///listen,成功返回0,出错返回-1
if(listen(server_sockfd,QUEUE) == -)
{
perror("listen");
exit();
} ///客户端套接字
char buffer[BUFFER_SIZE];
struct sockaddr_in client_addr;
socklen_t length = sizeof(client_addr); printf("等待客户端连接\n");
///成功返回非负描述字,出错返回-1
int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);
if(conn<)
{
perror("connect");
exit();
}
printf("客户端成功连接\n"); while()
{
memset(buffer,,sizeof(buffer));
int len = recv(conn, buffer, sizeof(buffer),);
//客户端发送exit或者异常结束时,退出
if(strcmp(buffer,"exit\n")== || len<=)
break;
printf("来自客户端数据:%s\n",buffer);
send(conn, buffer, len, );
printf("发送给客户端数据:%s\n",buffer);
}
close(conn);
close(server_sockfd);
return ;
}
基于UDP的网络程序
/*client.c*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/shm.h> #define MYPORT 8887
#define BUFFER_SIZE 1024
char* SERVER_IP = "127.0.0.1"; int main()
{
///定义sockfd
int sock_cli = socket(AF_INET,SOCK_STREAM, ); ///定义sockaddr_in
struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(MYPORT); ///服务器端口
servaddr.sin_addr.s_addr = inet_addr(SERVER_IP); ///服务器ip printf("连接%s:%d\n",SERVER_IP,MYPORT);
///连接服务器,成功返回0,错误返回-1
if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < )
{
perror("connect");
exit();
}
printf("服务器连接成功\n");
char sendbuf[BUFFER_SIZE];
char recvbuf[BUFFER_SIZE];
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
printf("向服务器发送数据:%s\n",sendbuf);
send(sock_cli, sendbuf, strlen(sendbuf),); ///发送
if(strcmp(sendbuf,"exit\n")==)
break;
recv(sock_cli, recvbuf, sizeof(recvbuf),); ///接收
printf("从服务器接收数据:%s\n",recvbuf); memset(sendbuf, , sizeof(sendbuf));
memset(recvbuf, , sizeof(recvbuf));
} close(sock_cli);
return ; }
UDP通信流程图
UDP例程
/*server.c*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h> #define MYPORT 8887 #define ERR_EXIT(m) \
do { \
perror(m); \
exit(EXIT_FAILURE); \
} while () void echo_ser(int sock)
{
char recvbuf[] = {};
struct sockaddr_in peeraddr;
socklen_t peerlen;
int n; while ()
{ peerlen = sizeof(peeraddr);
memset(recvbuf, , sizeof(recvbuf));
n = recvfrom(sock, recvbuf, sizeof(recvbuf), ,
(struct sockaddr *)&peeraddr, &peerlen);
if (n <= )
{ if (errno == EINTR)
continue; ERR_EXIT("recvfrom error");
}
else if(n > )
{
printf("接收到的数据:%s\n",recvbuf);
sendto(sock, recvbuf, n, ,
(struct sockaddr *)&peeraddr, peerlen);
printf("回送的数据:%s\n",recvbuf);
}
}
close(sock); } int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_DGRAM, )) < )
ERR_EXIT("socket error"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(MYPORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); printf("监听%d端口\n",MYPORT);
if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind error"); echo_ser(sock); return ; }
/*client.c*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define MYPORT 8887
char* SERVERIP = "127.0.0.1";
#define ERR_EXIT(m) \
do{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() void echo_cli(int sock)
{
struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(MYPORT);
servaddr.sin_addr.s_addr = inet_addr(SERVERIP); int ret;
char sendbuf[] = {};
char recvbuf[] = {};
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{ printf("向服务器发送:%s\n",sendbuf);
sendto(sock, sendbuf, strlen(sendbuf), , (struct sockaddr *)&servaddr, sizeof(servaddr)); ret = recvfrom(sock, recvbuf, sizeof(recvbuf), , NULL, NULL);
if (ret == -)
{
if (errno == EINTR)
continue;
ERR_EXIT("recvfrom");
}
printf("从服务器接收:%s\n",recvbuf); memset(sendbuf, , sizeof(sendbuf));
memset(recvbuf, , sizeof(recvbuf));
} close(sock); } int main(void)
{
int sock;
if ((sock = socket(PF_INET, SOCK_DGRAM, )) < )
ERR_EXIT("socket"); echo_cli(sock); return ; }
Linux-C网络编程的更多相关文章
- 嵌入式linux的网络编程(1)--TCP/IP协议概述
嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...
- Linux C网络编程学习笔记
Linux C网络编程总结报告 一.Linux C 网络编程知识介绍: 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端:(client) 在网络程序中, ...
- Linux C++ 网络编程学习系列(1)——端口复用实现
Linux C++ 网络编程学习系列(1)--端口复用实现 源码地址:https://github.com/whuwzp/linuxc/tree/master/portreuse 源码说明: serv ...
- Linux Socket 网络编程
Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后 ...
- 【LINUX/UNIX网络编程】之简单多线程服务器(多人群聊系统)
RT,Linux下使用c实现的多线程服务器.这个真是简单的不能再简单的了,有写的不好的地方,还希望大神轻拍.(>﹏<) 本学期Linux.unix网络编程的第四个作业. 先上实验要求: [ ...
- 【LINUX/UNIX网络编程】之使用消息队列,信号量和命名管道实现的多进程服务器(多人群聊系统)
RT,使用消息队列,信号量和命名管道实现的多人群聊系统. 本学期Linux.unix网络编程的第三个作业. 先上实验要求: 实验三 多进程服务器 [实验目的] 1.熟练掌握进程的创建与终止方法: 2 ...
- 【Linux/unix网络编程】之使用socket进行TCP编程
实验一 TCP数据发送与接收 [实验目的] 1.熟练掌握套接字函数的使用方法. 2.应用套接字函数完成基本TCP通讯,实现服务器与客户端的信息交互. [实验学时] 4学时 [实验内容] 实现一个服务器 ...
- Linux C 网络编程 - 获取本地 ip 地址,mac,通过域名获取对应的 ip
获取本地 ip 地址,mac,通过域名获取对应的 ip, 是网络编程可能遇到的比较常见的操作了,所以总结如下(封装了3个函数), 直接上代码: #include <stdio.h> #in ...
- LINUX socket网络编程
1. 网络中进程之间如何通信 进 程通信的概念最初来源于单机系统.由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进 程之间既互不干扰又协调一致工作,操作系统为进程通信提供了相应设施,如 U ...
- linux中网络编程<1>
1 网络编程API (1)网络层的ip地址可以唯一标识网络中的主机,传输层通过协议+端口唯一标识主机中的应用程序.这样以来使用三元组(地址,协议,端口)标识网络的进程. (2)socket---> ...
随机推荐
- js判断是否为空
http://dushanggaolou.iteye.com/blog/1293803 1.<input type="hidden" id="key" n ...
- eos节点启动源码分析
在eos源码目录中programs/nodeos/main.cpp文件里,为节点启动的主函数main函数内部做了两件事1 初始化 application if(!app().initialize< ...
- SQL优化:索引的重要性
开篇小测验 下面这样一个小SQL 你该怎么样添加最优索引 两个表上现在只有聚集索引 bigproduct 表上已经有聚集索引 ProductID bigtransactionhistory 表上已经有 ...
- java基础基础总结----- Math(随机数)
前言:math类中感觉最好玩的应该就是随机数 代码: package com.day13.math; import java.util.Random; /** * 类说明 :Math * @autho ...
- Java基础-Java数据类型
Java基础-Java数据类型 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.数据类型的作用 数据类型就是一组值,以及这一组值上的操作,数据类型可以决定数据的存储方式,取值范围 ...
- python操作txt文件中数据教程[1]-使用python读写txt文件
python操作txt文件中数据教程[1]-使用python读写txt文件 觉得有用的话,欢迎一起讨论相互学习~Follow Me 原始txt文件 程序实现后结果 程序实现 filename = '. ...
- My latest news
2018.04.12 0:01 本站点停止更新,启用0x7c00.vip站点. 2018.03.23 复试报道(心态不太平稳).每一行的深入都是需要知识的积累和时间的沉淀,就像学法律.计算机等等.愿 ...
- 何凯文每日一句||DAY10
- Django Book 学习笔记(上)
拜读了网上的Django Book,现在来总结一下吧...... 一.Django的配置 非常的蛋疼,由于Django的块组之间耦合度低,这既是它的优点,也是它的缺点.我在Ubuntu所配置的Djan ...
- vue路由DEMO
index.js,index.vue,course.vue,master.vue等 import Vue from 'vue' import Router from 'vue-router' impo ...