Socket编程是一种用于在不同的计算机之间建立网络连接通信的编程技术。它是通过在不同的计算机之间建立一个“套接字”(socket),使得程序能够进行网络连接通信。Sockets可以在不同的互联网协议中进行,最常见的是TCP和UDP。通过Socket编程,程序可以在不同的计算机之间传输数据,从而实现各种网络应用程序,如电子邮件、网页浏览器、文件传输等。
本代码演示简单的单向发送数据;
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#define MSG "hallo world"
int main(int argc,char** argv)//argc和argv的讲解在后面
{
int sockfd=-1;//习惯把初始值设为负值,这样发生错误了能明显看出来
int rv=-1;
struct sockaddr_in servaddr;
char *servip;
int port;
char buf[1024];
if(argc<3)
{
printf("Program usage:%s [servip] [port]\n",argv[0]);
return -5;
}
servip=argv[1];
port=atoi(argv[2]);//在命令行输入的为字符串,需要转换问int类型,相关函数百度
printf("The IP is:%s port is:%d\n",servip,port);
sockfd =socket(AF_INET ,SOCK_STREAM,0);
if(sockfd<0)//习惯每一次使用函数都判断返回值,返回值小于0的证明该函数出错,易修改
{
printf("socket is failure :%s\n",strerror(errno));
return -1;
}
printf("The socket is seccessfully\n");
memset(&servaddr,0,sizeof(servaddr));//习惯性用之前把数组里赋值为0,也可以叫清空数据
servaddr.sin_family =AF_INET;
servaddr.sin_port = htons(port);
inet_aton(servip,&servaddr.sin_addr);
rv=connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
if(rv<0)
{
printf("Connect to server[%s:%d] failure:%s\n",servip,port,strerror(errno));
return -2;
}
printf("connect to server [%s:%d] sucessfully\n",servip,port);
memset(buf,0,sizeof(buf));
rv=write(sockfd,MSG,strlen(MSG));
if(rv<0)
{
printf("Write to server by sockfd[%d]failure:%s\n",sockfd,strerror(errno));
return -3;
}
rv=read(sockfd,buf,sizeof(buf));
if(rv<0)
{
printf("Read data from server by sockfd[%d] failure:%s\n",sockfd,strerror(errno));
return -4;
}
else if(rv==0)
{
printf("Socket[%d] get disconnected\n",sockfd);
return -5;
}
else if(rv>0)
{
printf("Read %d bytes data from sever:%s",rv,buf);
}
close(sockfd);//结束后记得一定要关闭文件
return 0;
}
关于在main括号里的参数,第一个argc是命令行参数的个数,第二个参数是一个字符串的数组,你也可以写成char *argv[],每一个元素表示一个指针,指向每一个 参数,其实还有第三个参数,在此不讲,例如该程序,如果编译之后只是使用./a.out话会出现以下错误,所以要使用./编译后的名字 IP地址 端口号后才能正常运行,该方法避免了更改IP和端口号需要重新编译的麻烦,但是该程序还不够完善,只能发送一次,下一篇文章会出完整代码。
socket的头文件和原型为
第一个参数为
第二个参数为
domain
参数表示套接字要使用的协议簇,协议簇的在“linux/socket.h”里有详细定义,常用的协议簇:
AF_UNIX(本机通信)
AF_INET(TCP/IP – IPv4)
AF_INET6(TCP/IP – IPv6)
type
“type”参数指的是套接字类型,常用的类型有:
SOCK_STREAM(TCP流)
SOCK_DGRAM(UDP数据报)
SOCK_RAW(原始套接字)
protocol
协议“protocol”一般设置为“0”,就是说在已经确定套接字使用的协议簇和类型时,这个参数的值就为0。
但是有时候创建初始套接字时,在domain参数未知情况下,即不清楚协议簇和类型时,protocol参数可以用来确定协议的种类。
sockaddr_in结构体包含以下字段:
-
sin_family:地址族,通常设置为AF_INET表示IPv4协议。
-
sin_port:端口号,以网络字节序表示。
-
sin_addr:IP地址,以网络字节序表示。
-
sin_zero:填充字段,通常设置为0。
inet_aton函数是一个用来将IP地址转换为网络字节序的函数,
它能够识别有点分开的IP地址和它们的各个子网掩码,而且还能够
完成网络字节序的转换。它的定义形式如下:
int inet_aton(const char *cp, struct in_addr *inp);
函数 inet_aton()用来将一个点分十进制的IP地址(例如192.168.1.1)转换成一个用网络字节顺序存储的 in_addr构体。该函数可以将给定的 IP址转换成一个长整数型数,这样在网络通信中使用起来更加方便快捷。该函数的第一个参数是一个以 NULL束的字符串,指向一个非空的 IP址(例如192.168.1.1),第二个参数 inp向一个 in_addr构体,函数将会把点分十进制的 IP址转换为网络字节顺序存储在in_addr构体中。 inet_aton数的返回值是一个整数,若转换成功就会返回非零的
值,若失败就会返回 0(零)。
connect函数
值得注意的是第二个参数的类型于我们写的不一样,它是一个兼容的参数,为了方便两个结构体的使用
sockfd: 指定数据发送的套接字,解决从哪里发送的问题。内核需要维护大量IO通道,所以用户必需通过这个参数告诉内核从哪个IO通道,此处就是从哪个socket接口中发送数据。sockfd是先前socket返回的值。
server_addr: 指定数据发送的目的地,也就是服务器端的地址。这里服务器是针对connect说的,因为connect是主动连接的一方调用的,所以相应的要存在一个被连接的一方,被动连接的一方需要调用listen以接受connect的连接请求,如此被动连接的一方就是服务器了。
addrlen: 指定server_addr结构体的长度。我们知道系统中存在大量的地址结构,但socket接口只是通过一个统一的结构来指定参数类型,所以需要指定一个长度,以使内核在进行参数复制的时候有个有个界限。