1. socket套接字

(1)套接字简介

  ①socket是一种通讯机制,它包含一整套的调用接口和数据结构的定义,它给应用进程提供了使用如TCP/UDP等网络协议进行网络通讯的手段。

  ②Linux中的网络编程通过socket接口实现。socket是一种特殊的IO,提供对应的文件描述符

  ③一个完整的socket都有一个相关描述(协议、本地地址、本地端口、远程地址和远程端口等)。

  ④每一个套接字有一个本地的唯一socket,由操作系统分配。

(2)创建socket

头文件

#include <sys/socket.h>

函数

int socket(int domain, int type, int protocol);

参数

domain参数:

  ①AF_INET:   IPv4因特网域

  ②AF_INET6:  IPv6因特网域

  ③AF_UNIX:   unix域

  ④AF_UNSPEC: 未指定

type参数:

  ①SOCK_STREAM: 流式的套接字可以提供可靠的、面向连接的通讯流。它使用了TCP协议,TCP保证了数据传输的正确性和顺序性。

  ②SOCK_DGRAM:数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠及无差错。

  ③SOCK_RAW:原始套接字允许对低层协议(如IP或ICMP)直接访问,主要用于新的网络协议实现的测试等。

  ④SOCK_SEQPACKET: 长度固定、有序、可靠的面各连接报文传递。

protocol参数:通常为0,表示按给定的域和套接字类型选择默认协议。

功能

创建套接字

返回值

成功返回内核中消息队列的标识ID,出错返回-1

备注

套接字创建在内核中,若创建成功则返回内核文件描述表中的socket描述符。

2. 字节序、地址结构和IPv4地址族

(1)字节序

  ①不同体系结构的主机使用不同的字节序存储器来保存多字节整数。字节存储顺序不同,有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后。

  ②字节序分为大端(高位存储于低地址,低位存储于高地址)小端(高位存储于高地址,低位存储于低地址)

  ③网络协议使用网络字节序,即大端字节序

(2)字节序转换函数

  ①uint32_t htonl(unit32_t hostlong); //将32位整数从主机字节序转为网络字节序

  ②uint16_t htons(unit16_t hostshort);//将16位整数从主机字节序转为网络字节序③uint32_t ntohl(unit32_t netlong); //将32位整数从网络字节序转为主机字节序  

  ④uint16_t ntohs(unit16_t netshort);//将16位整数从网络字节序转为主机字节序

(3)通用地址结构

头文件

#include <sys/socket.h>

结构体

struct sockaddr{

  unsigned short sa_family;  //internet地址族,AF_XXX

  char sa_data[14];          //14字节的协议地址

}

备注

①sa_data包含了一些远程电脑的地址、端口和套接字的数目,它里面的数据是杂溶在一起的。

②sa_family一般来说,IPv4使用AF_INET。

③在传递给需要地址结构的函数时,把指向该结构体的指针转换成(struct sockaddr*)传递进去。

(4)因特网地址结构(专用地址结构)

头文件

#include <sys/socket.h>

结构体

struct in_addr{   //其中的in指的是internet

  in_addr_t  s_addr   //ipv4地址

}

struct sockaddr_in{

  short int  sin_family;    //internet地址族,AF_XXX(主机字节序)

  unsigned short int sin_port; //端口号,16位(网络字节序)

  struct in_addr sin_addr;   //32位的IPv4地址(网络字节序)

  unsigned char sin_zero[8]; //添0(为了格式对齐的填充位)

}

备注

sockaddr和sockaddr_in这两个数据类型是等效的,可以相互转换,通常使用sockaddr_in更为方便。

(5)IPv4地址族和字符地址间的转换

头文件

#include <arp/inet.h>

函数1

const char* inet_ntop(int domain, const void* addr,

char* str, socklen_t size);//网络字节地址转为点分十进制

返回:成功返回地址字符串指针,出错返回NULL

函数2

int inet_pton(int domain, const char* str, void* addr);//点分转网络

返回:成功返回1,无效格式返回0,出错返回-1

参数

domain:Internet地址族,如AF_INET

addr: internet地址,32位IPv4地址(网络字节序)

str: 地址字符串(点分十进制)指针

size: 地址字符串大小

(6)填写IPv4地址族结构体案例

struct sockaddr_in sin;      //定义一个sockaddr_in结构体
char buf[]; memset(&sin, , sizeof(sin));
sin.sin_family = AF_INET; //填写Internet地址族
sin.sin_port = htons((short)); //填写端口号(网络字节序) //填充sin_addr
if(inet_pton(AF_INET, "192.168.2.1", &sin.sin_addr.s_addr) <=){
//错误处理
} printf("%s\n", inet_ntop(AF_INET, &sin.sin_addr.s_addr,buf, sizeof(buf)));

3. TCP编程模型

3.1 TCP通讯模型

(1)服务端调用序列

  ①调用socket函数创建套接字

  ②调用bind绑定本地地址和端口

  ③调用listen启动监听

  ④调用access从己连接队列中提取客户连接

  ⑤调用I/O函数(read/write)与客户端通讯

  ⑥调用close关闭套接字

(2)客户端调用序列

  ①调用socket函数创建套接字

  ②调用connect连接服务器端

  ③调用I/O函数(read/write)与服务器端通讯

  ④调用close关闭套接字

3.2 相关函数

(1)套接字与地址绑定

头文件

#include <sys/socket.h>

绑定地址

int bind(int sockfd, const struct sockaddr* addr, socklen_t len);

返回:成功返回0,出错返回-1

特特地址:#define INADDR_ANY  (uint32_t)0x00000000 //响应主机上的任何一个可用的IP地址。

查找绑定到套接字的地址

int getsockname(int sockfd, struct sockaddr* addr, socklen_t alenp)

返回:成功返回0,出错返回-1

获取对方地址

int getpeername(int sockfd, struct sockaddr* addr, socklen_t alenp)

返回:成功返回0,出错返回-1

(2)建立连接

头文件

#include <sys/socket.h>

服务器端

①int listen(int sockfd, int backlog);//backlog为指定进行客户端连接排队的队列长度。

返回:成功返回0,出错返回-1

②int accept(int sockfd, struct sockaddr* addr, socklen_t* len);

客户端

int connect(int sockfd, cosnt struct sockaddr* addr, socklen_t len)

返回:成功返回0,出错返回-1

【编程实验】获得服务器的时间

//time_tcp_server.c(服务端)

#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <time.h>
#include <signal.h> /*获取服务器端的当前时间*
测试:telnet 127.0.0.1 xxxx
http://xxx.xxx.xxx.xxx:端口号
注意:演示时可关闭服务器的防火墙,防止端口被过滤
#service iptables status 查看防火墙
#service iptables stop 关闭防火墙
*/ int sockfd;
int bStop = ; void sig_handler(int signo)
{
if(signo == SIGINT){
bStop = ;
printf("server close\n"); exit();
}
} //显示客户端信息
void out_addr(struct sockaddr_in* addr)
{
//将端口从网络字节序转换成主机字节序
int port = ntohs(addr->sin_port);
//获得IP地址
char ip[] ={};
//将ip地址从网络字节序转换成点分十分制
inet_ntop(AF_INET, &addr->sin_addr.s_addr, ip, sizeof(ip)); printf("Client: %s(%d) connected\n", ip, port);
} //服务程序
void do_service(int fd)
{
//获得系统时间
long t = time();
char* s = ctime(&t);
size_t size = strlen(s) * sizeof(char); //将服务器端获得的系统时间写回客户端
if(write(fd, s, size) != size){
perror("write error");
}
} int main(int argc, char* argv[])
{
if(argc < ){
printf("usage: %s port\n", argv[]);
} //按ctrl-c时中止服务端程序
if(signal(SIGINT, sig_handler) == SIG_ERR){
perror("signal sigint error");
exit();
} /*步骤1:创建socket(套接字)
*注:socket创建在内核中,是一个结构体
*AF_INET:IPv4
*SOCK_STREAM:tcp协议
*/
sockfd = socket(AF_INET, SOCK_STREAM, ); /*步骤2:将sock和地址(包括ip、port)进行绑定*/
struct sockaddr_in servAddr; //使用专用地址结构体
memset(&servAddr, , sizeof(servAddr));
//往地址中填入ip、port和Internet地址族类型
servAddr.sin_family = AF_INET;//IPv4
servAddr.sin_port = htons(atoi(argv[])); //port
servAddr.sin_addr.s_addr = INADDR_ANY; //任一可用的IP if(bind(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < ){
perror("bind error");
exit();
} /*步骤3:调用listen函数启动监听
* 通知系统去接受来自客户端的连接请求
*/
if(listen(sockfd, ) < ){ //队列中最多允许10个连接请求
perror("listen error");
exit();
} /*步骤4:调用accept函数,从请求队列中获取一个连接
* 并返回新的socket描述符
* */
struct sockaddr_in clientAddr;
socklen_t clientAddr_len = sizeof(clientAddr);
while(!bStop){
//如果没有客户端连接,调用此函数后会阻塞,直至获得一个客户端连接
int fd = accept(sockfd, (struct sockaddr*)&clientAddr, &clientAddr_len); if(fd < ){
perror("accept error");
continue;
} //输出客户端信息
out_addr(&clientAddr); /*步骤5:调用IO函数(read/write)和客户端进行双向通信*/
do_service(fd); /*步骤6: 关闭fd套接字*/
close(fd);
} close(sockfd); return ;
}
/*输出结果
* [root@localhost 13.TCP]# bin/time_tcp_server 8888
* Client: 192.168.32.100(40672) connected
* Client: 127.0.0.1(40608) connected
* Client: 192.168.32.100(40674) connected
*/

//time_tcp_client.c(客户端)

#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h> int main(int argc, char* argv[])
{
if(argc < ){
printf("usage: %s ip port\n", argv[]);
exit();
} /*步骤1: 创建socket(套接字)*/
int sockfd = socket(AF_INET, SOCK_STREAM, );
if(sockfd < ){
perror("socket error");
} //往servAddr中填入ip、port和地址族类型
struct sockaddr_in servAddr;
memset(&servAddr, , sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(atoi(argv[]));
//将ip地址转换成网络字节序后填入servAdd中
inet_pton(AF_INET, argv[], &servAddr.sin_addr.s_addr); /*步骤2: 客户端调用connect函数连接到服务器端*/
if(connect(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < ){
perror("connect error");
exit();
} /*步骤3: 调用IO函数(read/write)和服务端进行双向通信*/
char buffer[];
memset(buffer, , sizeof(buffer));
size_t size;
if((size = read(sockfd, buffer, sizeof(buffer))) < ){
perror("read error");
} if(write(STDOUT_FILENO, buffer, size) != size){
perror("write error");
} /*关闭套接字*/
close(sockfd);
}
/*输出结果
[root@localhost 13.TCP]# bin/time_tcp_client 127.0.0.1 8888
Fri Mar 17 16:35:57 2017
*/

第13章 TCP编程(1)_socket套接字的更多相关文章

  1. 第13章 TCP编程(4)_基于自定义协议的多线程模型

    7. 基于自定义协议的多线程模型 (1)服务端编程 ①主线程负责调用accept与客户端连接 ②当接受客户端连接后,创建子线程来服务客户端,以处理多客户端的并发访问. ③服务端接到的客户端信息后,回显 ...

  2. 第13章 TCP编程(3)_基于自定义协议的多进程模型

    5. 自定义协议编程 (1)自定义协议:MSG //自定义的协议(TLV:Type length Value) typedef struct{ //协议头部 ];//TLV中的T unsigned i ...

  3. 第13章 TCP编程(2)_TCP的连接和关闭过程

    4. TCP的连接和关闭过程 4.1 TCP连接的三次握手和四次挥手 (1)三次握手 ①第1次握手:建立连接.客户端发送连接请求报文段(SYN=1,sequence Number=x):然后客户端进入 ...

  4. Python黑帽编程2.8 套接字编程

    Python黑帽编程2.8 套接字编程 套接字编程在本系列教程中地位并不是很突出,但是我们观察网络应用,绝大多数都是基于Socket来做的,哪怕是绝大多数的木马程序也是如此.官方关于socket编程的 ...

  5. Unix网络编程--卷一:套接字联网API

    UNIX网络编程--卷一:套接字联网API 本书面对的读者是那些希望自己编写的程序能够使用成为套接字(socket)的API进行彼此通信的人. 目录: 0.准备环境 1.简介 2.传输层:TCP.UD ...

  6. Linux网络编程:原始套接字简介

    Linux网络编程:原始套接字编程 一.原始套接字用途 通常情况下程序员接所接触到的套接字(Socket)为两类: 流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的T ...

  7. VC++学习之网络编程中的套接字

    VC++学习之网络编程中的套接字 套接字,简单的说就是通信双方的一种约定,用套接字中的相关函数来完成通信过程.应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问 ...

  8. 网络编程与socket套接字

    网络编程与socket套接字 传输层 PORT协议 port是一种接口,数据通过它在计算机和其他设备(比如打印机,鼠标,键盘或监视器)之间,网络之间和其他直接连接的计算机之间传递 TCP协议 ​ 传输 ...

  9. 网络编程之基于tcp和udp的套接字

    一   udp协议网络编程 DNS:将域名解析成ip地址 SOCK_DGRAM:数据报协议,也是udp协议 udp协议的网络编程的一些用法: recvfrom:接收消息,接收的时一个元组,元组里面的元 ...

随机推荐

  1. 解决在转发一条内容为满的彩信,删除主题FWD,发送的时候提示转化为短信。

    问题描述: 1.长按一条输入内容为满的彩信,选择转发 2.输入联系人-删除主题FWD-发送 测试结果为:提示正转化为短信(见附件) 预期结果为:不应该有提示,应该还是彩信 测试结果图为: 根据提示的T ...

  2. TCP三次握手与四次挥手详解

    目录 TCP三次握手与四次挥手详解 1.TCP报文格式 2.TCP三次握手 3.TCP四次挥手 4.为什么建立连接需要三次握手? 5.为什么断开连接需要四次挥手? 6.为什么TIME_WAIT状态还需 ...

  3. 导出导入grafana完整的dashboard(非单个图表)

    导出很简单,如下图操作即可 导入

  4. SSH项目搭建(五)——web.xml文件配置

    上一章写到pom.xml有一个报错,说找不到web.xml文件.确实是这样的,因为我们用maven搭建的web层里就是没有这个文件.我们能看到,webapp文件夹里是空的. 没有,就想办法把它弄出来. ...

  5. Java快速排序和归并排序详解

    快速排序 概述 快速排序算法借鉴的是二叉树前序遍历的思想,最终对数组进行排序. 优点: 对于数据量比较大的数组排序,由于采用的具有二叉树二分的思想,故排序速度比较快 局限 只适用于顺序存储结构的数据排 ...

  6. Android的界面设计工具——DroidDraw

    软件名称:DroidDraw 软件大小:489KB(Windows版本) 支持系统:Mac OS X/Windows/Linux 下载地址:http://code.google.com/p/droid ...

  7. 与众不同 制作会唱歌的WinRAR - imsoft.cnblogs

    为了使用方便,我们可能会把RAR压缩包制作成自解压文件.WinRAR自带的自解压模块虽然使用很方便,但千篇一律的外观看起来实在 乏味.其实,只要通过简单改造,你就可以制作出与众不同,声色俱佳的WinR ...

  8. 在有 UI 线程参与的同步锁(如 AutoResetEvent)内部使用 await 可能导致死锁

    AutoResetEvent.ManualResetEvent.Monitor.lock 等等这些用来做同步的类,如果在异步上下文(await)中使用,需要非常谨慎. 本文将说一个在同步上下文中非常常 ...

  9. (4)logging(日志模块)

    日志分成几个常用的级别 debug 10 代表程序调试过程中的信息 info 20 代表普通日志信息,用户的访问等等 warning 30 警告日志,有可能出错,但是目前还没出错的 error 40 ...

  10. ubuntu 远程gui显示

    图像从定向: ubuntu 显示系统xterm  重点是设置DISPLAY variable以及保证ubuntu和suse在同一网段即相互ping通,利用ssh -XY的方式重定向图像. 1.直接ss ...