socket网络编程时linux中数据传输的一种重要的方式。今天所介绍的是tcp的编程。
一、socket有三种类型:
1、流式socket(SOCK_STREAM)
流式套接字提供可靠、面向连接的通信流;它使用tcp协议,从而保证了数据传输的正确性和顺序性。说白了就是这家伙可靠。
2、数据包socket(SOCKET_DGRAM)
数据包套接字是一种无连接的服务,数据通过相互独立的报文进行传输,它是无序的。容易出错,使用的是UDP协议。
3、原始socket
原始套接字允许对底层协议如IP和ICMP进行直接访问,功能强大但使用不便。
常用的两个数据类型:sockaddr和sockaddr_in。
unsigned short sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
sa_data : 是14字节的协议地址。
short int sin_family; /* Address family */
unsigned short int sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
unsigned char sin_zero[8]; /* Same size as struct sockaddr */
};
sin_family:指代协议族,在socket编程中只能是AF_INET
sin_port:存储端口号(使用网络字节顺序)
sin_addr:存储IP地址,使用in_addr这个数据结构
sin_zero:是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
unsigned long s_addr;
};
五、TCP编程实例:
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#define SERVPORT 4444
#define BACKLOG 10
#define MAX_CONNECTED_NO 10
#define MAXDATASIZE 100
int main()
{
struct sockaddr_in server_sockaddr,client_sockaddr;
int sin_size,recvbytes;
int sockfd,client_fd;
char buf[MAXDATASIZE];
if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1){
perror("socket");
exit(1);
}
printf("socket success!,sockfd=%d\n",sockfd);
server_sockaddr.sin_family=AF_INET;
server_sockaddr.sin_port=htons(SERVPORT);
server_sockaddr.sin_addr.s_addr=INADDR_ANY;
bzero(&(server_sockaddr.sin_zero),8);
if(bind(sockfd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr))==-1){
perror("bind");
exit(1);
}
printf("bind success!\n");
if(listen(sockfd,BACKLOG)==-1){
perror("listen");
exit(1);
}
printf("listening....\n");
sin_size=sizeof(struct sockaddr_in);
printf("sin_size = %d\n",sin_size);
if((client_fd=accept(sockfd,(struct sockaddr *)&client_sockaddr,&sin_size))==-1){
perror("accept");
exit(1);
}
if((recvbytes=recv(client_fd,buf,MAXDATASIZE,0))==-1){
perror("recv");
exit(1);
}
printf("received a connection :%s\n",buf);
close(sockfd);
}
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 4444
#define MAXDATASIZE 100
main(int argc,char *argv[]){
int sockfd,sendbytes;
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){
perror("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((sendbytes=send(sockfd,"hello",6,0))==-1){
perror("send");
exit(1);
}
close(sockfd);
}
可以在主机上运行server.c,把client.c使用交叉环境编译,下载到开发板上运行,通过TCP实现开发板和主机的通讯。
首先在主机上运行server.c,然后在开发板上运行client.c.
在开发板上执行的程序:
注意事项:
一、在客户端执行client时,后面跟随的ip地址是主机的ip地址。
二、在accept函数中,一定要初始化sin_size的大小,否则在运行的结果中会出现:error:Invalid argument
一般初始sin_size采用的方法是:
sin_size=sizeof(struct sockaddr_in);
三、accept函数:系统调用 accept() 会有点古怪的地方的!你可以想象发生 这样的事情:有人从很远的地方通过一个你在侦听 (listen()) 的端口连接 (connect()) 到你的机器。它的连接将加入到等待接受 (accept()) 的队列 中。你调用 accept() 告诉它你有空闲的连接。它将返回一个新的套接字文 件描述符!这样你就有两个套接字了,原来的一个还在侦听你的那个端口, 新的在准备发送 (send()) 和接收 ( recv()) 数据。
四、定义的tcp的端口号:无论是tcp还是udp,端口号的范围是从1-65535。端口号分为三个类型:
1)公认端口(Well Known Ports):从0到1023,它们紧密绑定(binding)于一些服务。通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。
(2)注册端口(Registered Ports):从1024到49151。它们松散地绑定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
(3)动态和/或私有端口(Dynamic and/or Private Ports):从49152到65535。理论上,不应为服务分配这些端口。
五、在为TCP分配buff空间的时候,可以尽量的分配的大些,比如1024.这样在接收客户端较多数据时不至于出错。