一、socket运行过程

服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

二、socket实现过程

2.1 socket()函数

 int  socket(int protofamily, int type, int protocol);//返回sockfd

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符(socket descriptor),它唯一标识一个socket。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。

正如可以给fopen的传入不同参数值,以打开不同的文件。创建socket的时候,也可以指定不同的参数创建不同的socket描述符,socket函数的三个参数分别为:

  • protofamily:即协议域,又称为协议族(family)。常用的协议族有,AF_INET(IPV4)、AF_INET6(IPV6)、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
  • type:指定socket类型。常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等(socket的类型有哪些?)。
  • protocol:故名思意,就是指定协议。常用的协议有,IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议

注意:当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。

2.2 bind()函数

正如上面所说bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或ipv6地址和端口号组合赋给socket。

   int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • sockfd:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将给这个描述字绑定一个名字。
  • addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。这个地址结构根据地址创建socket时的地址协议族的不同而不同,如ipv4对应的是:
  • addrlen:对应的是地址的长度。

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

2.3 listen()、connect()函数

如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

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

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

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

2.4 accept()函数

TCP服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。TCP客户端依次调用socket()、connect()之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); //返回连接connect_fd

参数sockfd

参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。

参数addr

这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。

参数len

如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。

如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。

注意:

accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。

2.5、read()、write()等函数

万事具备只欠东风,至此服务器与客户已经建立好连接了。可以调用网络I/O进行读写操作了,即实现了网咯中不同进程之间的通信!网络I/O操作有下面几组:

  • read()/write()
  • recv()/send()
  • readv()/writev()
  • recvmsg()/sendmsg()
  • recvfrom()/sendto()

2.6、close()函数

在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。

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

close一个TCP socket的缺省行为时把该socket标记为以关闭,然后立即返回到调用进程。该描述字不能再由调用进程使用,也就是说不能再作为read或write的第一个参数。

注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。

三、SOCKET 之TCP通信例程

1、客户端

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define MAXLINE 4096  int main(int argc, char** argv)
{  int    sockfd, n,rec_len;  char    recvline[4096], sendline[4096];  char    buf[MAXLINE];  struct sockaddr_in    servaddr;  if( argc != 2){  printf("usage: ./client <ipaddress>\n");  exit(0);  }  if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){  printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);  exit(0);  }  memset(&servaddr, 0, sizeof(servaddr));  servaddr.sin_family = AF_INET;  servaddr.sin_port = htons(8000);  if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){  printf("inet_pton error for %s\n",argv[1]);  exit(0);  }  if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){  printf("connect error: %s(errno: %d)\n",strerror(errno),errno);  exit(0);  }  printf("send msg to server: \n");  fgets(sendline, 4096, stdin);  if( send(sockfd, sendline, strlen(sendline), 0) < 0)  {  printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);  exit(0);  }  if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {  perror("recv error");  exit(1);  }  buf[rec_len]  = '\0';  printf("Received : %s ",buf);  close(sockfd);  exit(0);
}  

2、服务端

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define DEFAULT_PORT 8000
#define MAXLINE 4096
int main(int argc, char** argv)
{  int    socket_fd, connect_fd;  struct sockaddr_in     servaddr;  char    buff[4096];  int     n;  //初始化Socket  if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);  exit(0);  }  //初始化  memset(&servaddr, 0, sizeof(servaddr));  servaddr.sin_family = AF_INET;  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。  servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT  //将本地地址绑定到所创建的套接字上  if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  exit(0);  }  //开始监听是否有客户端连接  if( listen(socket_fd, 10) == -1){  printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  exit(0);  }  printf("======waiting for client's request======\n");  while(1){
//阻塞直到有客户端连接,不然多浪费CPU资源。  if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){  printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  continue;  }
//接受客户端传过来的数据  n = recv(connect_fd, buff, MAXLINE, 0);
//向客户端发送回应数据  if(!fork()){ /*紫禁城*/  if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1)  perror("send error");  close(connect_fd);  exit(0);  }  buff[n] = '\0';  printf("recv msg from client: %s\n", buff);  close(connect_fd);  }  close(socket_fd);
}  

四、测试程序流程

编译server.c

gcc -o server server.c

启动进程:

./server

显示结果:

======waiting for client's request======

并等待客户端连接。

编译 client.c

arm-none-linux-gnueabi-gcc -o client client.c -static

客户端去连接server:

./client 192.168.1.101

Linux环境SOCKET编程1:套接字相关推荐

  1. Linux原始网络编程,Linux操作系统网络编程 原始套接字 (1)

    Linux操作系统网络编程--原始套接字 (1) http://soft.zdnet.com.cn/software_zone/2007/1020/568223.shtml 我们在前面已经学习过了网络 ...

  2. JAVA socket编程 Datagram套接字 UDP协议(转)

      查看文章     JAVA socket编程 Datagram套接字 UDP协议 2009-05-13 09:35 1 UDP套接字 数据报(Datagram)是网络层数据单元在介质上传输信息的一 ...

  3. Linux环境SOCKET编程3:压力测试

    测试方法:使用epoll实现一个通用的服务器压力测试程序. 测试代码: #include <stdlib.h> #include <stdio.h> #include < ...

  4. Linux环境SOCKET编程5:定时器接口timerfd

    1.概述 timerfd是Linux为用户程序提供的一个定时器接口.这个接口基于文件描述符,通过文件描述符的可读事件进行超时通知,所以能够被用于select/poll的应用场景.timerfd是lin ...

  5. Linux环境SOCKET编程2:epoll分析

    我们通过实现分析知道LT模式下epoll_wait被唤醒可以通过两种方式,而ET模式只能通过一种方式.所以ET模式下能被唤醒的情况,LT模式下一定也能被唤醒.我们先来讨论特殊情况(ET模式),再来讨论 ...

  6. python套接字编程_Python套接字编程(1)——socket模块与套接字编程

    在Python网络编程系列,我们主要学习以下内容: 5. 常见的Python异步编程框架 6. 协程在Python网络编程中的使用 本文介绍Python下的基本套接字编程,主要基于 socket 模块 ...

  7. [python学习] 专题七.网络编程之套接字Socket、TCP和UDP通信实例

    很早以前研究过C#和C++的网络通信,参考我的文章:                  C#网络编程之Tcp实现客户端和服务器聊天                 C#网络编程之套接字编程基础知识   ...

  8. Linux网络编程——原始套接字编程

    Linux网络编程--原始套接字编程 转自:http://blog.csdn.net/tennysonsky/article/details/44676377 原始套接字编程和之前的 UDP 编程差不 ...

  9. python socket清空接收缓冲区_Python网络编程——修改套接字发送和接收的缓冲区大小...

    很多情况下,默认的套接字缓冲区大小可能不够用.此时,可以将默认的套接字缓冲区大小改成一个更合适的值. 1. 代码 # ! /usr/bin/env python # -*- coding: utf-8 ...

最新文章

  1. GAN生成对抗网络-text to image原理与基本实现-文字转图像-11
  2. ubuntu查看版本
  3. uva 11997 K Smallest Sums 优先队列处理多路归并问题
  4. TCollector
  5. 在VMware开启此虚拟机时出现内部错误
  6. 边缘计算架构_边缘计算与开放基础架构的重要性
  7. matplotlib是python第三方库吗_python第三方库matplotlib
  8. Halcon 二维码
  9. 禁用Chrome自动更新
  10. 脚本自动安装rkhunter检查rootkit
  11. 医院管理数据库课程设计
  12. mysql源代码_MySQL源代码解读(一)
  13. lr常见问题汇总(持续更新版)
  14. 如何解决移动硬盘无法格式化?两招方法教会你
  15. Description Resource Path Location Type The import collides with another import statement
  16. 一个大数据架构师应该掌握的技能
  17. occt 几何图形库入门01
  18. 浙江工业大学计算机考研科目,浙江工业大学考研专业目录
  19. MyBatis框架学习笔记01:初生牛犊
  20. html5第八课时,属性前缀

热门文章

  1. 【汇编语言与计算机系统结构笔记10】C语言数组的汇编访问:连续存储、代码优化、无边界检查;结构对齐要求 #简洁笔记形式
  2. 【汇编语言与计算机系统结构笔记05】汇编的系统结构,从C代码生产汇编代码,一个具体的、经典的数据传送指令(mov)实例与分析
  3. Redis和Memcached在功能、性能以及内存使用效率上的对比
  4. 计算机病毒教学评课,计算机病毒评课稿.docx
  5. MySQL数据库与Oracle数据库在存储中文字符以字节或字符存储的区别
  6. Trufun Kant Studio 2008面向VS.NET的开发应用
  7. ArcGIS水文分析实战教程(6)河流提取与河网分级
  8. apache 下载安装linux,linux下安装apache详解
  9. 用友u8怎么导出凭证_CASE11 用友U8导入期初档案凭证批量作废编辑自动现流(5/5)...
  10. 微信 小程序 python 渲染_干货 | 微信小程序自动化测试最佳实践(附 Python 源码)...