㈠ 在linux系统下编写一个socket程序
我给你一个更高端的
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>
#define M 8888
struct qun
{
int cy[5];
};
struct haoyou
{
int py[5];
};
struct ri
{
int geren[5];
};
int main()
{
struct qun group[5]={0};
struct haoyou pyd[5]={0};
struct ri ziji[5]={0};
printf("server onil\n");
printf("this port is %d\n",M);
fd_set rest;
int servsock=socket(AF_INET,SOCK_STREAM,0);
if(-1==servsock)
{
perror("socket perror");
return 1;
}
struct sockaddr_in servaddr;
struct sockaddr_in clienaddr;
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(M);
int a=bind(servsock,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(-1==a)
{
perror("bind perror\n");
return 1;
}
int b=listen(servsock,5);
if(-1==b)
{
perror("listen perror");
return 1;
}
FD_ZERO(&rest);
char sendbuff[1024];
char recvbuff[1024];
int n;
int qw;
int r[10];
int min[1024]={0};
int i=0;
int iWho[10]={0};
int mabi[1024]={0};
char mingzi[10][50]={0};
char caonima[1024]={0};
int wocao[10]={0};
n=sizeof(clienaddr);
struct timeval time;
while(1)
{
time.tv_sec = 0;
time.tv_usec = 470;
FD_SET(servsock,&rest);
FD_SET(fileno(stdin),&rest);
select(servsock+1,&rest,NULL,NULL,&time);
if(FD_ISSET(servsock,&rest))
{
r[i]=accept(servsock,(struct sockaddr *)&clienaddr,&n);
if(r[i]==-1)
{
i--;
}
i++;
}
int p;
for(p=0;p<i;p++)
{
FD_SET(r[p],&rest);
}
int ret=select(r[i-1]+1,&rest,NULL,NULL,&time);
if(ret<0)
{
printf("socket error\n");
continue;
}
else
{
int u;
for(u=0;u<i;u++
㈡ linux socket是什么意思
基于Linux的SOCKET编程。
㈢ Linux下的socket是怎么回事,如何利用其实现局域网内的数据处理
//服务端server.c
#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>
#define SERVPORT 6000 /*服务器监听端口号 */
#define BACKLOG 10 /* 最大同时连接请求数 */
#define MAXDATASIZE 100
main()
{
char buf[MAXDATASIZE];
int sockfd,client_fd; /*sock_fd:监听socket;client_fd:数据传输socket */
struct sockaddr_in my_addr; /* 本机地址信息 */
struct sockaddr_in remote_addr; /* 客户端地址信息 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket创建出错!");
exit(1);
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
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出错!");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen出错!");
exit(1);
}
while(1)
{
sin_size = sizeof(struct sockaddr_in);
if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1)
{
perror("accept出错");
continue;
}
printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork())
{ /* 子进程代码段 */
if ((recvbytes=recv(client_fd, buf, MAXDATASIZE, 0)) ==-1)
{
perror("recv出错!");
close(client_fd);
exit(0);
}
buf[recvbytes] = '\0';
printf("from client Received: %s",buf);
if (send(client_fd, "thanks!\n", 8, 0) == -1)
perror("send出错!");
close(client_fd);
exit(0);
}
close(client_fd);
}
}
//客户端client.c
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVPORT 6000
#define MAXDATASIZE 100
main(int argc, char *argv[])
{
int sockfd, recvbytes;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
if (argc < 2)
{
fprintf(stderr,"Please enter the server's hostname!\n");
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL)
{
herror("gethostbyname出错!");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket创建出错!");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
{
perror("connect出错!");
exit(1);
}
if (send(sockfd, "hello!\n", 7, 0) == -1)
{
perror("send出错!");
exit(1);
}
if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1)
{
perror("recv出错!");
exit(1);
}
buf[recvbytes] = '\0';
printf("Received: %s",buf);
close(sockfd);
}
㈣ linux下 socket函数的返回值代表什么
int socket;domain指明所使用的协议族,通常为PF_INET,表示互联网协议族;type参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket,允许程序使用低层协议;protocol通常赋值"0"。
Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。 Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。
调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。 Socket执行体为你管理描述符表。
(4)linux本地socket扩展阅读:
支持下述类型描述:
SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。
SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。
SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()和recv()传送数据。当会话结束后,调用close()。带外数据根据规定用send()和recv()来接收。
㈤ linux socket 设置从哪个网络设备发送数据 SO
原因: 1、 因为服务器是时时在监听有没有客户端的连接,如果服务器不绑定IP和端口的话,客户端上线的时候怎么连到服务器呢,所以服务器要绑定IP和端口,而客户端就不需要了,客户端上线是主动向服务器发出请求的,因为服务器已经绑定了IP和端口,所以客户端上线的就向这个IP和端口发出请求,这时因为客户开始发数据了(发上线请求),系统就给客户端分配一个随机端口,这个端口和客户端的IP会随着上线请求一起发给服务器,服务收到上线请求后就可以从中获起发此请求的客户的IP和端口,接下来服务器就可以利用获起的IP和端口给客户端回应消息了。 2、采用UDP通信 1)若有客户端和服务器之分的程序,创建sock后即可在该socket上用recvfrom/sendto方法发送接受数据了,因为客户端只需要用sendto发送数据到指定的地址,当然若是bind了,程序也没什么问题,区别就是系统用默认自动bind()指定你自己的socket参数地址(特别是在指定特定端口的UDP对等通信)只是这种情况没有这样用的。 那UDP服务器是怎么知道客户端的IP地址和UDP端口? 一般来说有两种方式: 一种是客户端发消息显式地告诉服务器IP地址和端口,消息内容就包括IP地址和UDP端口。 另外一种就是隐式的,服务器从收到的包的头部中得到包的源IP地址和端口。 2)若是没有客户端和服务器之分的程序,即自己指定特定端口的UDP对等通信,则客户端和服务器都需要bind()IP地址和端口了。 通常udp服务端根本不需要知道客户端的socket,它直接建立一个socket用于发送即可,udp通信的关键只在于IP和端口。 多个客户端如果需要点到点分发,必须给服务端socket循环设置每个客户端的IP并发出,但更常用的是广播分发,服务端socket设定一个X.X.X.255的广播地址并始终向它发送,每个客户端建立的socket只需要绑定这个广播地址便可以收到。 客户端用不用bind 的区别 无连接的socket的客户端和服务端以及面向连接socket的服务端通过调用bind函数来配置本地信息。使用bind函数时,通过将my_addr.sin_port置为0,函数会自动为你选择一个未占用的端口来使用。 Bind()函数在成功被调用时返回0;出现错误时返回"-1"并将errno置为相应的错误号。需要注意的是,在调用bind函数时一般不要将端口号置为小于1024的值,因为1到1024是保留端口号,你可以选择大于1024中的任何一个没有被占用的端口号。 有连接的socket客户端通过调用Connect函数在socket数据结构中保存本地和远端信息,无须调用bind(),因为这种情况下只需知道目的机器的IP地址,而客户通过哪个端口与服务器建立连接并不需要关心,socket执行体为你的程序自动选择一个未被占用的端口,并通知你的程序数据什么时候打开端口。(当然也有特殊情况,linux系统中rlogin命令应当调用bind函数绑定一个未用的保留端口号,还有当客户端需要用指定的网络设备接口和端口号进行通信等等) 总之: 1.需要在建连前就知道端口的话,需要 bind 2.需要通过指定的端口来通讯的话,需要 bind 具体到上面那两个程序,本来用的是TCP,客户端就不用绑定端口了,绑定之后只能运行一个client 的程序,是属于自己程序中人为设定的障碍,而从服务器那边得到的客户机连接端口号(是系统自动分配的)与这边客户机绑定的端口号根本是不相关的,所以客户 绑定也就失去了意义。 注意: 一个端口可以用于多个连接(比如多个客户端连接服务器的同一端口)。但是在同一个操作系统上,即服务器和客户端都是本机上,多个客户端去连接服务器,只有第一个客户端的连接会被接收,第二个客户端的连接请求不会被接收。 首先,服务器和客户端都可以bind,bind并不是服务器的专利。 客户端进程bind端口: 由进程选择一个端口去连服务器,(如果默认情况下,调用bind函数时,内核指定的端口是同一个,那么运行多个调用了bind 的client 程序,会出现端口被占用的错误)注意这里的端口是客户端的端口。如果不分配就表示交给内核去选择一个可用端口。 客户端进程bind IP地址:相当于为发送出去的IP数据报分配了源IP地址,但交给进程分配IP地址的时候(就是这样写明了bind IP地址的时候)这个IP地址必须是主机的一个接口,不能分配一个不存在的IP。如果不分配就表示由内核根据所用的输出接口来选择源IP地址。 一般情况下客户端是不用调用bind函数的,一切都交给内核搞定! 服务端进程bind端口:基本是必须要做的事情,比如一个服务器启动时(比如freebsd),它会一个一个的捆绑众所周知的端口来提供服务,同样,如果bind了一个端口就表示我这个服务器会在这个端口提供一些“特殊服务”。 服务端进程bind IP地址:目的是限制了服务端进程创建的socket只接受那些目的地为此IP地址的客户链接,一般一个服务器程序里都有 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 只是针对IP4,IP6代码不太一样 这样一句话,意思就是:我不指定客户端的IP,随便连,来者不拒! 总之只要你bind时候没有指定哪一项(置为0),内核会帮你选择。
㈥ Linux怎么使用ss命令查看系统的socket状态
ss是Socket Statistics的缩写。顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容。但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信息,而且比netstat更快速更高效。当服务器的socket连接数量变得非常大时,无论是使用netstat命令还是直接cat /proc/net/tcp,执行速度都会很慢。可能你不会有切身的感受,但请相信我,当服务器维持的连接达到上万个的时候,使用netstat等于浪费 生命,而用ss才是节省时间。天下武功唯快不破。ss快的秘诀在于,它利用到了TCP协议栈中tcp_diag。tcp_diag是一个用于分析统计的模块,可以获得Linux 内核中第一手的信息,这就确保了ss的快捷高效。当然,如果你的系统中没有tcp_diag,ss也可以正常运行,只是效率会变得稍慢。(但仍然比 netstat要快。)
命令格式:
ss [参数]
ss [参数] [过滤]
2.命令功能:
ss(Socket Statistics的缩写)命令可以用来获取 socket统计信息,此命令输出的结果类似于 netstat输出的内容,但它能显示更多更详细的 TCP连接状态的信息,且比 netstat 更快速高效。它使用了 TCP协议栈中 tcp_diag(是一个用于分析统计的模块),能直接从获得第一手内核信息,这就使得 ss命令快捷高效。在没有 tcp_diag,ss也可以正常运行。
3.命令参数:
-h, --help 帮助信息
-V, --version 程序版本信息
-n, --numeric 不解析服务名称
-r, --resolve 解析主机名
-a, --all 显示所有套接字(sockets)
-l, --listening 显示监听状态的套接字(sockets)
-o, --options 显示计时器信息
-e, --extended 显示详细的套接字(sockets)信息
-m, --memory 显示套接字(socket)的内存使用情况
-p, --processes 显示使用套接字(socket)的进程
-i, --info 显示 TCP内部信息
-s, --summary 显示套接字(socket)使用概况
-4, --ipv4 仅显示IPv4的套接字(sockets)
-6, --ipv6 仅显示IPv6的套接字(sockets)
-0, --packet 显示 PACKET 套接字(socket)
-t, --tcp 仅显示 TCP套接字(sockets)
-u, --udp 仅显示 UCP套接字(sockets)
-d, --dccp 仅显示 DCCP套接字(sockets)
-w, --raw 仅显示 RAW套接字(sockets)
-x, --unix 仅显示 Unix套接字(sockets)
-f, --family=FAMILY 显示 FAMILY类型的套接字(sockets),FAMILY可选,支持 unix, inet, inet6, link, netlink
-A, --query=QUERY, --socket=QUERY
QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]
-D, --diag=FILE 将原始TCP套接字(sockets)信息转储到文件
-F, --filter=FILE 从文件中都去过滤器信息
FILTER := [ state TCP-STATE ] [ EXPRESSION ]
4.使用实例:
实例1:显示TCP连接
命令:ss -t -a
输出:
代码如下:
[root@localhost ~]# ss -t -a
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 0 127.0.0.1:smux *:*
LISTEN 0 0 *:3690 *:*
LISTEN 0 0 *:ssh *:*
ESTAB 0 0 192.168.120.204:ssh 10.2.0.68:49368
[root@localhost ~]#
实例2:显示 Sockets 摘要
命令:ss -s
输出:
代码如下:
[root@localhost ~]# ss -s
Total: 34 (kernel 48)
TCP: 4 (estab 1, closed 0, orphaned 0, synrecv 0, timewait 0/0), ports 3《/p》 《p》Transport Total IP IPv6
* 48 - -
RAW 0 0 0
UDP 5 5 0
TCP 4 4 0
INET 9 9 0
FRAG 0 0 0
[root@localhost ~]#
说明:列出当前的established, closed, orphaned and waiting TCP sockets
实例3:列出所有打开的网络连接端口
命令:ss -l
输出:
代码如下:
[root@localhost ~]# ss -l
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 127.0.0.1:smux *:*
0 0 *:3690 *:*
0 0 *:ssh *:*
[root@localhost ~]#
实例4:查看进程使用的socket
命令:ss -pl
输出:
代码如下:
[root@localhost ~]# ss -pl
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 127.0.0.1:smux *:* users:((“snmpd”,2716,8))
0 0 *:3690 *:* users:((“svnserve”,3590,3))
0 0 *:ssh *:* users:((“sshd”,2735,3))
[root@localhost ~]#
实例5:找出打开套接字/端口应用程序
命令:ss -lp | grep 3306
输出:
代码如下:
[root@localhost ~]# ss -lp|grep 1935
0 0 *:1935 *:* users:((“fmsedge”,2913,18))
0 0 127.0.0.1:19350 *:* users:((“fmsedge”,2913,17))
[root@localhost ~]# ss -lp|grep 3306
0 0 *:3306 *:* users:((“mysqld”,2871,10))
[root@localhost ~]#
实例6:显示所有UDP Sockets
命令:ss -u -a
输出:
代码如下:
[root@localhost ~]# ss -u -a
State Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0 0 127.0.0.1:syslog *:*
UNCONN 0 0 *:snmp *:*
ESTAB 0 0 192.168.120.203:39641 10.58.119.119:domain
[root@localhost ~]#
实例7:显示所有状态为established的SMTP连接
命令:ss -o state established ‘( dport = :smtp or sport = :smtp )’
输出:
代码如下:
[root@localhost ~]# ss -o state established ‘( dport = :smtp or sport = :smtp )’
Recv-Q Send-Q Local Address:Port Peer Address:Port
[root@localhost ~]#
实例8:显示所有状态为Established的HTTP连接
命令:ss -o state established ‘( dport = :http or sport = :http )’
输出:
代码如下:
[root@localhost ~]# ss -o state established ‘( dport = :http or sport = :http )’
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 75.126.153.214:2164 192.168.10.42:http
[root@localhost ~]#
实例9:列举出处于 FIN-WAIT-1状态的源端口为 80或者 443,目标网络为 193.233.7/24所有 tcp套接字
命令:ss -o state fin-wait-1 ‘( sport = :http or sport = :https )’ dst 193.233.7/24
实例10:用TCP 状态过滤Sockets:
命令:
代码如下:
ss -4 state FILTER-NAME-HERE
ss -6 state FILTER-NAME-HERE
输出:
代码如下:
[root@localhost ~]#ss -4 state closing
Recv-Q Send-Q Local Address:Port Peer Address:Port
1 11094 75.126.153.214:http 192.168.10.42:4669
说明:
FILTER-NAME-HERE 可以代表以下任何一个:
代码如下:
established
syn-sent
syn-recv
fin-wait-1
fin-wait-2
time-wait
closed
close-wait
last-ack
listen
closing
all : 所有以上状态
connected : 除了listen and closed的所有状态
synchronized :所有已连接的状态除了syn-sent
bucket : 显示状态为maintained as minisockets,如:time-wait和syn-recv.
big : 和bucket相反。
实例11:匹配远程地址和端口号
命令:
代码如下:
ss dst ADDRESS_PATTERN
ss dst 192.168.1.5
ss dst 192.168.119.113:http
ss dst 192.168.119.113:smtp
ss dst 192.168.119.113:443
输出:
代码如下:
[root@localhost ~]# ss dst 192.168.119.113
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:20229
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:61056
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:61623
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:60924
ESTAB 0 0 192.168.119.103:16050 192.168.119.113:43701
ESTAB 0 0 192.168.119.103:16073 192.168.119.113:32930
ESTAB 0 0 192.168.119.103:16073 192.168.119.113:49318
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:3844
[root@localhost ~]# ss dst 192.168.119.113:http
State Recv-Q Send-Q Local Address:Port Peer Address:Port
[root@localhost ~]# ss dst 192.168.119.113:3844
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:3844
[root@localhost ~]#
实例12:匹配本地地址和端口号
命令:
代码如下:
ss src ADDRESS_PATTERN
ss src 192.168.119.103
ss src 192.168.119.103:http
ss src 192.168.119.103:80
ss src 192.168.119.103:smtp
ss src 192.168.119.103:25
输出:
代码如下:
[root@localhost ~]# ss src 192.168.119.103:16021
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:63054
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:62894
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:63055
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:2274
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:44784
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:7233
ESTAB 0 0 192.168.119.103:16021 192.168.119.103:58660
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:44822
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56737
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:57487
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56736
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:64652
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56586
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:64653
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56587
[root@localhost ~]#
实例13:将本地或者远程端口和一个数比较
命令:
代码如下:
ss dport OP PORT
ss sport OP PORT
输出:
代码如下:
[root@localhost ~]# ss sport = :http
[root@localhost ~]# ss dport = :http
[root@localhost ~]# ss dport \》 :1024
[root@localhost ~]# ss sport \》 :1024
[root@localhost ~]# ss sport \《 :32000
[root@localhost ~]# ss sport eq :22
[root@localhost ~]# ss dport != :22
[root@localhost ~]# ss state connected sport = :http
[root@localhost ~]# ss \( sport = :http or sport = :https \)
[root@localhost ~]# ss -o state fin-wait-1 \( sport = :http or sport = :https \) dst 192.168.1/24
说明:
ss dport OP PORT 远程端口和一个数比较;ss sport OP PORT 本地端口和一个数比较。
OP 可以代表以下任意一个:
《= or le : 小于或等于端口号
》= or ge : 大于或等于端口号
== or eq : 等于端口号
!= or ne : 不等于端口号
《 or gt : 小于端口号
》 or lt : 大于端口号
实例14:ss 和 netstat 效率对比
命令:
代码如下:
time netstat -at
time ss
输出:
代码如下:
[root@localhost ~]# time ss
real 0m0.739s
user 0m0.019s
sys 0m0.013s
[root@localhost ~]#
[root@localhost ~]# time netstat -at
real 2m45.907s
user 0m0.063s
sys 0m0.067s
[root@localhost ~]#
说明:
用time 命令分别获取通过netstat和ss命令获取程序和概要占用资源所使用的时间。在服务器连接数比较多的时候,netstat的效率完全没法和ss比。
㈦ Linux下Socket编程 怎样实现客户端之间互相通信
网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
下面用Socket实现一个windows下的c语言socket通信例子,这里我们客户端传递一个字符串,服务器端进行接收。
【服务器端】
#include"stdafx.h"
#include<stdio.h>
#include<winsock2.h>
#include<winsock2.h>
#defineSERVER_PORT5208//侦听端口
voidmain()
{
WORDwVersionRequested;
WSADATAwsaData;
intret,nLeft,length;
SOCKETsListen,sServer;//侦听套接字,连接套接字
structsockaddr_insaServer,saClient;//地址信息
char*ptr;//用于遍历信息的指针
//WinSock初始化
wVersionRequested=MAKEWORD(2,2);//希望使用的WinSockDLL的版本
ret=WSAStartup(wVersionRequested,&wsaData);
if(ret!=0)
{
printf("WSAStartup()failed! ");
return;
}
//创建Socket,使用TCP协议
sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sListen==INVALID_SOCKET)
{
WSACleanup();
printf("socket()faild! ");
return;
}
//构建本地地址信息
saServer.sin_family=AF_INET;//地址家族
saServer.sin_port=htons(SERVER_PORT);//注意转化为网络字节序
saServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//使用INADDR_ANY指示任意地址
//绑定
ret=bind(sListen,(structsockaddr*)&saServer,sizeof(saServer));
if(ret==SOCKET_ERROR)
{
printf("bind()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//关闭套接字
WSACleanup();
return;
}
//侦听连接请求
ret=listen(sListen,5);
if(ret==SOCKET_ERROR)
{
printf("listen()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//关闭套接字
return;
}
printf("Waitingforclientconnecting! ");
printf("Tips:Ctrl+ctoquit! ");
//阻塞等待接受客户端连接
while(1)//循环监听客户端,永远不停止,所以,在本项目中,我们没有心跳包。
{
length=sizeof(saClient);
sServer=accept(sListen,(structsockaddr*)&saClient,&length);
if(sServer==INVALID_SOCKET)
{
printf("accept()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//关闭套接字
WSACleanup();
return;
}
charreceiveMessage[5000];
nLeft=sizeof(receiveMessage);
ptr=(char*)&receiveMessage;
while(nLeft>0)
{
//接收数据
ret=recv(sServer,ptr,5000,0);
if(ret==SOCKET_ERROR)
{
printf("recv()failed! ");
return;
}
if(ret==0)//客户端已经关闭连接
{
printf("Clienthasclosedtheconnection ");
break;
}
nLeft-=ret;
ptr+=ret;
}
printf("receivemessage:%s ",receiveMessage);//打印我们接收到的消息。
}
//closesocket(sListen);
//closesocket(sServer);
//WSACleanup();
}
【客户端】
#include"stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
#defineSERVER_PORT5208//侦听端口
voidmain()
{
WORDwVersionRequested;
WSADATAwsaData;
intret;
SOCKETsClient;//连接套接字
structsockaddr_insaServer;//地址信息
char*ptr;
BOOLfSuccess=TRUE;
//WinSock初始化
wVersionRequested=MAKEWORD(2,2);//希望使用的WinSockDLL的版本
ret=WSAStartup(wVersionRequested,&wsaData);
if(ret!=0)
{
printf("WSAStartup()failed! ");
return;
}
//确认WinSockDLL支持版本2.2
if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
printf("InvalidWinSockversion! ");
return;
}
//创建Socket,使用TCP协议
sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sClient==INVALID_SOCKET)
{
WSACleanup();
printf("socket()failed! ");
return;
}
//构建服务器地址信息
saServer.sin_family=AF_INET;//地址家族
saServer.sin_port=htons(SERVER_PORT);//注意转化为网络节序
saServer.sin_addr.S_un.S_addr=inet_addr("192.168.1.127");
//连接服务器
ret=connect(sClient,(structsockaddr*)&saServer,sizeof(saServer));
if(ret==SOCKET_ERROR)
{
printf("connect()failed! ");
closesocket(sClient);//关闭套接字
WSACleanup();
return;
}
charsendMessage[]="hellothisisclientmessage!";
ret=send(sClient,(char*)&sendMessage,sizeof(sendMessage),0);
if(ret==SOCKET_ERROR)
{
printf("send()failed! ");
}
else
printf("clientinfohasbeensent!");
closesocket(sClient);//关闭套接字
WSACleanup();
}
㈧ linux socket 怎么处理大量的数据
1、 引言
Linux的兴起可以说是Internet创造的一个奇迹。Linux作为一个完全开放其原代码的免费的自由软件,兼容了各种UNIX标准(如POSIX、UNIX System V 和 BSD UNIX 等)的多用户、多任务的具有复杂内核的操作系统。在中国,随着Internet的普及,一批主要以高等院校的学生和ISP的技术人员组成的Linux爱好者队伍已经蓬勃成长起来。越来越多的编程爱好者也逐渐酷爱上这个优秀的自由软件。本文介绍了Linux下Socket的基本概念和函数调用。
2、 什么是Socket
Socket(套接字)是通过标准的UNIX文件描述符和其它程序通讯的一个方法。每一个套接字都用一个半相关描述:{协议,本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述:{协议,本地地址、本地端口、远程地址、远程端口},每一个套接字都有一个本地的由操作系统分配的唯一的套接字号。
3、 Socket的三种类型
(1) 流式Socket(SOCK_STREAM)
流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序的。
(2) 数据报Socket(SOCK_DGRAM)
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠、无差错。它使用数据报协议UDP
(3) 原始Socket
原始套接字允许对底层协议如IP或ICMP直接访问,它功能强大但使用较为不便,主要用于一些协议的开发。
4、 利用套接字发送数据
1、 对于流式套接字用系统调用send()来发送数据。
2、 对于数据报套接字,则需要自己先加一个信息头,然后调用sendto()函数把数据发送出去。
5、 Linux中Socket的数据结构
(1) struct sockaddr { //用于存储套接字地址
unsigned short sa_family;//地址类型
char sa_data[14]; //14字节的协议地址
};
(2) struct sockaddr_in{ //in 代表internet
short int sin_family; //internet协议族
unsigned short int sin_port;//端口号,必须是网络字节顺序
struct in_addr sin_addr;//internet地址,必须是网络字节顺序
unsigned char sin_zero;//添0(和struct sockaddr一样大小
};
(3) struct in_addr{
unsigned long s_addr;
};
6、 网络字节顺序及其转换函数
(1) 网络字节顺序
每一台机器内部对变量的字节存储顺序不同,而网络传输的数据是一定要统一顺序的。所以对内部字节表示顺序与网络字节顺序不同的机器,一定要对数据进行转换,从程序的可移植性要求来讲,就算本机的内部字节表示顺序与网络字节顺序相同也应该在传输数据以前先调用数据转换函数,以便程序移植到其它机器上后能正确执行。真正转换还是不转换是由系统函数自己来决定的。
(2) 有关的转换函数
* unsigned short int htons(unsigned short int hostshort):
主机字节顺序转换成网络字节顺序,对无符号短型进行操作4bytes
* unsigned long int htonl(unsigned long int hostlong):
主机字节顺序转换成网络字节顺序,对无符号长型进行操作8bytes
* unsigned short int ntohs(unsigned short int netshort):
网络字节顺序转换成主机字节顺序,对无符号短型进行操作4bytes
* unsigned long int ntohl(unsigned long int netlong):
网络字节顺序转换成主机字节顺序,对无符号长型进行操作8bytes
注:以上函数原型定义在netinet/in.h里
7、 IP地址转换
有三个函数将数字点形式表示的字符串IP地址与32位网络字节顺序的二进制形式的IP地址进行转换
(1) unsigned long int inet_addr(const char * cp):该函数把一个用数字和点表示的IP地址的字符串转换成一个无符号长整型,如:struct sockaddr_in ina
ina.sin_addr.s_addr=inet_addr("202.206.17.101")
该函数成功时:返回转换结果;失败时返回常量INADDR_NONE,该常量=-1,二进制的无符号整数-1相当于255.255.255.255,这是一个广播地址,所以在程序中调用iner_addr()时,一定要人为地对调用失败进行处理。由于该函数不能处理广播地址,所以在程序中应该使用函数inet_aton()。
(2)int inet_aton(const char * cp,struct in_addr * inp):此函数将字符串形式的IP地址转换成二进制形式的IP地址;成功时返回1,否则返回0,转换后的IP地址存储在参数inp中。
(3) char * inet_ntoa(struct in-addr in):将32位二进制形式的IP地址转换为数字点形式的IP地址,结果在函数返回值中返回,返回的是一个指向字符串的指针。
8、 字节处理函数
Socket地址是多字节数据,不是以空字符结尾的,这和C语言中的字符串是不同的。Linux提供了两组函数来处理多字节数据,一组以b(byte)开头,是和BSD系统兼容的函数,另一组以mem(内存)开头,是ANSI C提供的函数。
以b开头的函数有:
(1) void bzero(void * s,int n):将参数s指定的内存的前n个字节设置为0,通常它用来将套接字地址清0。
(2) void b(const void * src,void * dest,int n):从参数src指定的内存区域拷贝指定数目的字节内容到参数dest指定的内存区域。
(3) int bcmp(const void * s1,const void * s2,int n):比较参数s1指定的内存区域和参数s2指定的内存区域的前n个字节内容,如果相同则返回0,否则返回非0。
注:以上函数的原型定义在strings.h中。
以mem开头的函数有:
(1) void * memset(void * s,int c,size_t n):将参数s指定的内存区域的前n个字节设置为参数c的内容。
(2) void * memcpy(void * dest,const void * src,size_t n):功能同b(),区别:函数b()能处理参数src和参数dest所指定的区域有重叠的情况,memcpy()则不能。
(4) int memcmp(const void * s1,const void * s2,size_t n):比较参数s1和参数s2指定区域的前n个字节内容,如果相同则返回0,否则返回非0。
注:以上函数的原型定义在string.h中。
9、 基本套接字函数
(1) socket()
#include< sys/types.h>
#include< sys/socket.h>
int socket(int domain,int type,int protocol)
参数domain指定要创建的套接字的协议族,可以是如下值:
AF_UNIX //UNIX域协议族,本机的进程间通讯时使用
AF_INET //Internet协议族(TCP/IP)
AF_ISO //ISO协议族
参数type指定套接字类型,可以是如下值:
SOCK_STREAM //流套接字,面向连接的和可靠的通信类型
SOCK_DGRAM //数据报套接字,非面向连接的和不可靠的通信类型
SOCK_RAW //原始套接字,只对Internet协议有效,可以用来直接访问IP协议
参数protocol通常设置成0,表示使用默认协议,如Internet协议族的流套接字使用TCP协议,而数据报套接字使用UDP协议。当套接字是原始套接字类型时,需要指定参数protocol,因为原始套接字对多种协议有效,如ICMP和IGMP等。
Linux系统中创建一个套接字的操作主要是:在内核中创建一个套接字数据结构,然后返回一个套接字描述符标识这个套接字数据结构。这个套接字数据结构包含连接的各种信息,如对方地址、TCP状态以及发送和接收缓冲区等等,TCP协议根据这个套接字数据结构的内容来控制这条连接。
(2) 函数connect()
#include< sys/types.h>
#include< sys/socket.h>
int connect(int sockfd,struct sockaddr * servaddr,int addrlen)
参数sockfd是函数socket返回的套接字描述符;参数servaddr指定远程服务器的套接字地址,包括服务器的IP地址和端口号;参数addrlen指定这个套接字地址的长度。成功时返回0,否则返回-1,并设置全局变量为以下任何一种错误类型:ETIMEOUT、ECONNREFUSED、EHOSTUNREACH或ENETUNREACH。
在调用函数connect之前,客户机需要指定服务器进程的套接字地址。客户机一般不需要指定自己的套接字地址(IP地址和端口号),系统会自动从1024至5000的端口号范围内为它选择一个未用的端口号,然后以这个端口号和本机的IP地址填充这个套接字地址。
客户机调用函数connect来主动建立连接。这个函数将启动TCP协议的3次握手过程。在建立连接之后或发生错误时函数返回。连接过程可能出现的错误情况有:
(1) 如果客户机TCP协议没有接收到对它的SYN数据段的确认,函数以错误返回,错误类型为ETIMEOUT。通常TCP协议在发送SYN数据段失败之后,会多次发送SYN数据段,在所有的发送都高中失败之后,函数以错误返回。
注:SYN(synchronize)位:请求连接。TCP用这种数据段向对方TCP协议请求建立连接。在这个数据段中,TCP协议将它选择的初始序列号通知对方,并且与对方协议协商最大数据段大小。SYN数据段的序列号为初始序列号,这个SYN数据段能够被确认。当协议接收到对这个数据段的确认之后,建立TCP连接。
(2) 如果远程TCP协议返回一个RST数据段,函数立即以错误返回,错误类型为ECONNREFUSED。当远程机器在SYN数据段指定的目的端口号处没有服务进程在等待连接时,远程机器的TCP协议将发送一个RST数据段,向客户机报告这个错误。客户机的TCP协议在接收到RST数据段后不再继续发送SYN数据段,函数立即以错误返回。
注:RST(reset)位:表示请求重置连接。当TCP协议接收到一个不能处理的数据段时,向对方TCP协议发送这种数据段,表示这个数据段所标识的连接出现了某种错误,请求TCP协议将这个连接清除。有3种情况可能导致TCP协议发送RST数据段:(1)SYN数据段指定的目的端口处没有接收进程在等待;(2)TCP协议想放弃一个已经存在的连接;(3)TCP接收到一个数据段,但是这个数据段所标识的连接不存在。接收到RST数据段的TCP协议立即将这条连接非正常地断开,并向应用程序报告错误。
(3) 如果客户机的SYN数据段导致某个路由器产生“目的地不可到达”类型的ICMP消息,函数以错误返回,错误类型为EHOSTUNREACH或ENETUNREACH。通常TCP协议在接收到这个ICMP消息之后,记录这个消息,然后继续几次发送SYN数据段,在所有的发送都告失败之后,TCP协议检查这个ICMP消息,函数以错误返回。
注:ICMP:Internet 消息控制协议。Internet的运行主要是由Internet的路由器来控制,路由器完成IP数据包的发送和接收,如果发送数据包时发生错误,路由器使用ICMP协议来报告这些错误。ICMP数据包是封装在IP数据包的数据部分中进行传输的,其格式如下:
类型
码
校验和
数据
0 8 16 24 31
类型:指出ICMP数据包的类型。
代码:提供ICMP数据包的进一步信息。
校验和:提供了对整个ICMP数据包内容的校验和。
ICMP数据包主要有以下类型:
(1) 目的地不可到达:A、目的主机未运行;B、目的地址不存在;C、路由表中没有目的地址对应的条目,因而路由器无法找到去往目的主机的路由。
(2) 超时:路由器将接收到的IP数据包的生存时间(TTL)域减1,如果这个域的值变为0,路由器丢弃这个IP数据包,并且发送这种ICMP消息。
(3) 参数出错:当IP数据包中有无效域时发送。
(4) 重定向:将一条新的路径通知主机。
(5) ECHO请求、ECHO回答:这两条消息用语测试目的主机是否可以到达。请求者向目的主机发送ECHO请求ICMP数据包,目的主机在接收到这个ICMP数据包之后,返回ECHO回答ICMP数据包。
(6) 时戳请求、时戳回答:ICMP协议使用这两种消息从其他机器处获得其时钟的当前时间。
调用函数connect的过程中,当客户机TCP协议发送了SYN数据段的确认之后,TCP状态由CLOSED状态转为SYN_SENT状态,在接收到对SYN数据段的确认之后,TCP状态转换成ESTABLISHED状态,函数成功返回。如果调用函数connect失败,应该用close关闭这个套接字描述符,不能再次使用这个套接字描述符来调用函数connect。
注:TCP协议状态转换图:
被动OPEN CLOSE 主动OPEN
(建立TCB) (删除TCB) (建立TCB,
发送SYN)
接收SYN SEND
(发送SYN,ACK) (发送SYN)
接收SYN的ACK(无动作)
接收SYN的ACK 接收SYN,ACK
(无动作) (发送ACK)
CLOSE
(发送FIN) CLOSE 接收FIN
(发送FIN) (发送FIN)
接收FIN
接收FIN的ACK(无动作) (发送ACK) CLOSE(发送FIN)
接收FIN 接收FIN的ACK 接收FIN的ACK
(发送ACK) (无动作) (无动作)
2MSL超时(删除TCB)
(3) 函数bind()
函数bind将本地地址与套接字绑定在一起,其定义如下:
#include< sys/types.h>
#include< sys/socket.h>
int bind(int sockfd,struct sockaddr * myaddr,int addrlen);
参数sockfd是函数sockt返回的套接字描述符;参数myaddr是本地地址;参数addrlen是套接字地址结构的长度。执行成功时返回0,否则,返回-1,并设置全局变量errno为错误类型EADDRINUSER。
服务器和客户机都可以调用函数bind来绑定套接字地址,但一般是服务器调用函数bind来绑定自己的公认端口号。绑定操作一般有如下几种组合方式:
表1
程序类型
IP地址
端口号
说明
服务器
INADDR_ANY
非零值
指定服务器的公认端口号
服务器
本地IP地址
非零值
指定服务器的IP地址和公认端口号
客户机
INADDR_ANY
非零值
指定客户机的连接端口号
客户机
本地IP地址
非零值
指定客户机的IP地址连接端口号
客户机
本地IP地址
零
指定客户机的IP地址
分别说明如下:
(1) 服务器指定套接字地址的公认端口号,不指定IP地址:即服务器调用bind时,设置套接字的IP地址为特殊的INADDE-ANY,表示它愿意接收来自任何网络设备接口的客户机连接。这是服务器最常用的绑定方式。
(2) 服务器指定套接字地址的公认端口号和IP地址:服务器调用bind时,如果设置套接字的IP地址为某个本地IP地址,这表示这台机器只接收来自对应于这个IP地址的特定网络设备接口的客户机连接。当服务器有多块网卡时,可以用这种方式来限制服务器的接收范围。
(3) 客户机指定套接字地址的连接端口号:一般情况下,客户机调用connect函数时不用指定自己的套接字地址的端口号。系统会自动为它选择一个未用的端口号,并且用本地的IP地址来填充套接字地址中的相应项。但有时客户机需要使用一个特定的端口号(比如保留端口号),而系统不会未客户机自动分配一个保留端口号,所以需要调用函数bind来和一个未用的保留端口号绑定。
(4) 指定客户机的IP地址和连接端口号:表示客户机使用指定的网络设备接口和端口号进行通信。
(5) 指定客户机的IP地址:表示客户机使用指定的网络设备接口和端口号进行通信,系统自动为客户机选一个未用的端口号。一般只有在主机有多个网络设备接口时使用。
我们一般不在客户机上使用固定的客户机端口号,除非是必须使用的情况。在客户机上使用固定的端口号有以下不利:
(1) 服务器执行主动关闭操作:服务器最后进入TIME_WAIT状态。当客户机再次与这个服务器进行连接时,仍使用相同的客户机端口号,于是这个连接与前次连接的套接字对完全一样,但是一呢、为前次连接处于TIME_WAIT状态,并未消失,所以这次连接请求被拒绝,函connect以错误返回,错误类型为ECONNREFUSED
(2) 客户机执行主动关闭操作:客户机最后进入TIME_WAIT状态。当马上再次执行这个客户机程序时,客户机将继续与这个固定客户机端口号绑定,但因为前次连接处于TIME_WAIT状态,并未消失,系统会发现这个端口号仍被占用,所以这次绑定操作失败,函数bind以错误返回,错误类型为EADDRINUSE。
(4) 函数listen()
函数listen将一个套接字转换为征听套接字,定义如下;
#include< sys/socket,h>
int listen(int sockfd,int backlog)
参数sockfd指定要转换的套接字描述符;参数backlog设置请求队列的最大长度;执行成功时返回0, 否则返回-1。函数listen功能有两个:
(1) 将一个尚未连接的主动套接字(函数socket创建的可以用来进行主动连接但不能接受连接请求的套接字)转换成一个被动连接套接字。执行listen之后,服务器的TCP状态由CLOSED转为LISTEN状态。
(2) TCP协议将到达的连接请求队列,函数listen的第二个参数指定这个队列的最大长度。
注:参数backlog的作用:
TCP协议为每一个征听套接字维护两个队列:
(1) 未完成连接队列:每个尚未完成3次握手操作的TCP连接在这个队列中占有一项。TCP希望仪在接收到一个客户机SYN数据段之后,在这个队列中创建一个新条目,然后发送对客户机SYN数据段的确认和自己的SYN数据段(ACK+SYN数据段),等待客户机对自己的SYN数据段的确认。此时,套接字处于SYN_RCVD状态。这个条目将保存在这个队列中,直到客户机返回对SYN数据段的确认或者连接超时。
(2) 完成连接队列:每个已经完成3次握手操作,但尚未被应用程序接收(调用函数accept)的TCP连接在这个队列中占有一项。当一个在未完成连接队列中的连接接收到对SYN数据段的确认之后,完成3次握手操作,TCP协议将它从未完成连接队列移到完成连接队列中。此时,套接字处于ESTABLISHED状态。这个条目将保存在这个队列中,直到应用程序调用函数accept来接收它。
参数backlog指定某个征听套接字的完成连接队列的最大长度,表示这个套接字能够接收的最大数目的未接收连接。如果当一个客户机的SYN数据段到达时,征听套接字的完成队列已经满了,那么TCP协议将忽略这个SYN数据段。对于不能接收的SYN数据段,TCP协议不发送RST数据段,
(5) 函数accept()
函数accept从征听套接字的完成队列中接收一个已经建立起来的TCP连接。如果完成连接队列为空,那么这个进程睡眠。
#include< sys/socket.h>
int accept(int sockfd,struct sockaddr * addr,int * addrlen)
参数sockfd指定征听套接字描述符;参数addr为指向一个Internet套接字地址结构的指针;参数addrlen为指向一个整型变量的指针。执行成功时,返回3个结果:函数返回值为一个新的套接字描述符,标识这个接收的连接;参数addr指向的结构变量中存储客户机地址;参数addrlen指向的整型变量中存储客户机地址的长度。失败时返回-1。
征听套接字专为接收客户机连接请求,完成3次握手操作而用的,所以TCP协议不能使用征听套接字描述符来标识这个连接,于是TCP协议创建一个新的套接字来标识这个要接收的连接,并将它的描述符发挥给应用程序。现在有两个套接字,一个是调用函数accept时使用的征听套接字,另一个是函数accept返回的连接套接字(connected socket)。一个服务器通常只需创建一个征听套接字,在服务器进程的整个活动期间,用它来接收所有客户机的连接请求,在服务器进程终止前关闭这个征听套接字;对于没一个接收的(accepted)连接,TCP协议都创建一个新的连接套接字来标识这个连接,服务器使用这个连接套接字与客户机进行通信操作,当服务器处理完这个客户机请求时,关闭这个连接套接字。
当函数accept阻塞等待已经建立的连接时,如果进程捕获到信号,函数将以错误返回,错误类型为EINTR。对于这种错误,一般重新调用函数accept来接收连接。
(6) 函数close()
函数close关闭一个套接字描述符。定义如下:
#include< unistd.h>
int close(int sockfd);
执行成功时返回0,否则返回-1。与操作文件描述符的close一样,函数close将套接字描述符的引用计数器减1,如果描述符的引用计数大于0,则表示还有进程引用这个描述符,函数close正常返回;如果为0,则启动清除套接字描述符的操作,函数close立即正常返回。
调用close之后,进程将不再能够访问这个套接字,但TCP协议将继续使用这个套接字,将尚未发送的数据传递到对方,然后发送FIN数据段,执行关闭操作,一直等到这个TCP连接完全关闭之后,TCP协议才删除该套接字。
(7) 函数read()和write()
用于从套接字读写数据。定义如下:
int read(int fd,char * buf,int len)
int write(int fd,char * buf,int len)
函数执行成功时,返回读或写的数据量的大小,失败时返回-1。
每个TCP套接字都有两个缓冲区:套接字发送缓冲区、套接字接收缓冲区,分别处理发送和接收任务。从网络读、写数据的操作是由TCP协议在内核中完成的:TCP协议将从网络上接收到的数据保存在相应套接字的接收缓冲区中,等待用户调用函数将它们从接收缓冲区拷贝到用户缓冲区;用户将要发送的数据拷贝到相应套接字的发送缓冲区中,然后由TCP协议按照一定的算法处理这些数据。
读写连接套接字的操作与读写文件的操作类似,也可以使用函数read和write。函数read完成将数据从套接字接收缓冲区拷贝到用户缓冲区:当套接字接收缓冲区有数据可读时,1:可读数据量大于函数read指定值,返回函数参数len指定的数据量;2:了度数据量小于函数read指定值,函数read不等待请求的所有数据都到达,而是立即返回实际读到的数据量;当无数据可读时,函数read将阻塞不返回,等待数据到达。
当TCP协议接收到FIN数据段,相当于给读操作一个文件结束符,此时read函数返回0,并且以后所有在这个套接字上的读操作均返回0,这和普通文件中遇到文件结束符是一样的。
当TCP协议接收到RST数据段,表示连接出现了某种错误,函数read将以错误返回,错误类型为ECONNERESET。并且以后所有在这个套接字上的读操作均返回错误。错误返回时返回值小于0。
函数write完成将数据从用户缓冲区拷贝到套接字发送缓冲区的任务:到套接字发送缓冲区有足够拷贝所有用户数据的空间时,函数write将数据拷贝到这个缓冲区中,并返回老辈的数量大小,如果可用空间小于write参数len指定的大小时,函数write将阻塞不返回,等待缓冲区有足够的空间。
当TCP协议接收到RST数据段(当对方已经关闭了这条连接之后,继续向这个套接字发送数据将导致对方TCP协议返回RST数据段),TCP协议接收到RST数据段时,函数write将以错误返回,错误类型为EINTR。以后可以继续在这个套接字上写数据。
(8) 函数getsockname()和getpeername()
函数getsockname返回套接字的本地地址;函数getpeername返回套接字对应的远程地址。
10、 结束语
网络程序设计全靠套接字接收和发送信息。上文主要讲述了Linux 下Socket的基本概念、Sockets API以及Socket所涉及到的TCP常识
㈨ 在linux中C语言socket怎么将接收到的波形数据(十六进制的数据)存入到本地磁盘中,并按文件分级存放。
估计你是用的tcp socket,导致“videlord”网友说的情况:对于tcp socket,send与recv不是对等的,recv时只要缓冲有数据就会收上来。简单说就是你send 4次,比如分别为10 10 10 10字节,对端可以一次recv到这40字节数据,也可以recv 40次、每次1字节。
解决办法有两种:
改用udp socket,send/recv自然对等了
继续用tcp socket,自己进行数据分段:比如自行约定在数据前约定4个字节用于描述数据长度,这样发送时,send 4+33字节,send 4+35字节;接收时,先获取描述长度的4字节获得长度,再按照长度接收数据(可能需要多次recv凑齐指定长度)。