计算机网络概述

Linux系统也与网络密不可分,无论是服务器开发,还是嵌入式应用等领域,都需要通过网络进行数据传递。Linux网络编程一般通过socket(套接字)接口实现。

关于网络的概述我就不细讲了,看了晕头转向的,在此推荐这个老哥的博文
1-1:网络初识之了解什么是协议以及TCP/IP协议
1-2:网络初识之了解OSI和TCP/IP及网络分层(物理层,数据链路层,网路层,传输层,应用层)
1-3:网络初识之网络传输的基本流程TCP首部,IP首部,MAC地址,IP地址等

补充几点:
网络结构模式:C/S B/S
网络结构模式分为两种,一种为客户机(client)/服务器(server)模式,即C/S模式,此种模式需要在进行通信的两端分别架设客户机和服务器;另一种为浏览器(browser)/服务器模式,即B/S模式,是WEB(World Wide Web)兴起后的一种网络结构模式,客户机只需安装浏览器,便可与服务器进行交互。

socket编程基础

在Linux系统中,socket可用于表示进程间进行网络通信时使用的特殊文件类型,也可用于表示socket编程中的一系列接口。socket本意为“插座”,常被称为套接字。当使用socket进行通信时,进程会先生成一个socket文件,之后再通过socket文件进行数据传递。

Linux系统中将socket具体化为一种文件只是为了给用户提供与操作普通文件相同的接口,使用户可以通过文件描述符来引用和操作套接字。实际上,socket的本质为内存缓冲区形成的伪文件,与管道本质类似,不同的是,socket多用于与网络相关的进程通信。


在网络通信中,socket一定是成对出现的。socket的缓冲区分为读写两个部分,每个socket都能接收和发送文件,一端的发送缓冲区会对应另一端的接收缓冲区。

对用户来说,不必了解socket文件的具体构成,只需掌握与socket相关的接口即可。socket接口位于应用层与TCP/IP协议族之间,是基于软件的抽象层,它与体系结构中各层的关系如图10-5所示。

socket编程接口

Linux系统中常用的socket网络编程接口有socket()、bind()、listen()、accept()、connect()、send()、recv()、close(),其中connect()与send()为客户端专用接口bind()、listen()、accept()及recv()为服务器端专用接口socket()与close()则由服务器与客户端共用。

socket()




bind()


如亦可使用以下语句,定义一个struct sockaddr_in类型的结构体:

struct sockaddr_in servaddr;         //结构体定义
bzero(&servaddr, sizeof(servaddr));     //结构体清零
servaddr.sin_family = AF_INET;         //设置地址类型为AF_INET
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //设置网络地址为INADDR_ANY
servaddr.sin_port = htons(85);         //设置端口号为85

listen()



accept()

connect()

send()



ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);



recv()

close()

socket 通信流程

根据进程在网络通信中使用的协议,可将socket通信方式分为两种:一种是面向连接、基于TCP协议的通信;另一种是面向无连接、基于UDP协议的通信。

网络编程相关知识

网络字节序—大小端

Linux系统中提供了一些用于字节序转换的函数,这些函数存在于函数库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);

ip转换函数



sockaddr数据结构


更加详细看:2-1:套接字(Socket)编程之必备知识

socket 网络通信实例

C/S TCP



服务端程序:tcpserver.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define MAXLINE 80                          //最大数据长度
#define SERV_PORT 6666                      //服务器端口号
int main(void)
{struct sockaddr_in servaddr, cliaddr;  //定义服务器与客户端地址结构体socklen_t cliaddr_len;                  //客户端地址长度int listenfd, connfd;char buf[MAXLINE];char str[INET_ADDRSTRLEN];int i, n;//创建服务器端套接字文件listenfd = socket(AF_INET, SOCK_STREAM, 0);//初始化服务器端口地址bzero(&servaddr, sizeof(servaddr)); //将服务器端口地址清零servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT);//将套接字文件与服务器端口地址绑定bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//监听,并设置最大连接数为20listen(listenfd, 20);printf("Accepting connections ...\n");//接收客户端数据,并处理请求while (1) {cliaddr_len = sizeof(cliaddr);connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);n = recv(connfd, buf, MAXLINE, 0);printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));for (i = 0; i < n; i++)buf[i] = toupper(buf[i]);send(connfd, buf, n, 0);//关闭连接close(connfd);}return 0;
}

tcpclient.c 客户端程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXLINE 80
#define SERV_PORT 6666
int main(int argc, char *argv[])
{struct sockaddr_in servaddr;       //定义服务器地址结构体char buf[MAXLINE];int sockfd, n;char *str;if (argc != 2) {fputs("usage: ./client message\n", stderr);exit(1);}str = argv[1];//创建客户端套接字文件sockfd = socket(AF_INET, SOCK_STREAM, 0);//初始化服务器端口地址bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(SERV_PORT);//请求链接connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//发送数据send(sockfd, str, strlen(str), 0);//接收客户端返回的数据n = recv(sockfd, buf, MAXLINE, 0);printf("Response from server:\n");//将客户端返回的数据打印到终端write(STDOUT_FILENO, buf, n);//关闭连接close(sockfd);return 0;
}

C/S UDP



udpserver.c //服务器端

#include <string.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <arpa/inet.h>
#include <ctype.h>
#define MAXLINE 80                          //最大数据长度
#define SERV_PORT 6666                      //服务器端口号
int main(void)
{struct sockaddr_in servaddr, cliaddr;  //定义服务器与客户端地址结构体socklen_t cliaddr_len;                  //客户端地址长度int sockfd;                                //服务器socket文件描述符char buf[MAXLINE];char str[INET_ADDRSTRLEN];int i, n;sockfd = socket(AF_INET, SOCK_DGRAM, 0);//创建服务器端套接字文件//初始化服务器端口地址bzero(&servaddr, sizeof(servaddr));  //地址结构体清零servaddr.sin_family = AF_INET;            //指定协议族servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(SERV_PORT); //指定端口号//绑定服务器端口地址bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));printf("Accepting connections ...\n");//数据传输while (1) {cliaddr_len = sizeof(cliaddr);//接收数据n = recvfrom(sockfd, buf, MAXLINE, 0, (struct sockaddr*)&cliaddr,&cliaddr_len);if (n == -1)perror("recvfrom error");printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),ntohs(cliaddr.sin_port));//服务器端操作,小写转大写for (i = 0; i < n; i++)buf[i] = toupper(buf[i]);n = sendto(sockfd, buf, n, 0, (struct sockaddr *)&cliaddr,sizeof(cliaddr));if (n == -1)perror("sendto error");}close(sockfd);return 0;
}

udpclient.c //客户端

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include <ctype.h>
#define MAXLINE 80
#define SERV_PORT 6666
int main(int argc, char *argv[])
{struct sockaddr_in servaddr;int sockfd, n;char buf[MAXLINE];sockfd = socket(AF_INET, SOCK_DGRAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;inet_pton(AF_INET, "127.0.0.1", &servaddr.sin_addr);servaddr.sin_port = htons(SERV_PORT);//发送数据到客户端while (fgets(buf, MAXLINE, stdin) != NULL) {n = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&servaddr,sizeof(servaddr));if (n == -1)perror("sendto error");//接收客户端返回的数据n = recvfrom(sockfd, buf, MAXLINE, 0, NULL, 0);if (n == -1)perror("recvfrom error");//将接收到的数据打印到终端send(STDOUT_FILENO, buf, n, 0);}close(sockfd);return 0;
}

2-3:套接字(Socket)编程之UDP通信

2-4:套接字(Socket)编程之TCP通信

socket本地通信





服务器端 dmserve.c

#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#define QLEN 10
//创建服务器进程,成功返回0,出错返回小于0的errno
int serv_listen(const char *name)
{int fd, len, err, rval;struct sockaddr_un un;//创建本地domain套接字if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)return(-1);//删除套接字文件,避免因文件存在导致bind()绑定失败 unlink(name);//初始化套接字结构体地址memset(&un, 0, sizeof(un));un.sun_family = AF_UNIX;strcpy(un.sun_path, name);len = offsetof(struct sockaddr_un, sun_path) + strlen(name);if (bind(fd, (struct sockaddr *)&un, len) < 0) {rval = -2;goto errout;}if (listen(fd, QLEN) < 0) { //告知内核这是一个服务器进程rval = -3;goto errout;}return(fd);
errout:err = errno;close(fd);errno = err;return(rval);
}
int serv_accept(int listenfd, uid_t *uidptr)
{int clifd, len, err, rval;time_t staletime;struct sockaddr_un un;struct stat statbuf;len = sizeof(un);if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0)return(-1);//从调用地址获取客户端的uidlen -= offsetof(struct sockaddr_un, sun_path); //获取路径名长度un.sun_path[len] = 0;     //为路径名字符串添加终止符if (stat(un.sun_path, &statbuf) < 0) {rval = -2;goto errout;}if (S_ISSOCK(statbuf.st_mode) == 0) {rval = -3;               //若返回值为-3,说明这不是一个socket文件goto errout;}if (uidptr != NULL)*uidptr = statbuf.st_uid;         //返回uid的调用者指针//到此成功获取路径名unlink(un.sun_path);return(clifd);
errout:err = errno;close(clifd);errno = err;return(rval);
}
int main(void)
{int lfd, cfd, n, i;uid_t cuid;char buf[1024];lfd = serv_listen("foo.socket");if (lfd < 0) {switch (lfd) {case -3:perror("listen"); break;case -2:perror("bind"); break;case -1:perror("socket"); break;}exit(-1);}cfd = serv_accept(lfd, &cuid);if (cfd < 0) {switch (cfd) {case -3:perror("not a socket"); break;case -2:perror("a bad filename"); break;case -1:perror("accept"); break;}exit(-1);}while (1) {r_again:n = read(cfd, buf, 1024);if (n == -1) {if (errno == EINTR)goto r_again;}else if (n == 0) {printf("the other side has been closed.\n");break;}for (i = 0; i < n; i++)buf[i] = toupper(buf[i]);write(cfd, buf, n);}close(cfd);close(lfd);return 0;
}

客户端 dmclient.c

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#define CLI_PATH "/var/tmp/" /* +5 for pid = 14 chars */
//创建客户端进程,成功返回0,出错返回小于0的errno
int cli_conn(const char *name)
{int fd, len, err, rval;struct sockaddr_un un;//创建本地套接字domainif ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)return(-1);//使用自定义地址填充socket地址结构体memset(&un, 0, sizeof(un));un.sun_family = AF_UNIX;sprintf(un.sun_path, "%s%05d", CLI_PATH, getpid());len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);unlink(un.sun_path); //避免因文件已存在导致的bind()失败if (bind(fd, (struct sockaddr *)&un, len) < 0) {rval = -2;goto errout;}//使用服务器进程地址填充socket地址结构体memset(&un, 0, sizeof(un));un.sun_family = AF_UNIX;strcpy(un.sun_path, name);len = offsetof(struct sockaddr_un, sun_path) + strlen(name);if (connect(fd, (struct sockaddr *)&un, len) < 0) {rval = -4;goto errout;}return(fd);
errout:err = errno;close(fd);errno = err;return(rval);
}
int main(void)
{int fd, n;char buf[1024];fd = cli_conn("foo.socket");       //套接字文件为foo.socketif (fd < 0) {                      //容错处理switch (fd) {case -4:perror("connect"); break;case -3:perror("listen"); break;case -2:perror("bind"); break;case -1:perror("socket"); break;}exit(-1);}while (fgets(buf, sizeof(buf), stdin) != NULL) {write(fd, buf, strlen(buf));n = read(fd, buf, sizeof(buf));write(STDOUT_FILENO, buf, n);}close(fd);return 0;
}

脚下留心:出错处理函数封装


面将对网络编程中的常用接read()write()等函数进行再次封装,为其添加出错处理功能,并将这些新函数的声明和定义分别保存在文件wrap.h 和 wrap.c

#ifndef __WRAP_H_
#define __WRAP_H_
void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void Connect(int fd, const struct sockaddr *sa, socklen_t salen);
void Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
void Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
static ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);
#endif

#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>
//socket()
int Socket(int family, int type, int protocol)
{int n;if ((n = socket(family, type, protocol)) < 0)//若socket调用失败perr_exit("socket error");return n;
}
//bind()
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{if (bind(fd, sa, salen) < 0)                //若bind()调用失败perr_exit("bind error");
}
//listen()
void Listen(int fd, int backlog)
{if (listen(fd, backlog) < 0)                //若listen()调用失败perr_exit("listen error");
}
//connect()
void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{if (connect(fd, sa, salen) < 0)             //若connect()调用失败perr_exit("connect error");
}
//accept()
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{int n;
again:if ((n = accept(fd, sa, salenptr)) < 0) { //若accept()调用失败if ((errno == ECONNABORTED) || (errno == EINTR))goto again;elseperr_exit("accept error");}return n;
}
//close()
void Close(int fd)
{if (close(fd) == -1)                     //关闭socket失败perr_exit("close error");
}
//read()
ssize_t Read(int fd, void *ptr, size_t nbytes)
{ssize_t n;
again:if ((n = read(fd, ptr, nbytes)) == -1) {   //读取数据失败if (errno == EINTR)goto again;                            //重读fdelsereturn -1;}return n;
}
//write()
ssize_t Write(int fd, const void *ptr, size_t nbytes)
{ssize_t n;
again:if ((n = write(fd, ptr, nbytes)) == -1) {  //写数据出错if (errno == EINTR)goto again;                         //重新写入elsereturn -1;}return n;
}
void perr_exit(const char *s)
{perror(s);exit(1);
}

linux---socket编程(网络)相关推荐

  1. Linux Socket编程

    IP socket 是在其上建立高级Internet 协议的最低级的层:从HTTP到SSL到POP3到Kerberos再到UDP-Time,每种Internet协议都建立在它的基础上.为了实现自定义的 ...

  2. 入门Linux系统编程--网络编程

    文章目录 一.网络编程 1.socket服务端代码实现(无连接客户端) 6.socket服务端代码实现(连接客户端) 7.socket客户端代码实现 8.实现双方聊天 9.多方消息收发 二.往期文章 ...

  3. linux socket 编程

    socket  <script type="text/javascript"> </script> <script type="text/j ...

  4. Windows Socket和Linux Socket编程的区别

    2019独角兽企业重金招聘Python工程师标准>>> 1.一些常用函数的移植 http://www.vckbase.com/document/viewdoc/?id=1586 2. ...

  5. Linux Socket编程入门——浅显易懂

    文章目录 1. 概述 2. Socket 3. 网络字节序 4. sockaddr 数据结构 5. 网络套接字API函数  5.1 socket()  5.2 bind()  5.3 listen() ...

  6. Linux应用编程网络编程

    常用Linux API和c标准库函数 常用[shell] stat :查看文件属性信息 chmod :权限修改(root用户可用) chown :属主修改 umask :设定我们系统中新创建的文件的默 ...

  7. Linux系统编程——网络编程

    文章目录 一.网络编程概述 1.网络通信即Socket编程介绍 (1)网络通信 ① 网络通信模式 ② OSI七层网络模型 (2)Socket编程 2.网络编程三要素 (1)协议 ①TCP传输协议 ②U ...

  8. Linux socket编程

    1.TCP/UDP对比 TCP面向连接(如打电话要先拨号建立连接) ;UDP是无连接的,即发送数据之前不需要建立连接 TCP提供可靠的服务.也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复, ...

  9. 【Linux Socket 编程入门】05 - 拉个骡子溜溜:TCP编程模型代码分析

    (一) 看看以前学了啥 前面介绍了socket的分类,IP地址,端口号(port),常用的socket数据结构以及常用的函数.现在我们来看一个例子,看看socket编程究竟是什么. (二) 一图看懂客 ...

  10. 基于TCP的socket编程网络掉线重连

    基于TCP的socket编程   sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW):基于TCP的socke ...

最新文章

  1. 这可能是最为详细的Docker入门吐血总结
  2. 处理异步利器 -- Redux-saga
  3. 《vSphere性能设计:性能密集场景下CPU、内存、存储及网络的最佳设计实践》一1.1.1 确定参数...
  4. potplayer 多个进程_操作系统 | 进程同步与进程互斥
  5. python网站设计开题报告_网站设计开题报告范文精选5篇
  6. Android笔记 android 7.0 动态申请权限
  7. (1)Linux进程调度
  8. 匿名对象方案与实体对象方案对比
  9. window 创建python虚拟环境
  10. 春运抢票靠加速包?试试这个 Python 开源项目吧
  11. MyDiskTest v2.98-U盘扩容检测工具
  12. rssi室内定位算法原理_基于RSSI的室内定位算法
  13. dell idrac 复位_重置DELL R630 iDRAC访问密码
  14. 论文笔记:主干网络——SENet
  15. 智能家居技术发展趋势及平台建设路径
  16. Java实习生常规技术面试题每日十题Java基础(七)
  17. 任务卡_05-数据库_-MySql 高级任务
  18. 处理器协同机制其二内存屏障与内存顺序(及Store Buffer与Invalidate Queue)
  19. 智慧家庭信息安全白皮书发布,推动智慧家庭叫好又叫座
  20. Angular实战项目(1)

热门文章

  1. antd-img-crop,使用 Ant Design Upload 时裁切图片
  2. 俺把所有粉丝显示在地图上啦~【详细教程+完整源码】
  3. [AHK]为通达信增加打开关闭交易窗口的功能热键
  4. 车间数字孪生解决方案(三)
  5. java手机游戏主角技能上剑魂,sa.java 源代码在线查看 - 手机游戏之剑魂。。j2me编写。。经典游戏只一 资源下载 虫虫电子下载站...
  6. java方法返回多个值,使用Pair、Triple
  7. 玩游戏性能最好的计算机,游戏性能最好的笔记本电脑有哪些
  8. python基础教程:Python如何基于selenium实现自动登录博客园
  9. 学习博客园开源代码笔记(登录页面)
  10. Linux路由器router,Linux路由器 Router 设定