套接字函数

1 创建套接字──socket()

应用程序在使用套接字前,首先必须拥有一个套接字,系统调用socket()向应用程序提供创建套接字的手段,

 
其调用格式如下:
SOCKET PASCAL FAR socket(int af, int type, int protocol);

该调用要接收三个参数:af、type、protocol。参数af指定通信发生的区域:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域。因此,地址族与协议族相同。参数type 描述要建立的套接字的类型。
 
这里分三种:
(1)一是TCP流式套接字(SOCK_STREAM)提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。文件传送协议(FTP)即使用流式套接字。
(2)二是数据报式套接字(SOCK_DGRAM)提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。网络文件系统(NFS)使用数据报式套接字。
(3)三是原始式套接字(SOCK_RAW)该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。
参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。因此,socket()系统调用实际上指定了相关五元组中的“协议”这一元。

2 指定本地地址──bind()

当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。

 
其调用格式如下:
int PASCAL FAR bind(SOCKET s, const struct sockaddr FAR * name, int namelen);
 
参数s是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。参数name 是赋给套接字s的本地地址(名字),其长度可变,结构随通信域的不同而不同。namelen表明了name的长度。如果没有错误发生,bind()返回0。否则返回SOCKET_ERROR。

3 建立套接字连接──connect()与accept()

这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。accept()用于使服务器等待来自某客户进程的实际连接。

connect()的调用格式如下:
int PASCAL FAR connect(SOCKET s, const struct sockaddr FAR * name, int namelen);

参数s是欲建立连接的本地套接字描述符。参数name指出说明对方套接字地址结构的指针。对方套接字地址长度由namelen说明。
如果没有错误发生,connect()返回0。否则返回值SOCKET_ERROR。在面向连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。
由于地址族总被包含在套接字地址结构的前两个字节中,并通过socket()调用与某个协议族相关。因此bind()和connect()无须协议作为参数。

accept()的调用格式如下:
SOCKET PASCAL FAR accept(SOCKET s, struct sockaddr FAR* addr, int FAR* addrlen);

参数s为本地套接字描述符,在用做accept()调用的参数前应该先调用过listen()。addr 指向客户方套接字地址结构的指针,用来接收连接实体的地址。addr的确切格式由套接字创建时建立的地址族决定。addrlen 为客户方套接字地址的长度(字节数)。如果没有错误发生,accept()返回一个SOCKET类型的值,表示接收到的套接字的描述符。否则返回值INVALID_SOCKET。

accept()用于面向连接服务器。参数addr和addrlen存放客户方的地址信息。调用前,参数addr 指向一个初始值为空的地址结构,而addrlen 的初始值为0;调用accept()后,服务器等待从编号为s的套接字上接受客户连接请求,而连接请求是由客户方的connect()调用发出的。当有连接请求到达时,accept()调用将请求连接队列上的第一个客户方套接字地址及长度放入addr 和addrlen,并创建一个与s有相同特性的新套接字号。新的套接字可用于处理服务器并发请求。

四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。socket()指定五元组中的协议元,它的用法与是否为客户或服务器、是否面向连接无关。bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关:在服务器方,无论是否面向连接,均要调用bind(),若采用面向连接,则可以不调用bind(),而通过connect()自动完成。若采用无连接,客户方必须使用bind()以获得一个唯一的地址。

4 监听连接──listen()

此调用用于面向连接服务器,表明它愿意接收连接。listen()需在accept()之前调用,

 
其调用格式如下:
int PASCAL FAR listen(SOCKET s, int backlog);

参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。backlog表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。如果没有错误发生,listen()返回0。否则它返回SOCKET_ERROR。
listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。
调用listen()是服务器接收一个连接请求的四个步骤中的第三步。它在调用socket()分配一个流套接字,且调用bind()给s赋于一个名字之后调用,而且一定要在accept()之前调用。

5 数据传输──send()与recv()

当一个连接建立以后,就可以传输数据了。常用的系统调用有send()和recv()。
send()调用用于s指定的已连接的数据报或流套接字上发送输出数据,格式如下:

int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);

参数s为已连接的本地套接字描述符。buf 指向存有发送数据的缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否发送带外数据等。如果没有错误发生,send()返回总共发送的字节数。否则它返回SOCKET_ERROR。

recv()调用用于s指定的已连接的数据报或流套接字上接收输入数据,格式如下:

int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);

参数s 为已连接的套接字描述符。buf指向接收输入数据缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否接收带外数据等。如果没有错误发生,recv()返回总共接收的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR。

6 关闭套接字──close

close()关闭套接字s,并释放分配给该套接字的资源;如果s涉及一个打开的TCP连接,则该连接被释放。

 

TCP套接字编程实例

本例是利用以上基本函数编写的一个完整的TCP客户/服务器程序。完成一个回射服务器的功能:
(1)客户从标准输入读入一行文本,并写给服务器;
(2)服务器从网络读入这行文本,并回射给客户;
(3)客户从网络输入读入这行文本,并显示在标准输出上。 
实现流程如下图:
 
注:服务器接收到的客户链接,创建子进程处理操作;

config.h

  1. /*
  2. * config.h 包含该tcp/ip套接字编程所需要的基本头文件,与server.c client.c位于同一目录下
  3. */
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/socket.h>
  9. #include <sys/types.h>
  10. #include <unistd.h>
  11. #include <errno.h>
  12. #include <netinet/in.h>
  13. #include <netdb.h>
  14. #include <arpa/inet.h>
  15.  
  16. const int MAX_LINE = ;
  17. const int PORT = ;
  18. const int BACKLOG = ;
  19. const int LISTENQ = ;
  20. const int MAX_CONNECT = ;

server.c

  1. /*
  2. * server.c为服务器端代码
  3. */
  4.  
  5. #include "config.h"
  6.  
  7. int main(int argc , char **argv)
  8. {
  9. /*声明服务器地址和客户链接地址*/
  10. struct sockaddr_in servaddr , cliaddr;
  11.  
  12. /*声明服务器监听套接字和客户端链接套接字*/
  13. int listenfd , connfd;
  14. pid_t childpid;
  15.  
  16. /*声明缓冲区*/
  17. char buf[MAX_LINE];
  18.  
  19. socklen_t clilen;
  20.  
  21. /*(1) 初始化监听套接字listenfd*/
  22. if((listenfd = socket(AF_INET , SOCK_STREAM , )) < )
  23. {
  24. perror("socket error");
  25. exit();
  26. }//if
  27.  
  28. /*(2) 设置服务器sockaddr_in结构*/
  29. bzero(&servaddr , sizeof(servaddr));
  30.  
  31. servaddr.sin_family = AF_INET;
  32. servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //表明可接受任意IP地址
  33. servaddr.sin_port = htons(PORT);
  34.  
  35. /*(3) 绑定套接字和端口*/
  36. if(bind(listenfd , (struct sockaddr*)&servaddr , sizeof(servaddr)) < )
  37. {
  38. perror("bind error");
  39. exit();
  40. }//if
  41.  
  42. /*(4) 监听客户请求*/
  43. if(listen(listenfd , LISTENQ) < )
  44. {
  45. perror("listen error");
  46. exit();
  47. }//if
  48.  
  49. /*(5) 接受客户请求*/
  50. for( ; ; )
  51. {
  52. clilen = sizeof(cliaddr);
  53. if((connfd = accept(listenfd , (struct sockaddr *)&cliaddr , &clilen)) < )
  54. {
  55. perror("accept error");
  56. exit();
  57. }//if
  58.  
  59. //新建子进程单独处理链接
  60. if((childpid = fork()) == )
  61. {
  62. close(listenfd);
  63. //str_echo
  64. ssize_t n;
  65. char buff[MAX_LINE];
  66. while((n = read(connfd , buff , MAX_LINE)) > )
  67. {
  68. write(connfd , buff , n);
  69. }
  70. exit();
  71. }//if
  72. close(connfd);
  73. }//for
  74.  
  75. /*(6) 关闭监听套接字*/
  76. close(listenfd);
  77. }

client.c

  1. /*
  2. * client.c为客户端代码
  3. */
  4.  
  5. #include "config.h"
  6.  
  7. /*readline函数实现*/
  8. ssize_t readline(int fd, char *vptr, size_t maxlen)
  9. {
  10. ssize_t n, rc;
  11. char c, *ptr;
  12.  
  13. ptr = vptr;
  14. for (n = ; n < maxlen; n++) {
  15. if ( (rc = read(fd, &c,)) == ) {
  16. *ptr++ = c;
  17. if (c == '\n')
  18. break; /* newline is stored, like fgets() */
  19. } else if (rc == ) {
  20. *ptr = ;
  21. return(n - ); /* EOF, n - 1 bytes were read */
  22. } else
  23. return(-); /* error, errno set by read() */
  24. }
  25.  
  26. *ptr = ; /* null terminate like fgets() */
  27. return(n);
  28. }
  29.  
  30. int main(int argc , char ** argv)
  31. {
  32. /*声明套接字和链接服务器地址*/
  33. int sockfd;
  34. struct sockaddr_in servaddr;
  35.  
  36. /*判断是否为合法输入*/
  37. if(argc != )
  38. {
  39. perror("usage:tcpcli <IPaddress>");
  40. exit();
  41. }//if
  42.  
  43. /*(1) 创建套接字*/
  44. if((sockfd = socket(AF_INET , SOCK_STREAM , )) == -)
  45. {
  46. perror("socket error");
  47. exit();
  48. }//if
  49.  
  50. /*(2) 设置链接服务器地址结构*/
  51. bzero(&servaddr , sizeof(servaddr));
  52. servaddr.sin_family = AF_INET;
  53. servaddr.sin_port = htons(PORT);
  54. if(inet_pton(AF_INET , argv[] , &servaddr.sin_addr) < )
  55. {
  56. printf("inet_pton error for %s\n",argv[]);
  57. exit();
  58. }//if
  59.  
  60. /*(3) 发送链接服务器请求*/
  61. if( connect(sockfd , (struct sockaddr *)&servaddr , sizeof(servaddr)) < )
  62. {
  63. perror("connect error");
  64. exit();
  65. }//if
  66.  
  67. /*(4) 消息处理*/
  68. char sendline[MAX_LINE] , recvline[MAX_LINE];
  69. while(fgets(sendline , MAX_LINE , stdin) != NULL)
  70. {
  71. write(sockfd , sendline , strlen(sendline));
  72.  
  73. if(readline(sockfd , recvline , MAX_LINE) == )
  74. {
  75. perror("server terminated prematurely");
  76. exit();
  77. }//if
  78.  
  79. if(fputs(recvline , stdout) == EOF)
  80. {
  81. perror("fputs error");
  82. exit();
  83. }//if
  84. }//while
  85.  
  86. /*(5) 关闭套接字*/
  87. close(sockfd);
  88. }

运行结果

 

C 套接字的更多相关文章

  1. Linux Socket 原始套接字编程

    对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...

  2. c 网络与套接字socket

    我们已经知道如何使用I/O与文件通信,还知道了如何让同一计算机上的两个进程进行通信,这篇文章将创建具有服务器和客户端功能的程序 互联网中大部分的底层网络代码都是用C语言写的. 网络程序通常有两部分组成 ...

  3. WebSocket异常 通常每个套接字地址(协议/网络地址/端口)只允许使用一次

    websocket的实例:http://blog.csdn.net/for_cxc/article/details/51500185 问题: 新建一个连接通信没有问题,但是如果关闭再建立就会报错:通常 ...

  4. Apache报错信息之通常每个套接字地址(协议/网络地址/端口)只允许使用一次(could not bind to address 0.0.0.0:80)

    我们常常在执行 httpd –k restart 重启Apache时报错提示: (OS 10048)通常每个套接字地址(协议/网络地址/端口)只允许使用一次.  : AH00072: make_soc ...

  5. C++网络套接字编程TCP和UDP实例

    原文地址:C++网络套接字编程TCP和UDP实例作者:xiaojiangjiang 1.       创建一个简单的SOCKET编程流程如下 面向有连接的套接字编程 服务器: 1)  创建套接字(so ...

  6. 谢欣伦 - OpenDev原创教程 - 服务端套接字类CxServerSocket

    这是一个精练的服务端套接字类,类名.函数名和变量名均采用匈牙利命名法.小写的x代表我的姓氏首字母(谢欣伦),个人习惯而已,如有雷同,纯属巧合. CxServerSocket的使用如下(以某个叫做CSo ...

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

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

  8. 探索UDP套接字编程

    UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务, ...

  9. 【Python网络编程】利用Python进行TCP、UDP套接字编程

    之前实现了Java版本的TCP和UDP套接字编程的例子,于是决定结合Python的学习做一个Python版本的套接字编程实验. 流程如下: 1.一台客户机从其标准输入(键盘)读入一行字符,并通过其套接 ...

  10. Linux进程间通信(八):流套接字 socket()、bind()、listen()、accept()、connect()、read()、write()、close()

    前面说到的进程间的通信,所通信的进程都是在同一台计算机上的,而使用socket进行通信的进程可以是同一台计算机的进程,也是可以是通过网络连接起来的不同计算机上的进程.通常我们使用socket进行网络编 ...

随机推荐

  1. C++(四十三) — 函数模板机制

     1.普通函数与模板函数调用原则 函数模板可以像普通函数一样被重载: 当函数模板和普通函数都符合条件时,编译器优先考虑普通函数: 但如果函数模板产生一个更好的匹配,则选择函数模板: 可以通过空模板实参 ...

  2. 2013.5.21 - KDD第三十三天

    实验室例会,上到一半之后发现今天下午第二节课是Android,上次两节Android都没跟中秋碰头,这次又不能碰头了,然 后就赶紧给中秋发了个短信,说我在开会,晚上约个时间再谈.正好也称这一下午加一晚 ...

  3. abp vNext微服务框架分析

    本文转载自:https://www.cnblogs.com/william-xu/p/11245738.html abp vNext新框架的热度一直都很高,于是最近上手将vNext的微服务Demo做了 ...

  4. 二维码内嵌LOGO

    package Test; import java.awt.BasicStroke; import java.awt.Graphics; import java.awt.Graphics2D; imp ...

  5. 团队作业Beta冲刺--第二天

    团队作业Beta冲刺 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 你们都是魔鬼吗 作业学习目标 (1)掌握软件黑盒测试技术:(2)学会编制软件 ...

  6. Python语言程序设计(2)--深入理解python

  7. commons-dbutils工具栏的编写

    db.properties driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test_db?useUnicode=t ...

  8. Tensorflow细节-P84-梯度下降与批量梯度下降

    1.批量梯度下降 批量梯度下降法是最原始的形式,它是指在每一次迭代时使用所有样本来进行梯度的更新.从数学上理解如下: 对应的目标函数(代价函数)即为: (1)对目标函数求偏导: (2)每次迭代对参数进 ...

  9. Prometheus监控学习笔记

    K8S最佳实战(包括了K8S的Prometheus监控和EFK日志搜集) https://jimmysong.io/kubernetes-handbook/practice/ Prometheus-b ...

  10. 洛谷 P1199 三国游戏 题解

    每日一题 day18 打卡 Analysis 贪心 假如小A先选最大的[5,4],虽然电脑必须选一个破坏, 我们可以理解为5和4都属于小A的,假如后面未被破坏的最大值无论是和5相关还是和4相关,必然还 ...