linux下用socket通信,有TCP、UDP两种协议,网上的很多教程把两个混在了一起,或者只讲其中一种。现在我把自己这两天研究的成果汇总下来,写了一个完整的,适合初学者参考,也方便自己以后查阅。

首先讲什么是socket,不喜欢理论的可以略过。

Berkeley套接字应用程序接口(API)包括了一个用C语言写成的应用程序开发库,主要用于实现进程间通讯,在计算机网络通讯方面被广泛使用。(来自 wikipedia socket )

下面介绍一下常用的socket API(也来自 wikipedia socket)

这个列表是一个Berkeley套接字API库提供的函数或者方法的概要:

  • socket() 创建一个新的确定类型的套接字,类型用一个整型数值标识,并为它分配系统资源。
  • bind() 一般用于服务器端,将一个套接字与一个套接字地址结构相关联,比如,一个指定的本地端口和IP地址。
  • listen() 用于服务器端,使一个绑定的TCP套接字进入监听状态。
  • connect() 用于客户端,为一个套接字分配一个自由的本地端口号。 如果是TCP套接字的话,它会试图获得一个新的TCP连接。
  • accept() 用于服务器端。 它接受一个从远端客户端发出的创建一个新的TCP连接的接入请求,创建一个新的套接字,与该连接相应的套接字地址相关联。
  • send()recv(),或者write()read(),或者recvfrom()sendto(), 用于往/从远程套接字发送和接受数据。
  • close() 用于系统释放分配给一个套接字的资源。 如果是TCP,连接会被中断。
  • gethostbyname()gethostbyaddr() 用于解析主机名和地址。
  • select() 用于修整有如下情况的套接字列表: 准备读,准备写或者是有错误。
  • poll() 用于检查套接字的状态。 套接字可以被测试,看是否可以写入、读取或是有错误。
  • getsockopt() 用于查询指定的套接字一个特定的套接字选项的当前值。
  • setsockopt() 用于为指定的套接字设定一个特定的套接字选项。

更多的细节如下给出。

[编辑]socket()

socket() 为通讯创建一个端点,为套接字返回一个文件描述符。 socket() 有三个参数:

  • domain 为创建的套接字指定协议集。 例如:

    • PF_INET 表示IPv4网络协议
    • PF_INET6 表示IPv6
    • PF_UNIX 表示本地套接字(使用一个文件)
  • type 如下:
    • SOCK_STREAM (可靠的面向流服务或流套接字)
    • SOCK_DGRAM (数据报文服务或者数据报文套接字)
    • SOCK_SEQPACKET (可靠的连续数据包服务)
    • SOCK_RAW (在网络层之上的原始协议)。
  • protocol 指定实际使用的传输协议。 最常见的就是IPPROTO_TCPIPPROTO_SCTPIPPROTO_UDPIPPROTO_DCCP。这些协议都在<netinet/in.h>中有详细说明。 如果该项为“0”的话,即根据选定的domain和type选择使用缺省协议。

如果发生错误,函数返回值为-1。 否则,函数会返回一个代表新分配的描述符的整数。

原型:
int socket(int domain, int type, int protocol)。

[编辑]bind()

bind() 为一个套接字分配地址。当使用socket()创建套接字后,只赋予其所使用的协议,并未分配地址。在接受其它主机的连接前,必须先调用bind()为套接字分配一个地址。bind()有三个参数:

  • sockfd, 表示使用bind函数的套接字描述符
  • my_addr, 指向sockaddr结构(用于表示所分配地址)的指针
  • addrlen, 用socklen_t字段指定了sockaddr结构的长度

如果发生错误,函数返回值为-1,否则为0。

原型
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

[编辑]listen()

当socket和一个地址绑定之后,listen()函数会开始监听可能的连接请求。然而,这只能在有可靠数据流保证的时候使用,例如:数据类型(SOCK_STREAM,SOCK_SEQPACKET)。

listen()函数需要两个参数:

  • sockfd, 一个socket的描述符.
  • backlog, 一个决定监听队列大小的整数,当有一个连接请求到来,就会进入此监听队列,当队列满后,新的连接请求会返回错误。当请求被接受,返回 0。反之,错误返回 -1。

原型:

int listen(int sockfd, int backlog);

[编辑]accept()

当应用程序监听来自其他主机的面对数据流的连接时,通过事件(比如Unix select()系统调用)通知它。必须用 accept()函数初始化连接。 Accept() 为每个连接创立新的套接字并从监听队列中移除这个连接。它使用如下参数:

  • sockfd,监听的套接字描述符
  • cliaddr, 指向sockaddr 结构体的指针,客户机地址信息。
  • addrlen,指向 socklen_t的指针,确定客户机地址结构体的大小 。

返回新的套接字描述符,出错返回-1。进一步的通信必须通过这个套接字。

Datagram 套接字不要求用accept()处理,因为接收方可能用监听套接字立即处理这个请求。

函数原型:
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

[编辑]connect()

connect()系统调用为一个套接字设置连接,参数有文件描述符和主机地址。

某些类型的套接字是无连接的,大多数是UDP协议。对于这些套接字,连接时这样的:默认发送和接收数据的主机由给定的地址确定,可以使用 send()和 recv()。 返回-1表示出错,0表示成功。

函数原型:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

TCP socket通信

服务器端流程如下:

1.创建serverSocket

2.初始化 serverAddr(服务器地址)

3.将socket和serverAddr 绑定 bind

4.开始监听 listen

5.进入while循环,不断的accept接入的客户端socket,进行读写操作write和read

6.关闭serverSocket

客户端流程:

1.创建clientSocket

2.初始化 serverAddr

3.链接到服务器 connect

4.利用write和read 进行读写操作

5.关闭clientSocket

具体实现代码如下

#server.c(TCP)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#define SRVPORT 10005
#define CONNECT_NUM 5
#define MAX_NUM 80
int main()
{int serverSock=-1,clientSock=-1;struct sockaddr_in serverAddr;serverSock=socket(AF_INET,SOCK_STREAM,0);if(serverSock<0){printf("socket creation failed\n");exit(-1);}printf("socket create successfully.\n");memset(&serverAddr,0,sizeof(serverAddr));serverAddr.sin_family=AF_INET;serverAddr.sin_port=htons((u_short) SRVPORT);serverAddr.sin_addr.s_addr=htons(INADDR_ANY);if(bind(serverSock,&serverAddr,sizeof(struct sockaddr_in))==-1){printf("Bind error.\n");exit(-1);}printf("Bind successful.\n");if(listen(serverSock,10)==-1){printf("Listen error!\n");}printf("Start to listen!\n");char revBuf[MAX_NUM]={0};char sedBuf[MAX_NUM]={0};while(1){clientSock=accept(serverSock,NULL,NULL);while(1){if(read(clientSock,revBuf,MAX_NUM)==-1){printf("read error.\n");}else{printf("Client:%s\n",revBuf);}if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0){strcpy(sedBuf,"Goodbye,my dear client!");}else{strcpy(sedBuf,"Hello Client.");}if(write(clientSock,sedBuf,sizeof(sedBuf))==-1){printf("Send error!\n");}printf("Me(Server):%s\n",sedBuf);if(strcmp(revBuf,"Quit")==0||strcmp(revBuf,"quit")==0){break;}bzero(revBuf,sizeof(revBuf));bzero(sedBuf,sizeof(sedBuf));}close(clientSock);}close(serverSock);return 0;
}
#client.c(TCP)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <string.h>
#define SRVPORT 10005
#define CONNECT_NUM 5
#define MAX_NUM 80
int main()
{int clientSock=-1;struct sockaddr_in serverAddr;clientSock=socket(AF_INET,SOCK_STREAM,0);if(clientSock<0){printf("socket creation failed\n");exit(-1);}printf("socket create successfully.\n");memset(&serverAddr,0,sizeof(serverAddr));serverAddr.sin_family=AF_INET;serverAddr.sin_port=htons((u_short) SRVPORT);serverAddr.sin_addr.s_addr=htons(INADDR_ANY);if(connect(clientSock,&serverAddr,sizeof(struct sockaddr_in))<0){printf("Connect error.\n");exit(-1);}printf("Connect successful.\n");char sedBuf[MAX_NUM]={0};char revBuf[MAX_NUM]={0};while(gets(sedBuf)!=-1){if(write(clientSock,sedBuf,strlen(sedBuf))==-1){printf("send error!\n");}printf("Me(Client):%s\n",sedBuf);bzero(sedBuf,sizeof(sedBuf));if(read(clientSock,revBuf,MAX_NUM)==-1){printf("rev error!\n");}printf("Sever:%s\n",revBuf);if(strcmp(revBuf,"Goodbye,my dear client!")==0)break;bzero(revBuf,sizeof(revBuf));}close(clientSock);return 0;
}

UDP协议不能保证数据通信的可靠性,但是开销更低,编起来也更加简单

服务器流程:

1.创建serverSocket

2.设置服务器地址 serverAddr

3.将serverSocket和serverAddr绑定 bind

4.开始进行读写 sendto和recvfrom

5.关闭serverSocket

客户端流程

1.创建clientSocket

2.设置服务器地址 serverAddr

3.可选 设置clientAddr并和clientSocket(一般不用绑定)

4.进行发送操作 sendto

5.关闭clientSocket

具体代码如下:

#server.c(UDP)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>//for sockaddr_in
#include <arpa/inet.h>//for socket
int main()
{int fd=socket(AF_INET,SOCK_DGRAM,0);if(fd==-1){perror("socket create error!\n");exit(-1);}printf("socket fd=%d\n",fd);struct sockaddr_in addr;addr.sin_family=AF_INET;addr.sin_port=htons(6666);addr.sin_addr.s_addr=inet_addr("127.0.0.1");int r;r=bind(fd,(struct sockaddr*)&addr,sizeof(addr));if(r==-1){printf("Bind error!\n");close(fd);exit(-1);}printf("Bind successfully.\n");char buf[255];struct sockaddr_in from;socklen_t len;len=sizeof(from);while(1){r=recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&from,&len);if(r>0){buf[r]=0;printf("The message received for %s is :%s\n",inet_ntoa(from.sin_addr),buf);}else{break;}}close(fd);return 0;
}
#client.c(UDP)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>//for sockaddr_in
#include <arpa/inet.h>//for socket
int main()
{int fd=socket(AF_INET,SOCK_DGRAM,0);if(fd==-1){perror("socket create error!\n");exit(-1);}printf("socket fd=%d\n",fd);struct sockaddr_in addr_to;//目标服务器地址addr_to.sin_family=AF_INET;addr_to.sin_port=htons(6666);addr_to.sin_addr.s_addr=inet_addr("127.0.0.1");struct sockaddr_in addr_from;addr_from.sin_family=AF_INET;addr_from.sin_port=htons(0);//获得任意空闲端口addr_from.sin_addr.s_addr=htons(INADDR_ANY);//获得本机地址r=bind(fd,(struct sockaddr*)&addr_from,sizeof(addr_from));int r; if(r==-1){printf("Bind error!\n");close(fd);exit(-1);}printf("Bind successfully.\n");char buf[255];int len;while(1){r=read(0,buf,sizeof(buf)); if(r<0){break;}len=sendto(fd,buf,r,0,(struct sockaddr*)&addr_to,sizeof(addr_to)); if(len==-1){printf("send falure!\n");}else{printf("%d bytes have been sended successfully!\n",len);}}close(fd);return 0;
}

以上代码均经过测试(Ubuntu12.04),可以运行。有疑问,可以发电邮到ladd.cn@gmail.com

参考文章

1.wikipedia socket http://zh.wikipedia.org/wiki/Socket

2.TCP socket 之linux实现http://os.51cto.com/art/201001/179878.htm

本文由ladd原创,欢迎转载,但请注明出处:

http://www.cnblogs.com/ladd/archive/2012/06/25/2560888.html

转载于:https://www.cnblogs.com/ladd/archive/2012/06/25/2560888.html

Linux 下socket通信终极指南(附TCP、UDP完整代码)相关推荐

  1. linux socket ip层配置,Linux下Socket通信(TCP实现)

    近期在做的项目中,涉及到了进程间数据传输,系统的原本实现是通过管道,但是原有的实现中两个进程是在同一台机器,而且两个进程的关系为父子关系,而我们要做的是将其中一个进程移植到服务器上,因此两个进程要分开 ...

  2. Linux下Socket通信中非阻塞connect、select、recv 和 recvfrom、send和sendto大致讲解,附带非租塞connect代码、MSG_NOSIGNAL

    linux中send函数MSG_NOSIGNAL异常消息 在服务器端用ctrl+c 来结束服务器接收进程来模拟服务器宕机的情况,结束服务 socket 进程之后,服务端自然关闭进程,可是 client ...

  3. Linux下socket通信和epoll

    上一篇博客用多线程实现服务端和多个客户端的通信,但是在实际应用中如果服务端有高并发的需求,多线程并不是一个好选择. 实现高并发的一种方法是IO多路复用,也就是select,poll,epoll等等. ...

  4. linux下socket通信,server和client简单例子

    #include "metafile.h" #include <string.h> #include <ctype.h>//定义全局变量 TrackerLi ...

  5. 【Linux】一篇文章搞定 CPP模拟实现TCP协议下socket通信

    CPP模拟实现TCP协议下socket通信 1. TCP 编程流程图 2. 数据收发阶段使用的API 2.1 send接口 2.2 recv接口 3. 两个队列 4. 总结TCP 编程双端流程 5. ...

  6. Linux下Socket编程之TCP应用

    现在,我们用前面所构建的socket类,重新设计<Linux下Socket编程之TCP Server端>中echo的服务器,然后设计客户端程序. echo服务器的工作原理很简单: 1.接收 ...

  7. Socket通信总结(附C++实现)

    Socket通信总结(附C++实现) 2012年08月16日 ⁄ C++⁄ 共 5870字 ⁄ 评论数 1 ⁄ 被围观 10,061+ 分享到: 文章目录 [隐藏] 一.Socket是什么 二.一些基 ...

  8. 一文了解linux下socket编程

    一文了解linux下socket编程 文章目录 一文了解linux下socket编程 1 网络编程的相关简述 1.1 引言 1.2 Tcp和Udp简介 1.3 TCP三次握手和四次挥手 1.4 网络编 ...

  9. linux下socket和MySQL数据库编程

    一.基本socket函数 Linux系统是通过提供套接字(socket)来进行网络编程的.网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符.socket也有一个类似于打 开 ...

最新文章

  1. 解决事件多次绑定,执行多次问题
  2. 【深度学习前沿】一文回顾深度学习十年发展
  3. tensorflow就该这么学--6(多层神经网络)
  4. 训练集、验证集、测试集区分
  5. negative binomial(Pascal) distribution —— 负二项式分布(帕斯卡分布)
  6. 挑战程序设计竞赛:Ants
  7. 数字逻辑电路试题及答案
  8. Aspose.word保存PDF时进行授权访问设置
  9. 【老生谈算法】matlab实现FFT算法源码——FFT算法
  10. PHP快速入门指南-基础篇一
  11. 索尼koov机器人比赛_搭上“想象”去成长 索尼KOOV可编程教育机器人评测
  12. 江西师范大学计算机学院,江西师范大学计算机信息工程学院导师介绍:叶继华...
  13. 【工作流】工作流快速入门
  14. 光纤HDMI线不再脆弱,开博尔铠装HDMI光纤抗拖拽
  15. 基于FPGA的频率计设计
  16. Apache Flink介绍、架构、原理以及实现
  17. XCode8新的功能
  18. c++ 读写 .flo 文件
  19. 机器视觉领域专业词汇中英对照
  20. 清理SONY D-NE10 的线控器

热门文章

  1. IT程序员必知!TCP/IP为什么会有这么多的致命漏洞?
  2. 简单梳理帆软报表即决策系统的登录步骤 二
  3. python udp通信_Python网络编程(三)
  4. python廖老师_Python3.5-20190518-廖老师-自我笔记-面向对象
  5. java事件 socket_java中有关socket通信的学习笔记
  6. android hook底层代码_Java-Hook技术-入门实践(反射、动态代理)-Hook拦截通知(当前App/Context)...
  7. mysql workbench中文设置 mac系统,win系统,linux系统
  8. html表格怎么改圆角边框,Table元素设置圆角border
  9. think php5目录结构,目录结构 · ThinkPHP5.1完全开发手册 · 看云
  10. 2018年第九届蓝桥杯【C++省赛B组】【第八题:日志统计】尺取法