Linux下Socket编程
- 格式:pdf
- 大小:169.23 KB
- 文档页数:13
Linux的SOCKET编程详解1. 网络中进程之间如何通信进程通信的概念最初来源于单机系统。
由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进程之间既互不干扰又协调一致工作,操作系统为进程通信提供了相应设施,如UNIX BSD有:管道(pipe)、命名管道(named pipe)软中断信号(signal)UNIX system V有:消息(message)、共享存储区(shared memory)和信号量(semaphore)等.他们都仅限于用在本机进程之间通信。
网间进程通信要解决的是不同主机进程间的相互通信问题(可把同机进程通信看作是其中的特例)。
为此,首先要解决的是网间进程标识问题。
同一主机上,不同进程可用进程号(process ID)唯一标识。
但在网络环境下,各主机独立分配的进程号不能唯一标识该进程。
例如,主机A赋于某进程号5,在B机中也可以存在5号进程,因此,“5号进程”这句话就没有意义了。
其次,操作系统支持的网络协议众多,不同协议的工作方式不同,地址格式也不同。
因此,网间进程通信还要解决多重协议的识别问题。
其实TCP/IP协议族已经帮我们解决了这个问题,网络层的―ip地址‖可以唯一标识网络中的主机,而传输层的―协议+端口‖可以唯一标识主机中的应用程序(进程)。
这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
使用TCP/IP协议的应用程序通常采用应用编程接口:UNIX BSD的套接字(socket)和UNIX System V的TLI(已经被淘汰),来实现网络进程之间的通信。
就目前而言,几乎所有的应用程序都是采用socket,而现在又是网络时代,网络中进程通信是无处不在,这就是我为什么说―一切皆s ocket‖。
TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
Linux网络编程socket错误分析socket错误码:EINTR:4阻塞的操作被取消阻塞的调用打断。
如设置了发送接收超时,就会遇到这种错误。
只能针对阻塞模式的socket。
读,写阻塞的socket时,-1返回,错误号为INTR。
另外,如果出现EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。
如果recv 的返回值为0,那表明连接已经断开,接收操作也应该结束。
ETIMEOUT:1101、操作超时。
一般设置了发送接收超时,遇到网络繁忙的情况,就会遇到这种错误。
2、服务器做了读数据做了超时限制,读时发生了超时。
3、错误被描述为“connect time out”,即“连接超时”,这种情况一般发生在服务器主机崩溃。
此时客户TCP 将在一定时间内(依具体实现)持续重发数据分节,试图从服务TCP 获得一个ACK 分节。
当最终放弃尝试后(此时服务器未重新启动),内核将会向客户进程返回ETIMEDOUT 错误。
如果某个中间路由器判定该服务器主机已经不可达,则一般会响应“destination unreachable”-“目的地不可达”的ICMP消息,相应的客户进程返回的错误是EHOSTUNREACH 或ENETUNREACH。
当服务器重新启动后,由于TCP 状态丢失,之前所有的连接信息也不存在了,此时对于客户端发来请求将回应RST。
如果客户进程对检测服务器主机是否崩溃很有必要,要求即使客户进程不主动发送数据也能检测出来,那么需要使用其它技术,如配置SO_KEEPALIVE Socket 选项,或实现某些心跳函数。
EAGAIN:1、Send返回值小于要发送的数据数目,会返回EAGAIN和EINTR。
2、recv 返回值小于请求的长度时说明缓冲区已经没有可读数据,但再读不一定会触发EAGAIN,有可能返回0表示TCP连接已被关闭。
3、当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,可以做延时后再重试.4、在Linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno 代码为11(EAGAIN),表明在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏socket的同步,不用管它,下次循环接着recv就可以。
linux⽹络编程常⽤函数详解与实例(socket--bind--listen--accept)常⽤的⽹络命令:netstat命令netstat是⽤来显⽰⽹络的连接,路由表和接⼝统计等⽹络的信息.netstat有许多的选项我们常⽤的选项是 -an ⽤来显⽰详细的⽹络状态.⾄于其它的选项我们可以使⽤帮助telnettelnet是⼀个⽤来远程控制的程序,但是我们完全可以⽤这个程序来调试我们的服务端程序的. ⽐如我们的服务器程序在监听8888端⼝,我们可以⽤telnet localhost 8888来查看服务端的状况.linux⽹络编程【参考】:⽹络函数描述和实例:int socket(int domain, int type,int protocol)domain:说明我们⽹络程序所在的主机采⽤的通讯协族(AF_UNIX和AF_INET等). AF_UNIX只能够⽤于单⼀的Unix系统进程间通信,⽽AF_INET是针对Internet的,因⽽可以允许在远程主机之间通信type:我们⽹络程序所采⽤的通讯协议(SOCK_STREAM,SOCK_DGRAM等) SOCK_STREAM表明我们⽤的是TCP协议,这样会提供按顺序的,可靠,双向,⾯向连接的⽐特流. SOCK_DGRAM 表明我们⽤的是UDP协议,这样只会提供定长的,不可靠,⽆连接的通信.protocol:由于我们指定了type,所以这个地⽅我们⼀般只要⽤0来代替就可以了socket为⽹络通讯做基本的准备.成功时返回⽂件描述符,失败时返回-1,看errno可知道出错的详细情况int bind(int sockfd, struct sockaddr *my_addr, int addrlen)sockfd:是由socket调⽤返回的⽂件描述符.addrlen:是sockaddr结构的长度.my_addr:是⼀个指向sockaddr的指针. 在中有 sockaddr的定义struct sockaddr{unisgned short as_family;char sa_data[14];};不过由于系统的兼容性,我们⼀般不⽤这个头⽂件,⽽使⽤另外⼀个结构(struct sockaddr_in) 来代替.在中有sockaddr_in的定义struct sockaddr_in{unsigned short sin_family;unsigned short int sin_port;struct in_addr sin_addr;unsigned char sin_zero[8];}我们主要使⽤Internet所以sin_family⼀般为AF_INET,sin_addr设置为INADDR_ANY表⽰可以和任何的主机通信,sin_port是我们要监听的端⼝号.sin_zero[8]是⽤来填充的. bind将本地的端⼝同socket返回的⽂件描述符捆绑在⼀起.成功是返回0,失败的情况和socket⼀样int listen(int sockfd,int backlog)sockfd:是bind后的⽂件描述符.backlog:设置请求排队的最⼤长度.当有多个客户端程序和服务端相连时, 使⽤这个表⽰可以介绍的排队长度. listen函数将bind的⽂件描述符变为监听套接字.返回的情况和bind⼀样.int accept(int sockfd, struct sockaddr *addr,int *addrlen)sockfd:是listen后的⽂件描述符.addr,addrlen是⽤来给客户端的程序填写的,服务器端只要传递指针就可以了. bind,listen和accept是服务器端⽤的函数,accept调⽤时,服务器端的程序会⼀直阻塞到有⼀个客户程序发出了连接. accept成功时返回最后的服务器端的⽂件描述符,这个时候服务器端可以向该描述符写信息了. 失败时返回-1int connect(int sockfd, struct sockaddr * serv_addr,int addrlen)sockfd:socket返回的⽂件描述符.serv_addr:储存了服务器端的连接信息.其中sin_add是服务端的地址addrlen:serv_addr的长度connect函数是客户端⽤来同服务端连接的.成功时返回0,sockfd是同服务端通讯的⽂件描述符失败时返回-1总的来说⽹络程序是由两个部分组成的--客户端和服务器端.它们的建⽴步骤⼀般是:服务器端socket-->bind-->listen-->accept客户端socket-->connect实例1:#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <netinet/in.h>#include <sys/socket.h>#include <sys/wait.h>int main(){int sockfd,new_fd;struct sockaddr_in my_addr;struct sockaddr_in their_addr;int sin_size;//建⽴TCP套接⼝if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){printf("create socket error");perror("socket");exit(1);}//初始化结构体,并绑定2323端⼝my_addr.sin_family = AF_INET;my_addr.sin_port = htons(2323);my_addr.sin_addr.s_addr = INADDR_ANY;bzero(&(my_addr.sin_zero),8);//绑定套接⼝if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1) {perror("bind socket error");exit(1);}//创建监听套接⼝if(listen(sockfd,10)==-1){perror("listen");exit(1);}//等待连接while(1){sin_size = sizeof(struct sockaddr_in);printf("server is run./n");//如果建⽴连接,将产⽣⼀个全新的套接字if((new_fd = accept(sockfd,(struct sockaddr *)&their_addr,&sin_size))==-1) {perror("accept");exit(1);}printf("accept success./n");//⽣成⼀个⼦进程来完成和客户端的会话,⽗进程继续监听if(!fork()){printf("create new thred success./n");//读取客户端发来的信息int numbytes;char buff[256];memset(buff,0,256);if((numbytes = recv(new_fd,buff,sizeof(buff),0))==-1){perror("recv");exit(1);printf("%s",buff);//将从客户端接收到的信息再发回客户端if(send(new_fd,buff,strlen(buff),0)==-1)perror("send");close(new_fd);exit(0);}close(new_fd);}close(sockfd);}#include <stdio.h>#include <stdlib.h>#include <string.h>#include <netdb.h>#include <sys/types.h>#include <sys/socket.h>int main(int argc,char *argv[]){int sockfd,numbytes;char buf[100];struct sockaddr_in their_addr;//int i = 0;//将基本名字和地址转换//he = gethostbyname(argv[1]);//建⽴⼀个TCP套接⼝if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){perror("socket");printf("create socket error.建⽴⼀个TCP套接⼝失败");exit(1);}//初始化结构体,连接到服务器的2323端⼝their_addr.sin_family = AF_INET;their_addr.sin_port = htons(2323);// their_addr.sin_addr = *((struct in_addr *)he->h_addr);inet_aton( "127.0.0.1", &their_addr.sin_addr );bzero(&(their_addr.sin_zero),8);//和服务器建⽴连接if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr))==-1) {perror("connect");exit(1);}//向服务器发送数据if(send(sockfd,"hello!socket.",6,0)==-1){perror("send");exit(1);}//接受从服务器返回的信息if((numbytes = recv(sockfd,buf,100,0))==-1){perror("recv");exit(1);}buf[numbytes] = '/0';close(sockfd); return 0;}。
Linux下的CSocket编程--server端的简单⽰例Linux下的C Socket编程(三)server端的简单⽰例经过前⾯的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的⼀个端⼝上⾯去。
绑定socket到⼀个端⼝上bind()函数可以将socket绑定到⼀个端⼝上,client可以通过向这个端⼝发起请求,端⼝对应的socket便会与client端的socket连接。
#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>int main() {int socket_desc;struct sockaddr_in server;socket_desc = socket(AF_INET, SOCK_STREAM, 0);if (-1 == socket_desc) {perror("cannot create socket");exit(1);}// 监听服务器⾃⾝server.sin_addr.s_addr = INADDR_ANY;server.sin_family = AF_INET;server.sin_port = htons(8888);// 绑定到端⼝if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {perror("cannot bind error");exit(1);}printf("bind success");close(socket_desc);return 0;}对于server.sin_addr.s_addr的更多信息可以参考通过将socket绑定到⼀个确定的端⼝上,我们接下来要做的便是接收这个端⼝下的所有数据。
Linux下C语言的socket函数解析socketsocket()我们使用系统调用socket()来获得文件描述符:#include#includeint socket(int domain,int type,int protocol);第一个参数domain设置为“AF_INET”。
第二个参数是套接口的类型:SOCK_STREAM或SOCK_DGRAM。
第三个参数设置为0。
系统调用socket()只返回一个套接口描述符,如果出错,则返回-1。
bind()一旦你有了一个套接口以后,下一步就是把套接口绑定到本地计算机的某一个端口上。
但如果你只想使用connect()则无此必要。
下面是系统调用bind()的使用方法:#include#includeintbind(int sockfd,struct sockaddr*my_addr,int addrlen);第一个参数sockfd是由socket()调用返回的套接口文件描述符。
第二个参数my_addr是指向数据结构sockaddr的指针。
数据结构sockaddr中包括了关于你的地址、端口和IP地址的信息。
第三个参数addrlen可以设置成sizeof(structsockaddr)。
下面是一个例子:#include#include#include#define MYPORT 3490main(){int sockfd;struct sockaddr_inmy_addr;sockfd=socket(AF_INET,SOCK_STREAM,0);/*do someerror checking!*/my_addr.sin_family=AF_INET;/*hostbyteorder*/my_addr.sin_port=htons(MYPORT);/*short,network byte order*/my_addr.sin_addr.s_addr=inet_addr("132.241.5.10");bzero(&(my_addr.sin_zero),8);/*zero the rest of the struct*//*don't forget your error checking for bind():*/bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr));...如果出错,bind()也返回-1。
Linux中的Socket是一种用于网络通信的编程接口,它允许进程通过网络进行数据传输。
Socket在Linux内核中的实现涉及到多个组件和原理。
1. 网络协议栈:Linux内核中的网络协议栈负责处理网络通信的各个层次,包括物理层、数据链路层、网络层和传输层。
Socket通过网络协议栈与网络进行交互。
2. 套接字数据结构:在Linux内核中,套接字(Socket)被实现为一种数据结构,用于表示网络连接。
套接字数据结构包含了连接的相关信息,如IP地址、端口号等。
3. 文件描述符:在Linux中,套接字被视为一种文件,因此每个套接字都有一个对应的文件描述符。
通过文件描述符,进程可以对套接字进行读写操作。
4. 网络设备驱动程序:Linux内核中的网络设备驱动程序负责处理网络设备的底层操作,如发送和接收数据包。
套接字通过网络设备驱动程序与网络设备进行通信。
5. 网络协议处理:当进程通过套接字发送或接收数据时,Linux内核会根据套接字的协议类型(如TCP或UDP)进行相应的协议处理。
这包括建立连接、数据分片、错误检测等操作。
6. 系统调用:在用户空间中,进程通过系统调用(如socket、bind、connect等)来创建和操作套接字。
系统调用会触发内核中相应的函数,完成套接字的创建和操作。
总的来说,Linux内核中的Socket实现涉及到网络协议栈、套接字数据结构、文件描述符、网络设备驱动程序、网络协议处理和系统调用等多个组件和原理。
这些组件和原理共同工作,使得进程能够通过套接字进行网络通信。
{"code":0,"msg":"请求出现异常","data":{}}。
实验6. Linux Socket 编程实验1、实验目的:(1) 了解TCP/IP 协议;(2) 掌握socket 编程。
2、实验设备:(1) PC 机的VMware 虚拟机运行Ubuntu Linux 系统;(2) 两机对连的网络线;(3) 带网口的测试计算机;(4) WINDOWS“SOCKET TOOL”调试工具。
3、实验内容:实现典型客户机/服务器程序中的服务器及客户机。
4、实验原理4.1 客户机/服务器工作流程使用TCP协议的客户机/服务器进程的工作过程如下图4.2 Socket 编程相关函数常用的socket函数有:socket,bind,listen,accept,connect,send,recv。
1)socket(建立连接)表头文件:#include<sys/types.h>#include<sys/socket.h>定义函数:int socket(int family,int type,int protocol);函数说明:socket()函数用来生成一个套接口描述字,也称为套接字,指定协议簇和套接口。
参数:family指定协议族,type指明字节流方式,而protocol一般为0Family的取值范围:AF_LOCALUNIX协议族AF_ROUTE路由套接口AF_INETIPv4协议AF_INET6IPv6协议AF_KEY密钥套接口参数type的取值范围:SOCK_STREAMTCP套接口SOCK_DGRAMUDP套接口SOCK_PACKET支持数据链路访问SOCK_RAM原始套接口返回值:成功返回非负描述字,失败返回负值2)bind(对socket定位)表头文件:#include<sys/types.h>#include<sys/socket.h>定义函数:Int bind(int sockfd,struct sockaddr * my_addr,int addrlen);函数说明bind()用来设置给参数sockfd的socket一个名称。
linux创建socket收发链路层报文的c语言代码引言概述:在Linux操作系统中,使用C语言编写代码可以创建socket并进行收发链路层报文的操作。
本文将详细介绍如何使用C语言编写代码来实现这一功能。
正文内容:1. socket的创建1.1. 引入必要的头文件:在C语言代码中,需要引入一些必要的头文件,如<sys/types.h>、<sys/socket.h>和<netinet/in.h>等,以便使用相关的函数和数据结构。
1.2. 创建socket:使用socket()函数可以创建一个socket,该函数需要指定协议族、套接字类型和协议类型等参数。
常用的协议族有AF_PACKET(链路层协议族)、AF_INET(IPv4协议族)和AF_INET6(IPv6协议族)等。
1.3. 设置socket选项:可以使用setsockopt()函数来设置socket的选项,如设置接收和发送缓冲区的大小等。
2. 绑定socket2.1. 创建一个用于绑定的结构体:使用struct sockaddr_ll结构体来保存链路层地址信息,包括接口索引、协议类型和目标MAC地址等。
2.2. 绑定socket:使用bind()函数将socket与特定的链路层地址绑定,以便接收和发送链路层报文。
3. 发送链路层报文3.1. 构建报文:使用C语言的数据结构和函数来构建链路层报文,包括设置目标MAC地址、源MAC地址、协议类型和数据等。
3.2. 发送报文:使用sendto()函数发送链路层报文,该函数需要指定socket、报文数据和报文长度等参数。
4. 接收链路层报文4.1. 创建一个接收缓冲区:使用malloc()函数动态分配一个足够大的缓冲区来接收链路层报文。
4.2. 接收报文:使用recvfrom()函数接收链路层报文,该函数需要指定socket、接收缓冲区和缓冲区大小等参数。
5. 关闭socket5.1. 关闭socket:使用close()函数关闭已创建的socket,释放相关资源。
C++网络编程之路——从socket开始作为一名C++程序员,网络编程是我必须要掌握的一项重要技能。
而学习网络编程,就不得不从socket说起。
今天,我就来和大家分享一下我在学习socket过程中的一些心得体会。
什么是socket?Socket,又称套接字,是一种通信机制,可以让不同主机上的进程之间进行通信。
它为网络通信提供了一组通用的接口,使得编程人员可以像读写文件一样方便地进行网络编程。
在实际的网络通信中,我们通常会涉及到三个关键概念:IP地址、端口号和通信数据。
其中,IP地址用于标识网络上的主机,端口号用于标识主机上的应用程序,而通信数据则是在网络上传输的具体内容。
大端小端问题在进行socket编程前,我们需要先了解一个重要的概念:字节序。
字节序,顾名思义就是字节的顺序,分为大端字节序和小端字节序两种。
大端字节序(Big-Endian)是指数据的高字节存储在内存的低地址,低字节存储在内存的高地址。
而小端字节序(Little-Endian)则正好相反,是指数据的高字节存储在内存的高地址,低字节存储在内存的低地址。
为什么要关注字节序呢?因为不同的CPU有不同的字节序,而网络上传输的数据都是以大端字节序进行的。
所以,在发送数据前,我们需要先将数据转换为大端字节序;而在接收到数据后,又要将其转换回小端字节序,才能正确地处理数据。
在socket编程中,我们通常会用到以下几个字节序转换函数:htons/ntohs: 用于16位短整型的主机字节序和网络字节序的转换。
htonl/ntohl: 用于32位长整型的主机字节序和网络字节序的转换。
此外,对于IP地址的转换,还有以下两个常用函数:inet_pton: 将点分十进制的IP地址转换为网络字节序。
inet_ntop: 将网络字节序的IP地址转换为点分十进制。
sockaddr结构体在socket编程中,我们经常会用到sockaddr这个结构体来表示socket地址。
linux socket状态函数-回复Linux socket状态函数是指用于获取和设置socket连接状态信息的特定函数集合。
这些函数非常重要,因为它们允许开发人员监视和控制网络连接的各个方面,从而确保网络应用程序的可靠性和稳定性。
本文将详细介绍Linux socket状态函数的使用和原理,并提供一些常见的实例和应用场景。
一、什么是socket状态函数Socket状态函数是一组用于获取和设置socket连接状态的API函数。
它们允许开发人员查询和修改socket连接的各个方面,包括连接的状态、缓冲区状态、传输层协议状态等。
通过使用这些函数,开发人员能够更好地监控和管理其网络应用程序,以确保其可靠性和稳定性。
在Linux系统中,socket状态函数主要包括以下几个关键函数:1. getsockopt:用于获取socket选项的值,如缓冲区大小、超时值等。
2. setsockopt:用于设置socket选项的值,如缓冲区大小、超时值等。
3. ioctl:用于控制socket的属性和状态,如非阻塞I/O、接收和发送缓冲区大小等。
4. poll/epoll:用于监视和等待多个socket文件描述符的状态变化,以便及时处理网络事件。
二、getsockopt函数详解getsockopt函数用于获取socket选项的值。
它的原型如下:cint getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);其中,sockfd是socket文件描述符,level表示选项所属的协议层,如SOL_SOCKET表示socket级选项,optname表示具体的选项名称,optval是用于存放选项值的缓冲区,optlen表示缓冲区的长度。
3.1)常见使用场景getsockopt函数广泛应用于网络编程中,主要用于获取和查询socket的各个状态和属性。
SOCKET网络编程:Linux下实现聊天室程序介绍:本聊天室程序在Ubuntu下,采用C语言实现,结构为Client/Server结构;服务端程序通过共享存储区存储聊天数据,并发送给每个连接的客户端;服务端程序和客户端程序都是通过父子进程分别负责发送和接收数据的,以避免数据冲撞;需按以下格式调用客户端程序:client.exe 服务端主机IP 端口号(本程序设定为:3490) 用户名(在聊天室中显示的用户名)。
程序截图://--------------------------------服务端----------------------------------------------//--------------------------------客户端1:真水无香--------------------------------------//--------------------------------客户端2:蜡笔小新--------------------------------------程序代码如下://--------------------------------server.c-------------------------------------------------- //包含工程所需的头文件#include<stdio.h>#include<stdlib.h>#include<sys/types.h>//数据类型定义#include<sys/stat.h>#include<netinet/in.h>//定义数据结构sockaddr_in#include<sys/socket.h>//提供socket函数及数据结构#include<string.h>#include<unistd.h>#include<signal.h>#include<sys/ipc.h>#include<errno.h>#include<sys/shm.h>#include<time.h>#define PERM S_IRUSR|S_IWUSR#define MYPORT 3490 //宏定义定义通信端口#define BACKLOG 10 //宏定义,定义服务程序可以连接的最大客户数量#define WELCOME "|----------Welcome to the chat room! ----------|"//宏定义,当客户端连接服务端时,想客户发送此欢迎字符串//转换函数,将int类型转换成char *类型void itoa(int i,char*string){int power,j;j=i;for(power=1;j>=10;j/=10)power*=10;for(;power>0;power/=10){*string++='0'+i/power;i%=power;}*string='\0';}//得到当前系统时间void get_cur_time(char * time_str){time_t timep;struct tm *p_curtime;char *time_tmp;time_tmp=(char *)malloc(2);memset(time_tmp,0,2);memset(time_str,0,20);time(&timep);p_curtime = localtime(&timep);strcat(time_str," (");itoa(p_curtime->tm_hour,time_tmp);strcat(time_str,time_tmp);strcat(time_str,":");itoa(p_curtime->tm_min,time_tmp);strcat(time_str,time_tmp);strcat(time_str,":");itoa(p_curtime->tm_sec,time_tmp);strcat(time_str,time_tmp);strcat(time_str,")");free(time_tmp);}//创建共享存储区key_t shm_create(){key_t shmid;//shmid = shmget(IPC_PRIVATE,1024,PERM);if((shmid = shmget(IPC_PRIVATE,1024,PERM)) == -1){fprintf(stderr,"Create Share Memory Error:%s\n\a",strerror(errno)); exit(1);}return shmid;}//端口绑定函数,创建套接字,并绑定到指定端口int bindPort(unsigned short int port){int sockfd;struct sockaddr_in my_addr;sockfd = socket(AF_INET,SOCK_STREAM,0);//创建基于流套接字my_addr.sin_family = AF_INET;//IPv4协议族my_addr.sin_port = htons(port);//端口转换my_addr.sin_addr.s_addr = INADDR_ANY;bzero(&(my_addr.sin_zero),0);if(bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr)) == -1){perror("bind");exit(1);}printf("bing success!\n");return sockfd;}int main(int argc, char *argv[]){int sockfd,clientfd,sin_size,recvbytes; //定义监听套接字、客户套接字pid_t pid,ppid; //定义父子线程标记变量char *buf, *r_addr, *w_addr, *temp, *time_str;//="\0"; //定义临时存储区struct sockaddr_in their_addr; //定义地址结构key_t shmid;shmid = shm_create(); //创建共享存储区temp = (char *)malloc(255);time_str=(char *)malloc(20);sockfd = bindPort(MYPORT);//绑定端口while(1){if(listen(sockfd,BACKLOG) == -1)//在指定端口上监听{perror("listen");exit(1);}printf("listening......\n");if((clientfd = accept(sockfd,(struct sockaddr*)&their_addr,&sin_size)) == -1)//接收客户端连接{perror("accept");exit(1);}printf("accept from:%d\n",inet_ntoa(their_addr.sin_addr));send(clientfd,WELCOME,strlen(WELCOME),0);//发送问候信息 buf = (char *)malloc(255);ppid = fork();//创建子进程if(ppid == 0){//printf("ppid=0\n");pid = fork(); //创建子进程while(1){if(pid > 0){//父进程用于接收信息memset(buf,0,255);//printf("recv\n");//sleep(1);if((recvbytes = recv(clientfd,buf,255,0)) <= 0) {perror("recv1");close(clientfd);raise(SIGKILL);exit(1);}//write buf's data to share memoryw_addr = shmat(shmid, 0, 0);memset(w_addr, '\0', 1024);strncpy(w_addr, buf, 1024);get_cur_time(time_str);strcat(buf,time_str);printf(" %s\n",buf);}else if(pid == 0){//子进程用于发送信息//scanf("%s",buf);sleep(1);r_addr = shmat(shmid, 0, 0);//printf("---%s\n",r_addr);//printf("cmp:%d\n",strcmp(temp,r_addr));if(strcmp(temp,r_addr) != 0){strcpy(temp,r_addr);get_cur_time(time_str);strcat(r_addr,time_str);//printf("discriptor:%d\n",clientfd);//if(send(clientfd,buf,strlen(buf),0) == -1)if(send(clientfd,r_addr,strlen(r_addr),0) == -1){perror("send");}memset(r_addr, '\0', 1024);strcpy(r_addr,temp);}}elseperror("fork");}}}printf("------------------------------\n");free(buf);close(sockfd);close(clientfd);return 0;}//-----------------------------client.c------------------------------------------------- //包含工程所需的头文件#include<stdio.h>#include<netinet/in.h>//定义数据结构sockaddr_in#include<sys/socket.h>//提供socket函数及数据结构#include<sys/types.h>//数据类型定义#include<string.h>#include<stdlib.h>#include<netdb.h>#include<unistd.h>#include<signal.h>#include<time.h>int main(int argc, char *argv[]){struct sockaddr_in clientaddr;//定义地址结构pid_t pid;int clientfd,sendbytes,recvbytes;//定义客户端套接字struct hostent *host;char *buf,*buf_r;if(argc < 4){printf("usage:\n");printf("%s host port name\n",argv[0]);exit(1);}host = gethostbyname(argv[1]);if((clientfd = socket(AF_INET,SOCK_STREAM,0)) == -1) //创建客户端套接字{perror("socket\n");exit(1);}//绑定客户端套接字clientaddr.sin_family = AF_INET;clientaddr.sin_port = htons((uint16_t)atoi(argv[2]));clientaddr.sin_addr = *((struct in_addr *)host->h_addr);bzero(&(clientaddr.sin_zero),0);if(connect(clientfd,(struct sockaddr *)&clientaddr,sizeof(struct sockaddr)) == -1) //连接服务端{perror("connect\n");exit(1);}buf=(char *)malloc(120);memset(buf,0,120);buf_r=(char *)malloc(100);if( recv(clientfd,buf,100,0) == -1){perror("recv:");exit(1);}printf("\n%s\n",buf);pid = fork();//创建子进程while(1){if(pid > 0){//父进程用于发送信息//get_cur_time(time_str);strcpy(buf,argv[3]);strcat(buf,":");memset(buf_r,0,100);//gets(buf_r);fgets(buf_r,100,stdin);strncat(buf,buf_r,strlen(buf_r)-1);//strcat(buf,time_str);//printf("---%s\n",buf);if((sendbytes = send(clientfd,buf,strlen(buf),0)) == -1) {perror("send\n");exit(1);}}else if(pid == 0){//子进程用于接收信息memset(buf,0,100);if(recv(clientfd,buf,100,0) <= 0){perror("recv:");close(clientfd);raise(SIGSTOP);exit(1);}printf("%s\n",buf);}elseperror("fork");}close(clientfd);return 0;}。
Linux Socket Debug方法是指在Linux系统中,利用Socket API对网络通信进行调试的方法。
通过Socket Debug,开发人员可以在通信过程中捕获和分析数据包,以检查网络连接是否正常、数据传输是否正确。
这对于排查网络故障和优化网络性能非常有帮助。
使用Socket Debug的一般步骤包括:
1.创建一个Socket,使用socket()函数创建一个Socket,指定使用TCP或
UDP协议。
2.绑定Socket,使用bind()函数将Socket与本地IP地址和端口绑定。
3.使Socket进入监听状态,等待客户端连接,使用listen()函数。
4.接受客户端的连接请求,并返回一个新的Socket,使用accept()函数。
5.从客户端接收数据,并进行分析,使用recv()函数。
6.向客户端发送数据,使用send()函数。
7.在数据传输完成后,关闭Socket,使用close()函数。
以上信息仅供参考,可以查阅关于Linux Socket Debug的文献资料以获取更全面准确的信息。
linux系统下socket的c或c++程序设计实例一、引言在Linux系统下,Socket编程是一种常用的网络通信方式。
通过Socket,我们可以轻松地在不同程序之间进行通信,实现数据的传输和共享。
本文将介绍在Linux系统下进行Socket编程的基本概念和C 或C++程序设计实例。
二、Socket编程基础1.Socket的概念:Socket是网络编程中的一种抽象概念,它代表了一个通信端点。
在Linux系统中,Socket通常是指套接字,用于应用程序之间进行通信。
2.Socket的类型:Socket有多种类型,包括流式Socket (TCP)、数据报式Socket(UDP)等。
不同的Socket类型适用于不同的通信场景。
3.Socket的建立:在使用Socket进行通信之前,需要先建立Socket连接。
这通常需要使用Socket函数来创建套接字,并指定协议类型和地址族。
三、C或C++程序设计实例以下是一个简单的C或C++程序设计实例,演示了如何使用Socket进行基本的网络通信。
```c#include<stdio.h>#include<stdlib.h>#include<string.h>#include<sys/socket.h>#include<arpa/inet.h>intmain(){intsockfd;structsockaddr_inserver_addr;charmessage[100];char*host="localhost";//服务器地址intport=8888;//服务器端口号//创建Socket对象sockfd=socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("socketcreationfailed");exit(EXIT_FAILURE);}//设置服务器地址和端口号memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(port);server_addr.sin_addr.s_addr=inet_addr(host);//连接服务器if(connect(sockfd,(structsockaddr*)&server_addr,sizeof(se rver_addr))<0){perror("connectionfailed");exit(EXIT_FAILURE);}//发送数据到服务器printf("Entermessagetosendtoserver:");fgets(message,sizeof(message),stdin);send(sockfd,message,strlen(message),0);//接收服务器响应intn=recv(sockfd,message,sizeof(message),0);if(n<0){perror("receivefailed");exit(EXIT_FAILURE);}else{printf("Serverresponse:%s",message);}//关闭Socket连接close(sockfd);return0;}```以上代码演示了如何使用Socket进行基本的网络通信,包括创建Socket对象、连接服务器、发送数据和接收响应等操作。
Linux下Socket编程的端口问题( Bind(): Address already in use )转载请注明 原文: /xl_xunzhao/archive/2008/10/23/3130037.aspx最近在开发一个Linux下的聊天软件,每次修改了源代码并再次编译运行时,常遇到下面的地使用错误:Cann't bind server socket !: Address already in use虽然用Ctrl+C强制结束了进程,但错误依然存在,用netstat -an |grep 5120和ps aux |grep 5120都还能看到刚才用Ctrl+C“强制结束”了的进程,端口还是使用中,只好每次用kill结束进程,很是麻烦。
昨天晚上无意间浏览到IBM网站上的一篇题为《 Linux 个隐患》的文章,恍然大悟,今天试了一下,果然解决问题,在此表示套接字编程中的 5感谢,也希望更多的coder看到这篇文章,避免出错。
主要代码为:现在我每次用Ctrl+C强制结束进程后,用netstat和ps都还能看到端口在使用中,但运行程序不会出现“Address already in use”的错误了,实现了端口的重用。
以下是原文中的第三个隐患--地址使用错误地址使用错误(EADDRINUSE)您可以使用 bind API 函数来绑定一个地址(一个接口和一个端口)到一个套接字端点。
可以在服务器设置中使用这个函数,以便限制可能有连接到来的接口。
也可以在客户端设置中使用这个函数,以便限制应当供出去的连接所使用的接口。
bind 最常见的用法是关联端口号和服务器,并使用通配符地址(INADDR_ANY),它允许任何接口为到来的连接所使用。
bind 普遍遭遇的问题是试图绑定一个已经在使用的端口。
该陷阱是也许没有活动的套接字存在,但仍然禁止绑定端口(bind 返回 EADDRINUSE),它由 TCP 套接字状态TIME_WAIT 引起。
从Linux源码看Socket(TCP)Client端的Connect的⽰例详解前⾔笔者⼀直觉得如果能知道从应⽤到框架再到操作系统的每⼀处代码,是⼀件Exciting的事情。
今天笔者就来从Linux源码的⾓度看下Client端的Socket在进⾏Connect的时候到底做了哪些事情。
由于篇幅原因,关于Server端的Accept源码讲解留给下次给⼤家介绍。
(基于Linux 3.10内核)⼀个最简单的Connect例⼦int clientSocket;if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {// 创建socket失败失败return -1;}......if(connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {// connect 失败return -1;}.......⾸先我们通过socket系统调⽤创建了⼀个socket,其中指定了SOCK_STREAM,⽽且最后⼀个参数为0,也就是建⽴了⼀个通常所有的TCP Socket。
在这⾥,我们直接给出TCP Socket所对应的ops也就是操作函数。
如果你想知道上图中的结构是怎么来的,可以看下笔者以前的⽂章:值得注意的是,由于socket系统调⽤操作做了如下两个代码的判断sock_map_fd|->get_unused_fd_flags|->alloc_fd|->expand_files (ulimit)|->sock_alloc_file|->alloc_file|->get_empty_filp (/proc/sys/fs/max_files)第⼀个判断,ulmit超限:int expand_files(struct files_struct *files, int nr{......if (nr >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)return -EMFILE;......}这边的判断即是ulimit的限制!在这⾥返回-EMFILE对应的描述就是"Too many open files"第⼆个判断max_files超限struct file *get_empty_filp(void){....../** 由此可见,特权⽤户可以⽆视⽂件数最⼤⼤⼩的限制!*/if (get_nr_files() >= files_stat.max_files && !capable(CAP_SYS_ADMIN)) {/** percpu_counters are inaccurate. Do an expensive check before* we go and fail.*/if (percpu_counter_sum_positive(&nr_files) >= files_stat.max_files)goto over;}......}所以在⽂件描述符超过所有进程能打开的最⼤⽂件数量限制(/proc/sys/fs/file-max)的时候会返回-ENFILE,对应的描述就是"Too many open files in system",但是特权⽤户确可以⽆视这⼀限制,如下图所⽰:connect系统调⽤我们再来看⼀下connect系统调⽤:int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen)这个系统调⽤有三个参数,那么依据规则,它肯定在内核中的源码长下⾯这个样⼦SYSCALL_DEFINE3(connect, ......笔者全⽂搜索了下,就找到了具体的实现:socket.cSYSCALL_DEFINE3(connect, int, fd, struct sockaddr __user *, uservaddr,int, addrlen){......err = sock->ops->connect(sock, (struct sockaddr *)&address, addrlen,sock->file->f_flags);......}前⾯图给出了在TCP下的sock->ops == inet_stream_ops,然后再陷⼊到更进⼀步的调⽤栈中,即下⾯的:SYSCALL_DEFINE3(connect|->inet_stream_ops|->inet_stream_connect|->tcp_v4_connect|->tcp_set_state(sk, TCP_SYN_SENT);设置状态为TCP_SYN_SENT|->inet_hash_connect|->tcp_connect⾸先,我们来看⼀下inet_hash_connect这个函数,⾥⾯有⼀个端⼝号的搜索过程,搜索不到可⽤端⼝号就会导致创建连接失败!内核能够建⽴⼀个连接也是跋涉了千⼭万⽔的!我们先看⼀下搜索端⼝号的逻辑,如下图所⽰:获取端⼝号范围⾸先,我们从内核中获取connect能够使⽤的端⼝号范围,在这⾥采⽤了Linux中的顺序锁(seqlock)void inet_get_local_port_range(int *low, int *high){unsigned int seq;do {// 顺序锁seq = read_seqbegin(&sysctl_local_ports.lock);*low = sysctl_local_ports.range[0];*high = sysctl_local_ports.range[1];} while (read_seqretry(&sysctl_local_ports.lock, seq));}顺序锁事实上就是结合内存屏障等机制的⼀种乐观锁,主要依靠⼀个序列计数器。
linux socket状态函数Linux Socket状态函数用于获取或设置Socket的状态信息,通过这些函数可以获取Socket的连接状态、接收和发送队列的状态等。
本文将为您详细介绍常用的Linux Socket状态函数,内容分为以下几个部分:1. 概述2. 获取Socket状态3. 设置Socket状态4. 示例应用5. 总结1. 概述在Linux中,Socket是一种通用的网络编程接口,用于实现网络通信。
Socket使用一套状态来描述不同的网络连接以及连接中传输的数据。
通过Socket状态函数,我们可以获取或设置Socket的状态信息,以便了解连接状态、流量控制等信息,或者进行操作。
2. 获取Socket状态首先,我们来介绍获取Socket状态的函数。
常用的获取Socket状态的函数有以下几个:- getsockname:用于获取本地Socket的地址信息;- getpeername:用于获取远程Socket的地址信息;- isfdtype:用于检查给定文件描述符是否是给定类型的Socket。
getsockname函数的原型如下:cint getsockname(int sockfd, struct sockaddr *addr, socklen_t*addrlen);该函数用于获取本地Socket的地址信息,参数sockfd为Socket文件描述符,addr为指向sockaddr结构体的指针,用于存储获取的地址信息,addrlen为addr的长度。
函数执行成功后,返回0,失败返回-1。
getpeername函数的原型如下:cint getpeername(int sockfd, struct sockaddr *addr, socklen_t*addrlen);该函数用于获取远程Socket的地址信息,参数含义与getsockname函数类似。
同样地,函数执行成功后返回0,失败返回-1。
linux 本地socket通信原理Linux本地socket通信原理一、概述在Linux操作系统中,本地socket通信是一种进程间通信的方式,它允许位于同一台计算机上的进程之间进行数据交换。
本地socket 通信是一种高效、可靠的通信机制,被广泛应用于各种场景,如客户端-服务器模型、进程间数据传递等。
二、本地socket的基本概念1. SocketSocket是一种抽象的通信端点,用于进程间的通信。
在本地socket 通信中,每个进程都有一个或多个socket,一个socket可以用于发送和接收数据。
2. 本地socket本地socket是指位于同一台计算机上的两个进程之间的通信机制。
它通过文件系统中的文件来表示,即每个本地socket都与一个文件关联。
三、本地socket通信的流程1. 创建socket本地socket通信的第一步是创建socket。
在Linux中,可以使用socket()系统调用来创建一个本地socket。
创建成功后,系统会返回一个文件描述符,该文件描述符用于后续的通信操作。
2. 绑定socket创建socket后,需要将其绑定到一个特定的文件上。
这个文件可以是已存在的文件,也可以是新创建的文件。
绑定socket的目的是为了让其他进程可以通过该文件找到这个socket。
3. 监听连接如果一个进程希望接收其他进程的连接请求,那么它需要将socket 设置为监听状态。
这可以通过listen()系统调用来实现。
4. 接受连接一旦socket处于监听状态,其他进程就可以通过connect()系统调用来连接到该socket。
被连接的进程可以通过accept()系统调用来接受连接请求,并返回一个新的socket用于后续的通信。
5. 数据交换一旦建立了连接,两个进程就可以通过send()和recv()系统调用来进行数据交换。
其中,send()用于发送数据,recv()用于接收数据。
6. 关闭连接当通信结束后,可以通过close()系统调用来关闭socket。
linux sock_raw原始套接字编程sock_raw原始套接字编程能够接收到本机网卡上的数据帧或数据包,对与监听网络的流量和分析是很有作用的.一共能够有3种方式创建这种socket1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))过时了,不要用啊明白得一下SOCK_RAW的原理, 比如网卡收到了一个14+20+8+100+4 的udp的以太网数据帧.第一,网卡对该数据帧进行硬过滤(依照网卡的模式不同会有不同的动作,若是设置了promisc混杂模式的话,那么不做任何过滤直接交给下一层输入例程,不然非本机mac或广播mac会被直接抛弃).依照上面的例子,若是成功的话,会进入ip 输入例程.可是在进入ip输入例程之前,系统会检查系统中是不是有通过socket(AF_PACKET, SOCK_RAW, ..)创建的套接字.若是有的话而且协议相符,在那个例子中确实是需要ETH_P_IP或ETH_P_ALL类型.系统就给每一个如此的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.第二,进入了ip输入例程(ip层会对该数据包进行软过滤,确实是检查校验或抛弃非本机ip或广播ip的数据包等,具体要参考源代码),例子中确实是若是成功的话会进入udp输入例程.可是在交给udp输入例程之前,系统会检查系统中是不是有通过socket(AF_INET, SOCK_RAW, ..)创建的套接字.若是有的话而且协议相符,在那个例子中确实是需要IPPROTO_UDP类型.系统就给每一个如此的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.最后,进入udp输入例程 ...ps:若是校验和犯错的话,内核会直接抛弃该数据包的.而可不能拷贝给sock_raw 的套接字,因为校验和都犯错了,数据确信有问题的包括所有信息都没成心义了.进一步分析他们的能力.1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);能:该套接字能够接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的确实是20+8+100.不能:不能收到非发往本地ip的数据包(ip软过滤会抛弃这些不是发往本机ip的数据包).不能:不能收到从本机发送出去的数据包.发送的话需要自己组织tcp udp icmp等头部.能够setsockopt来自己包装ip 头部这种套接字用来写个ping程序比较适合2. socket(PF_PACKET, SOCK_RAW, htons(x));那个套接字比较壮大,创建这种套接字能够监听网卡上的所有数据帧.从上面看确实是20+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判定过了,对程序来讲没有任何意义了.能: 接收发往本地mac的数据帧能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)协议类型一共有四个ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧ETH_P_ARP 0x806 只同意发往本机mac的arp类型的数据帧ETH_P_ARP 0x8035 只同意发往本机mac的rarp类型的数据帧ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情形下,会接收到非发往本地mac的数据帧)发送的时候需要自己组织整个以太网数据帧.所有相关的地址利用struct sockaddr_ll 而不是struct sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机械,对方的地址需要利用struct sockaddr_ll.这种socket大小通吃,强悍下面是一段相关的代码:perror("iotcl()");return -1;}return 0;}int unset_promisc(char *interface, int fd) {struct ifreq ifr;strcpy(ifr.ifr_name, interface);if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {perror("iotcl()");return -1;}ifr.ifr_flags &= ~IFF_PROMISC;if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {perror("iotcl()");return -1;}return 0;}3. socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))那个最好不要用,终归我不用...总结利用方式: 1.只想收到发往本机某种协议的ip数据包的话用第一种就足够了2. 更多的详细的内容请利用第二种.包括ETH_P_ALL参数和混杂模式都能够使它的能力不断的增强.ps:很多自己的方式.虚拟机测试环境.有错欢迎指出交流qq:110024218我写的ping#include "arpa/inet.h"#include "signal.h"#include "sys/time.h"extern int errno;int sockfd;struct sockaddr_in addr; //peer addrchar straddr[128]; //peer addr ip(char*)char sendbuf[2048];char recvbuf[2048];int sendnum;int recvnum;int datalen = 30;unsigned short my_cksum(unsigned short *data, int len) {int result = 0;for(int i=0; i<len/2; i++) {result += *data;data++;}while(result >> 16)result = (result&0xffff) + (result>>16);return ~result;}void tv_sub(struct timeval* recvtime, const struct timeval* sendtime) { int sec = recvtime->tv_sec - sendtime->tv_sec;int usec = recvtime->tv_usec - sendtime->tv_usec;if(usec >= 0) {recvtime->tv_sec = sec;recvtime->tv_usec = usec;} else {recvtime->tv_sec = sec-1;recvtime->tv_usec = -usec;}}void send_icmp() {struct icmp* icmp = (struct icmp*)sendbuf;icmp->icmp_type = ICMP_ECHO;icmp->icmp_code = 0;icmp->icmp_cksum = 0;icmp->icmp_id = getpid(); //needn't use htons() call, because peer networking kernel didn't handle this data and won't make different meanings(bigdian litteldian)icmp->icmp_seq = ++sendnum; //needn't use hotns() call too.gettimeofday((struct timeval*)icmp->icmp_data, NULL);int len = 8+datalen;icmp->icmp_cksum = my_cksum((unsigned short*)icmp, len);int retval = sendto(sockfd, sendbuf, len, 0, (struct sockaddr*)&addr, sizeof(addr));if(retval == -1){perror("sendto()");exit(-1);} else {// printf("send icmp request to %s(%d) bytes\n", straddr, len);}}void recv_icmp() {struct timeval *sendtime;struct timeval recvtime;for(;;) {int n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, 0, 0);if(n == -1) {if(errno == EINTR)continue;else {perror("recvfrom()");exit(-1);}} else {gettimeofday(&recvtime, NULL);struct ip *ip = (struct ip*)recvbuf;if(ip->ip_src.s_addr != addr.sin_addr.s_addr) {// printf("ip_src is not : %s\n", straddr);continue;}struct icmp *icmp = (struct icmp*)(recvbuf + ((ip->ip_hl)<<2));if(icmp->icmp_id != getpid()) {// printf("icmp_id is not :%d\n", getpid());continue;}recvnum++;sendtime = (structtimeval*)icmp->icmp_data;tv_sub(&recvtime, sendtime);printf("imcp echo from %s(%dbytes)\tttl=%d\tseq=%d\ttime=%d.%06d s\n", straddr, n, ip->ip_ttl, icmp->icmp_seq, _sec, _usec);}}}void catch_sigalrm(int signum) {send_icmp();alarm(1);}void catch_sigint(int signum) {printf("\nPing statics:send %d packets, recv %d packets, %d%% lost...\n", sendnum, recvnum, (int)((float)(sendnum-recvnum)/sendnum)*100);exit(0);}int main(int argc, char **argv) {if(argc != 2) {printf("please use format: ping hostname\n");exit(-1);}sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);if(sockfd == -1) {perror("socket()");return -1;}/*int sendbufsize = 180;socklen_t sendbufsizelen = sizeof(sendbufsize);if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sendbufsize, sendbufsizelen) == -1)perror("setsockopt()");int recvbufsize;socklen_t recvbufsizelen;if(getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbufsize, &recvbufsizelen) == -1)perror("getsockopt()");*/memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;int retval = inet_pton(AF_INET, argv[1], &addr.sin_addr);if(retval == -1 || retval == 0) {struct hostent* host = gethostbyname(argv[1]);if(host == NULL) {fprintf(stderr, "gethostbyname(%s):%s\n", argv[1], strerror(errno));exit(-1);}/*if(host->h_name != NULL)printf("hostent.h_name:%s\n", host->h_name);if(host->h_aliases != NULL && *(host->h_aliases) != NULL)printf("hostent.h_aliases:%s\n", *(host->h_aliases));printf("hostent.h_addrtype:%d\n",host->h_addrtype);printf("hostent.h_length:%d\n", host->h_length);*/if(host->h_addr_list != NULL && *(host->h_addr_list) != NULL) {strncpy((char*)&addr.sin_addr,*(host->h_addr_list), 4);inet_ntop(AF_INET, *(host->h_addr_list), straddr, sizeof(straddr));}printf("Ping address:%s(%s)\n\n", host->h_name, straddr);} else {strcpy(straddr, argv[1]);printf("Ping address:%s(%s)\n\n", straddr, straddr);}struct sigaction sa1;memset(&sa1, 0, sizeof(sa1));sa1.sa_handler = catch_sigalrm;sigemptyset(&sa1.sa_mask);sa1.sa_flags = 0;if(sigaction(SIGALRM, &sa1, NULL) == -1)perror("sigaction()");struct sigaction sa2;memset(&sa2, 0, sizeof(sa2));sa2.sa_handler = catch_sigint;sigemptyset(&sa2.sa_mask);sa2.sa_flags = 0;if(sigaction(SIGINT, &sa2, NULL) == -1)perror("sigaction()");。
Linux下Socket编程什么是SocketSocket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程式员能够用他们来研发TCP/IP网络上的应用程式。
要学Internet上的TCP/IP网络编程,必须理解Socket接口。
Socket接口设计者最先是将接口放在Unix操作系统里面的。
假如了解Unix系统的输入和输出的话,就很容易了解Socket了。
网络的Socket数据传输是一种特别的I/O,Socket也是一种文档描述符。
Socket也具备一个类似于打开文档的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
常用的Socket类型有两种:流式Socket (SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。
流式是一种面向连接的Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。
Socket建立为了建立Socket,程式能够调用Socket函数,该函数返回一个类似于文档描述符的句柄。
socket函数原型为:int socket(int domain,int type,int protocol);domain指明所使用的协议族,通常为PF_INET,表示互连网协议族(TCP/IP协议族);type参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket (SOCK_RAW),允许程式使用低层协议;protocol通常赋值"0"。
Socket()调用返回一个整型socket描述符,您能够在后面的调用使用他。
Socket描述符是个指向内部数据结构的指针,他指向描述符表入口。
调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。
Socket执行体为您管理描述符表。
两个网络程式之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。
Socket数据结构中包含这五种信息。
Socket配置通过socket调用返回一个socket描述符后,在使用socket进行网络传输以前,必须配置该socket。
面向连接的socket客户端通过调用Connect函数在socket数据结构中保存本地和远端信息。
无连接socket的客户端和服务端连同面向连接socket的服务端通过调用bind函数来配置本地信息。
Bind函数将socket和本机上的一个端口相关联,随后您就能够在该端口监听服务请求。
Bind函数原型为:int bind(int sockfd,struct sockaddr*my_addr,int addrlen);Sockfd是调用socket函数返回的socket描述符,my_addr是个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;addrlen常被配置为sizeof(struct sockaddr)。
struct sockaddr结构类型是用来保存socket信息的:struct sockaddr{unsigned short sa_family;/*地址族,AF_xxx*/char sa_data[14];/*14字节的协议地址*/};sa_family一般为AF_INET,代表Internet(TCP/IP)地址族;sa_data则包含该socket的IP地址和端口号。
另外更有一种结构类型:struct sockaddr_in{short int sin_family;/*地址族*/unsigned short int sin_port;/*端口号*/ struct in_addr sin_addr;/*IP地址*/unsigned char sin_zero[8];/*填充0以保持和struct sockaddr同样大小*/};这个结构更方便使用。
sin_zero用来将sockaddr_in结构填充到和struct sockaddr同样的长度,能够用bzero()或memset()函数将其置为零。
指向sockaddr_in的指针和指向sockaddr的指针能够相互转换,这意味着假如一个函数所需参数类型是sockaddr时,您能够在函数调用的时候将一个指向sockaddr_in的指针转换为指向sockaddr的指针;或相反。
使用bind函数时,能够用下面的赋值实现自动获得本机IP地址和随机获取一个没有被占用的端口号:my_addr.sin_port=0;/*系统随机选择一个未被使用的端口号*/ my_addr.sin_addr.s_addr=INADDR_ANY;/*填入本机IP地址*/通过将my_addr.sin_port置为0,函数会自动为您选择一个未占用的端口来使用。
同样,通过将my_addr.sin_addr.s_addr置为INADDR_ANY,系统会自动填入本机IP地址。
注意在使用bind函数是需要将sin_port和sin_addr转换成为网络字节优先顺序;而sin_addr则无需转换。
电脑数据存储有两种字节优先顺序:高位字节优先和低位字节优先。
Internet上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在Internet上传输数据时就需要进行转换,否则就会出现数据不一致。
下面是几个字节顺序转换函数:·htonl():把32位值从主机字节序转换成网络字节序·htons():把16位值从主机字节序转换成网络字节序·ntohl():把32位值从网络字节序转换成主机字节序·ntohs():把16位值从网络字节序转换成主机字节序Bind()函数在成功被调用时返回0;出现错误时返回"-1"并将errno置为相应的错误号。
需要注意的是,在调用bind函数时一般不要将端口号置为小于1024的值,因为1到1024是保留端口号,您能够选择大于1024中的任何一个没有被占用的端口号。
连接建立面向连接的客户程式使用Connect函数来配置socket并和远端服务器建立一个TCP连接,其函数原型为:int connect(int sockfd,struct sockaddr*serv_addr,int addrlen);Sockfd是socket函数返回的socket描述符;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是远端地质结构的长度。
Connect函数在出现错误时返回-1,并且配置errno为相应的错误码。
进行客户端程式设计无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口和服务器建立连接并无需关心,socket执行体为您的程式自动选择一个未被占用的端口,并通知您的程式数据什么时候到打断口。
Connect函数启动和远端主机的直接连接。
只有面向连接的客户程式使用socket时才需要将此socket和远端主机相连。
无连接协议从不建立直接连接。
面向连接的服务器也从不启动一个连接,他只是被动的在协议端口监听客户的请求。
Listen函数使socket处于被动的监听模式,并为该socket 建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程式处理他们。
int listen(int sockfd,int backlog);Sockfd是Socket系统调用返回的socket描述符;backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()他们(参考下文)。
Backlog对队列中等待服务的请求的数目进行了限制,大多数系统缺省值为20。
假如一个服务请求到来时,输入队列已满,该socket将拒绝连接请求,客户将收到一个出错信息。
当出现错误时listen函数返回-1,并置相应的errno错误码。
accept()函数让服务器接收客户的连接请求。
在建立好输入队列后,服务器就调用accept函数,然后睡眠并等待客户的连接请求。
int accept(int sockfd,void*addr,int*addrlen);sockfd是被监听的socket描述符,addr通常是个指向sockaddr_in 变量的指针,该变量用来存放提出连接请求服务的主机的信息(某台主机从某个端口发出该请求);addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。
出现错误时accept函数返回-1并置相应的errno值。
首先,当accept函数监控的socket 收到连接请求时,socket执行体将建立一个新的socket,执行体将这个新socket和请求连接进程的地址联系起来,收到服务请求的初始socket仍能够继续在以前的socket上监听,同时能够在新的socket描述符上进行数据传输操作。
数据传输Send()和recv()这两个函数用于面向连接的socket上进行数据传输。
Send()函数原型为:int send(int sockfd,const void *msg,int len,int flags);Sockfd是您想用来传输数据的socket描述符;msg是个指向要发送数据的指针;Len是以字节为单位的数据的长度;flags一般情况下置为0(关于该参数的用法可参照man手册)。
Send()函数返回实际上发送出的字节数,可能会少于您希望发送的数据。
在程式中应该将send()的返回值和欲发送的字节数进行比较。
当send()返回值和len不匹配时,应该对这种情况进行处理。
char*msg="Hello!";int len,bytes_sent;……len=strlen(msg);bytes_sent=send(sockfd,msg,len,0);……recv()函数原型为:int recv(int sockfd,void*buf,int len,unsigned int flags);Sockfd是接受数据的socket描述符;buf是存放接收数据的缓冲区;len是缓冲的长度。
Flags也被置为0。
Recv()返回实际上接收的字节数,当出现错误时,返回-1并置相应的errno值。
Sendto()和recvfrom()用于在无连接的数据报socket方式下进行数据传输。
由于本地socket并没有和远端机器建立连接,所以在发送数据时应指明目的地址。