Linux网络编程学习(十) ----- Socket(第六章)
前言:由于第五章主要介绍了TCP和UDP协议以及两者的包头的字段以及相应的功能,这里就不介绍了,对着字段看功能就好了,后续开始学习第六章
1、Socket
Socket实质上就是提供了通信的端点,每个socket都用一个半相关描述{协议,本地地址,本地端口},完整的socket描述{协议,本地地址,本地端口,远程地址,远程端口}
可以这样解释:套接字是通过标准的UNIX文件描述符和其他的程序通讯的一个方法。
2、Socket的三种类型
1)流式套接字(SOCK_STREAM)
提供可靠、面向连接的通讯流,流式套接字使用了TCP协议,保证了数据传输的正确性。
2)数据报套接字(SOCK_DGRAM)
定义了一种无连接的服务,数据通过相互独立的报文进行传输,无序且不保证可靠、无差错。使用的是UDP协议,为什么无连接?由于UPD不像TCP那样维护一个打开的连接,只需要把数据打包,把远程的IP贴上,然后把这个包发出去即可,不需要建立连接。
既然数据会丢失,那如何保证程序正常工作呢?其实每个使用UDP的程序都要有自己的对数据进行确认的协议,比如TFTP协议定义了每一个发出去的数据包,远程在接收到之后都要反馈一个数据告诉本地程序已经收到,如果在5秒内未得到回应,就重发ACK信号。
3)原始套接字
用于协议开发,可进行底层操作,一般不涉及
3、套接字的基本结构
sockaddr用来存储套接字地址
struct sockaddr
{
unsigned short sa_family; /* address族, AF_xxx */
char sa_data[14]; /* 14 bytes的协议地址 */
};
为了处理sockaddr,建立另一个结构sockaddr_in
/*因特网地址*/
struct in_addr
{
unsigned long s_addr;
};
struct sockaddr_in
{
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* Internet地址 */
unsigned char sin_zero[8]; /* 添0(和struct sockaddr一样大小)*/
};
有一点很重要,就是一个指向struct sockaddr_in的指针可以声明指向一个sturct sockaddr 的结构。所以虽然socket() 函数需要一个structaddr * ,你也可以给他一个sockaddr_in * 。注意在structsockaddr_in 中,sin_family 相当于在 struct sockaddr 中的sa_family,需要设成“AF_INET”。最后一定要保证sin_port 和sin_addr 必须是网络字节顺序。这是因为sin_addr和sin_port时从IP和UDP协议层取出来的数据,而在IP和UDP协议层,是直接和网络相关的,所以必须用网络字节序 ,而sin_famliy只是内核用来判断struct sockaddr_in是存储什么类型数据,并且sin_famliy也不会发送到网络上,可以用主机字节序保存。
4、常用的系统调用函数,如socket(),bind(),listen(),accept(),send(),recv()各自的参数可以具体参考手册,用man (send/socket)查看,下面给个流式套接字例子介绍一下,服务器端代码,功能就是给远程客户端发送字符串"Hello,World"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
/* 服务器要监听的本地端口 */
#define MYPORT 4000
/* 能够同时接受多少没有accept 的连接 */
#define BACKLOG 10
main()
{
/* 在sock_fd 上进行监听,new_fd 接受新的连接 */
int sock_fd, new_fd ;
/* 自己的地址信息 */
struct sockaddr_in my_addr;
/* 连接者的地址信息*/
struct sockaddr_in their_addr;
int sin_size;
/* 这里就是我们一直强调的错误检查.如果调用socket() 出错,则返回 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* 输出错误提示并退出 */
perror(“socket”);
exit(1);
}
/* 主机字节顺序 */
my_addr.sin_family = AF_INET;
/* 网络字节顺序,短整型 */
my_addr.sin_port = htons(MYPORT);
/* 将运行程序机器的IP 填充入s_addr */
my_addr.sin_addr.s_addr = INADDR_ANY;
/* 将此结构的其余空间清零 */
bzero(&(my_addr.sin_zero), 8);
/* 这里是我们一直强调的错误检查!! */
if (bind(sockfd, (struct sockaddr *)&my_addr,sizeof(struct sockaddr)) == -1)
{
/* 如果调用bind()失败,则给出错误提示,退出 */
perror(“bind”);
exit(1);
}
/* 这里是我们一直强调的错误检查!! */
if (listen(sockfd, BACKLOG) == -1)
{
/* 如果调用listen 失败,则给出错误提示,退出 */
perror(“listen”);
exit(1);
}
while(1)
{
/* 这里是主accept()循环 */
sin_size = sizeof(struct sockaddr_in);
/* 这里是我们一直强调的错误检查!! */
if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1)
{
/* 如果调用accept()出现错误,则给出错误提示,进入下一个循环 */
perror(“accept”);
continue;
}
/* 服务器给出出现连接的信息 */
printf(“server: got connection from %s\n”, inet_ntoa(their_addr.sin_addr));
/* 这里将建立一个子进程来和刚刚建立的套接字进行通讯 */
if (!fork())
{
/* 这里是子进程 */
/* 这里就是我们说的错误检查! */
if (send(new_fd, “Hello, world!\n”, 14, 0) == -1)
{
/* 如果错误,则给出错误提示,然后关闭这个新连接,退出 */
perror(“send”);
close(new_fd);
exit(0);
}
/* 关闭new_fd 代表的这个套接字连接 */
close(new_fd);
}
}
/* 等待所有的子进程都退出 */
while(waitpid(-1,NULL,WNOHANG) > 0);
}
对应的客户端的程序代码:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
/* 服务器程序监听的端口号 */
#define PORT 4000
/* 我们一次所能够接收的最大字节数 */
#define MAXDATASIZE 100
int main(int argc, char *argv[])
{
/* 套接字描述符 */
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct hostent *he;
/* 连接者的主机信息 */
struct sockaddr_in their_addr;
/* 检查参数信息 */
if (argc != 2)
{
/* 如果没有参数,则给出使用方法后退出 */
fprintf(stderr,“usage: client hostname\n”);
exit(1);
}
/* 取得主机信息 */
if ((he=gethostbyname(argv[1])) == NULL)
{
/* 如果gethostbyname()发生错误,则显示错误信息并退出 */
herror(“gethostbyname”);
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* 如果socket()调用出现错误则显示错误信息并退出 */
perror(“socket”);
exit(1);
}
/* 主机字节顺序 */
their_addr.sin_family = AF_INET;
/* 网络字节顺序,短整型 */
their_addr.sin_port = htons(PORT);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
/* 将结构剩下的部分清零*/
bzero(&(their_addr.sin_zero), 8);
if(connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1)
{
/* 如果connect()建立连接错误,则显示出错误信息,退出 */
perror(“connect”);
exit(1);
}
if((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1)
{
/* 如果接收数据错误,则显示错误信息并退出 */
perror(“recv”);
exit(1);
}
buf[numbytes] = ‘\0’;
printf(“Received: %s”,buf);
close(sockfd);
return 0;
}
Linux网络编程学习(十) ----- Socket(第六章)的更多相关文章
- Linux网络编程学习(十二) ----- 结语
该书提前看完了,重点看了第四章和第六章,第七章以后只是大致浏览了一下,如果以后工作中涉及这一块再仔细研究一下,大概花了二十天的样子,主要了解了进程间的通信方式.socket编程以及五种I/O模式,看的 ...
- Linux网络编程学习(五) ----- 信号(第四章)
1.基本概念 进程阻塞: 进程执行条件得不到满足,就自动放弃CPU资源而进入休眠状态,以等待条件满足,当条件满足时,系统就将控制权还给该进程进行未完成的操作 共享资源: 进程间协调使用的系统资源 锁定 ...
- Linux网络编程学习计划
由于网络编程是很重要的一块,自己这一块也比较欠缺,只知道一些皮毛,从今天开始系统学习<Linux网络编程>一书,全书分为十四个章节: 第一章 概论 P1-16 第二章 UNIX ...
- Linux网络编程:UDP Socket编程范例
TCP协议提供的是一种可靠的,复杂的,面向连接的数据流(SOCK_STREAM)传输服务,它通过三段式握手过程建立连接.TCP有一种"重传确认"机制,即接收端收到数据后要发出一个肯 ...
- Linux网络编程学习路线
转载自:https://blog.csdn.net/lianghe_work/article 一.网络应用层编程 1.Linux网络编程01——网络协议入门 2.Linux网络编程02——无连接和 ...
- Linux 网络编程三(socket代码详解)
//网络编程客户端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include < ...
- Linux 网络编程二(Socket创建)
TCP通信 一个程序使用套接字需要执行4个步骤. --分配套接口和初始化 --连接 --发送或接收数据 --关闭套接字 涉及到的调用包括socket.bind.listen.connect(阻塞线程) ...
- Linux 网络编程四(socket多线程升级版)
//网络编程--客户端 #include <stdio.h> #include <stdlib.h> #include <string.h> #include &l ...
- Unix 网络编程 I/O 模型 第六章
前提,也是重点是, 当接收收据.或者读取数据时,分两步 1 等待数据准备好. 2 从内核拷贝数据到进程. 对于一个network IO 即 socket(这里我们以read举例),它会涉及到两个系统对 ...
随机推荐
- Docker系列(三)Dockerfile 离线构建镜像
一.Dockfile介绍: Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令.Docker程序将这些Dockerfil ...
- 图形化SVN管理搭建 subversion edge自行修改密码
参考文章: https://blog.csdn.net/buyaore_wo/article/details/84313467 安装版本: Subversion Edge 5.2.3 (Linux 6 ...
- netty拆包粘包
客户端 tcp udp socket网络编程接口 http/webservice mqtt/xmpp 自定义RPC (dubbo) 应用层 服务端 ServerSocket ss = new serv ...
- Sqlserver常用基础语句
1. char/nchar,varchar/nvarchar char(10) 只能放五个中文,定长,如果只放一个字节,后面就是九个空格(一个中文两个字节) nchar(10) 放十个中文,定长 va ...
- Ubuntu16.04下安装elasticsearch+kibana实现php客户端的中文分词
1.下载安装java, elasticsearch和kibana apt-get install default-jre default-jdk wget https://artifacts.elas ...
- 简简单单美化你Mac os x的终端配色
Mac OS x虽然是以图形界面出名的,但是作为一个类Unix系统,还是离不开终端(shell)的,尤其是对于开发人员来说,Mac OS x默认状态的终端给人的感觉总是不那么舒服,所以很有必要对它进行 ...
- 在 Docker 里跑 Java,你必须知道的那些事儿!(转)
原文 https://www.jianshu.com/p/0897d0581872 背景:众所周知,当我们执行没有任何调优参数(如“java-jar mypplication-fat.jar”)的 J ...
- Thinkpad 小红点飘移的不完美解决办法
环境:T420 BIOS1.49 windows7 x64 对硬盘执行写入操作,比如说建立一个空白记事本,每次飘移的时候,就alt+tab切到记事本,随便输入一个字符,ctrl+s保存,搞定.
- 2、Netty基础
一.前言 主要包含下面内容: 初识 Netty: 使用 Java NIO 搭建简单的客户端与服务端实现网络通讯: 使用 Netty 搭建简单的客户端与服务端实现网络通讯: Netty 底层操作与 Ja ...
- 面试题_lambda函数调用
res多少? def func(): return [lambda x: i * x for i in range(4)] res = [m(2) for m in func()] # print(r ...