1.TCP/UDP对比

  1. TCP面向连接(如打电话要先拨号建立连接) ;UDP是无连接的,即发送数据之前不需要建立连接
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
  3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP 是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
  4. 每一条TCP连接只能是点到点的;UDP支持一 对一,一对多,多对一和多对多的交互通信
  5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
  6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

2端口号的作用

  1. 一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等

  2. 这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对 多的关系。

  3. 实际上是通过"IP地址+端口号"来区分不同的服务的。
    端口提供了一种访问通道,

  4. 服务器一般都是通过知名 端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务 器的TCP端口号都是21,每个Telnet服 务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69

3. 什么是socket?

网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。

一 连接协议socket()函数

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


当protocol为0时,会自动选择type类型对应的默认协议。
当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数。

二 bind()函数

bind()函数把一个地址族中的特定地址赋给socket。

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

  1. sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
  2. addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:`
struct sockaddr
{unsigned short sa_family; /*地址族*/char sa_data[14]; /*14字节的协议地址,包含该socket的IP地址和端口号。*/
};
struct sockaddr_in
{short int sa_family; /*地址族*/unsigned short int sin_port; /*端口号*/struct in_addr sin_addr; /*IP地址*/unsigned char sin_zero[8]; /*填充0 以保持与struct sockaddr同样大小*/
};
struct in_addr
{unsigned long int  s_addr; /* 32位IPv4地址,网络字节序 */
};
头文件<netinet/in.h>
sa_family:AF_INET  IPv4协议   AF_INET6  IPv6协议
国内一般使用的都是IPv4
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位二进制的网络字节序地址。

struct sockaddr_in {sa_family_t    sin_family; /* address family: AF_INET */in_port_t      sin_port;   /* port in network byte order */struct in_addr sin_addr;   /* internet address */
};/* Internet address. */
struct in_addr {uint32_t       s_addr;     /* address in network byte order */
};

ipv6对应的是:

struct sockaddr_in6 { sa_family_t     sin6_family;   /* AF_INET6 */ in_port_t       sin6_port;     /* port number */ uint32_t        sin6_flowinfo; /* IPv6 flow information */ struct in6_addr sin6_addr;     /* IPv6 address */ uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */
};struct in6_addr { unsigned char   s6_addr[16];   /* IPv6 address */
};

Unix域对应的是:

#define UNIX_PATH_MAX    108struct sockaddr_un { sa_family_t sun_family;               /* AF_UNIX */ char        sun_path[UNIX_PATH_MAX];  /* pathname */
};
  1. addrlen:对应的是地址的长度。

    通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

三 listen()、connect()函数

  1. int listen(int sockfd, int backlog);
  2. int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。

四 accept()函数

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

accept函数的第一个参数为服务器的socket描述字,第二个参数为指向struct sockaddr *的指针,用于返回客户端的协议地址,第三个参数为协议地址的长度。

如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

五 read()、write()等函数

       #include <unistd.h>ssize_t read(int fd, void *buf, size_t count);ssize_t write(int fd, const void *buf, size_t count);#include <sys/types.h>#include <sys/socket.h>ssize_t send(int sockfd, const void *buf, size_t len, int flags);ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

六 close()函数

#include <unistd.h>
int close(int fd);

网络字节序与主机字节序

主机字节序就是我们平常说的大端和小端模式:不同的CPU有不同的字节序类型,这些字节序是指整数在内存中保存的顺序,这个叫做主机序。引用标准的Big-Endian和Little-Endian的定义如下:

a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

字节序:即字节在电脑中存放时的序列与输入(输出)时的序列是先到的在前还是后到的在前。
1. Little endian:将低序字节存储在起始地址
2. Big endian:将高序字节存储在起始地址
网络中使用大大端字节序(Big endian)
四个函数:htons(),ntohs(),htonl()和ntohl().
这四个地址分别实现网络字节序和主机字节序的转化,
这里的h代表host,n代表network,s代表short,l代表long。
通常16位的IP端口号用s代表,而IP地址用l来代表

网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。字节序,顾名思义字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序,一个字节的数据没有顺序的问题了。

所以:在将一个地址绑定到socket的时候,请先将主机字节序转换成为网络字节序,而不要假定主机字节序跟网络字节序一样使用的是Big-Endian。由于这个问题曾引发过血案!公司项目代码中由于存在这个问题,导致了很多莫名其妙的问题,所以请谨记对主机字节序不要做任何假定,务必将其转化为网络字节序再赋给socket。

服务器代码

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>int main(){int s_fd;int c_fd;int mark = 0;int n_read;char readBuf[128] = {0};char str[128] = {0};struct sockaddr_in s_addr;struct sockaddr_in c_addr;memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));s_fd = socket(AF_INET,SOCK_STREAM,0);if(s_fd == -1){perror("socket");exit(-1);}s_addr.sin_family = AF_INET;s_addr.sin_port  = htons(8989);inet_aton("127.0.0.1",&s_addr.sin_addr);bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));listen(s_fd,10);int len = sizeof(struct sockaddr_in);while(1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&len);if(c_fd == -1 ){perror("accept");exit(-1);}mark++;printf("connect sucess : %s\n",inet_ntoa(c_addr.sin_addr));if(fork() == 0){if(fork()==0){while(1){sprintf(str,"welcome NO.%d  client\n",mark);write(c_fd,str, strlen(str));sleep(4);}}while(1){memset(readBuf,0,sizeof(readBuf));n_read = read(c_fd,&readBuf,128);if(n_read ==-1){perror("read:");exit(-1);}elseprintf("receve msg from client : %s\n ", readBuf);}}}return 0;
}

因为多个客户端同时接入时会有资源竞争,所以在服务端在向发送数据时,不确定那个客户端能收到,并且只有一个客户端能收到,所以对服务端进行简易改进(Mark++来代表几=记录第几个客户端连接,每连接一个Mark会加1),实现多方数据通信。首先不断向客户端发送数据,确保通讯,然后不同客户端可任意向客服端发送数据,服务端可监听所有数据。

此次改进只为让服务端成为一个中转站,小改一下,太复杂的目前水平无法实现

客户端代码

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
//#include <linux/in.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>int main(){int c_fd;int n_read;char msg[128]={0};char readBuf[128] = {0};struct sockaddr_in c_addr;memset(&c_addr,0,sizeof(struct sockaddr_in));c_fd = socket(AF_INET,SOCK_STREAM,0);if(c_fd == -1){perror("socket");exit(-1);}c_addr.sin_family = AF_INET;c_addr.sin_port  = htons(8089);inet_aton("127.20.0.1",&c_addr.sin_addr);if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr_in)) == -1){                          perror("connect : ");exit(-1);}while(1){if(fork()==0){while(1){memset(msg, 0 , sizeof(msg));printf("plesase input msg : \n");gets(msg);write(c_fd, msg ,strlen(msg));}while(1){memset(readBuf, 0 , sizeof(readBuf));n_read = read(c_fd,readBuf,128);if(n_read ==-1){perror("read:");exit(-1);}elseprintf(" message from sever %s\n ", readBuf);}}return 0;
}

Linux socket编程相关推荐

  1. linux socket 编程

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

  2. Linux Socket编程

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

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

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

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

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

  5. linux下sig_pipe函数,linux socket编程 出现信号SIGPIPE,分析及解决

    在编写一个仿QQ软件,C/S模式.出现的问题:当客户机关闭时,服务器也随着关闭,纠结很久之后,我gdb了下,出现下面提示信息: Program received signal SIGPIPE, Bro ...

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

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

  7. linux socket编程epoll模型实现群发消息

    1.实现功能 本代码主要实现了socket编程epoll模型实现多个客户端连接服务器,客户端可以进行群发消息和接收用户输入文本信息,然后发送该信息给服务器,服务器收到后发送应答信息.客户端接收并显示该 ...

  8. Linux Socket编程(不限Linux)

    我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程怎么与web 服务器通信的?当你用QQ聊天时,QQ进程怎么与服务器或你好友所在的QQ进程通信?这些都得靠s ...

  9. Linux Socket编程的一些总结

    最近写了一些Linux下网络编程的一些程序,做几点总结吧. 先给出客户端后服务器的一些Socket初始化的代码,以后可以直接拿来调用. 客户端Socket初始化代码 #include <stdi ...

  10. 【Linux网络】Linux Socket编程 TCP协议

    话虽些许夸张,但是事实也是,现在的网络编程几乎都是用的socket. --有感于实际编程和开源项目研究. 我们深谙信息交流的价值,那网络中进程之间如何通信,如我们每天打开浏览器浏览网页时,浏览器的进程 ...

最新文章

  1. 在MFC中使用AFXBEGINTHREAD的方法
  2. 为DEDE织梦添加XMl网站地图
  3. ERP的昨天、今天和明天
  4. go发布一个公开的自定义仓库包
  5. java程序猿面试问缺点怎么回答_JAVA程序员面试32问,你能回答多少题
  6. linux virt java_Linux下Java环境安装
  7. Oracle Statspack分析报告详解(一)
  8. windows启动管理器怎么修复计算机,如果启动管理器丢失怎么办
  9. 物资申请php,危废企业申请经营许可证需满足的条件及申请程序
  10. 保证服务4个9的可用性的核心思路
  11. mysql解压版安装步骤
  12. 基于人体姿态识别的AI健身系统(浅谈
  13. windows 10 安装jira进行开发管理
  14. h5获取视频的第一帧
  15. BCB操作EXCEL
  16. 3dmax软件的制作木桶过程:三步流程
  17. 大学综评自招面试 计算机专业,【荔枝高校大会】自招综评面试6月16日扎堆举行,这些准备工作要做好!...
  18. rating vs nominal 额定和标称区别
  19. 黑马JavaScript核心操作BOM与DOM课程笔记1-DOM
  20. 如何用数据分析进行游戏道具的精准投放?

热门文章

  1. Python安装Anaconda安装
  2. 2021-04-30双螺杆挤出机与挤塑机之间有什么区别?
  3. yield 和 yield*
  4. 沸点大前端组正式成员面试小问题
  5. keepalived配置,解决vip无法ping通,虚拟服务器端口无法访问的问题
  6. 小波变换的matlab实现,维小波变换MATLAB实现
  7. Chapter2:时域分析法(上)
  8. 说说Flink的连接器connector有哪些,怎么用?
  9. Linux pip安装与使用
  10. Facebook名人软件Mentions有了Android版本