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---> ...
随机推荐
- 高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少
高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少 阅读(81374) | 评论(9)收藏16 淘帖1 赞3 JackJiang Lv.9 1 年前 | 前言 曾几何时我 ...
- 【CSS】定位层
html:定位层1.概念: >>.定位层是由html元素(标签)形成的一个特殊的box盒子. >>.其重点在于“定位”,而html元素(标签)的定位方式由CSS来控制. 通常情 ...
- 文件通过svn updata更新不到,并且svn st显示被删除的解决办法
不知道什么原因导致某些文件丢失,svn updata更新后仍然没有找到,采用svn st 显示这些文件被删除,svn reslove 也解决不了,头疼了很久,最近突然解决了,具体步骤如下(已经过验证) ...
- 百度钱包、百付宝、baifubao接入支付的常见问题
[5004:参数非法,请检查输入参数后重试.]:检查是否缺少了其它必要的参数,我当时是缺少了order_no [5804,抱歉,订单创建失败,请联系客服处理]:即验证签名失败,这个只能参考文档进行处理 ...
- SSM数据库数据导出excel
首先,这是我对自己的需求而使用的逻辑,若有可以完美的地方方便告诉下小白. apache的poi MAVEN <dependency> <groupId>org.apache.p ...
- Bugfree——CentOS6.8搭建测试环境
参考资料:http://blog.csdn.net/qq_29227939/article/details/52295917 BugFree基于PHP和MySQL开发,是免费且开发源代码的缺陷管理系统 ...
- Nginx模块之Nginx-Ts-Module学习笔记(一)抢险体验
1.通过HTTP接收MPEG-TS2.生产和管理Live HLS 3.按照官方的编译和配置,当然了我是第一次编译没有通过,在作者重新调整下,编译成功,感谢:@arut https://github.c ...
- Parquet 格式文件
Apache Parquet是Hadoop生态圈中一种新型列式存储格式,它可以兼容Hadoop生态圈中大多数计算框架(Hadoop.Spark等),被多种查询引擎支持(Hive.Impala.Dril ...
- Lessons Learned from Developing a Data Product
Lessons Learned from Developing a Data Product For an assignment I was asked to develop a visual ‘da ...
- java Concurrent 中的数据结构
一:阻塞数据结构(线程安全) ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列. LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列. PriorityBl ...