socket网络编程
//ARM嵌入linux与PC的通信程序
int fd = 0;
int newfd = 0;
int addr_len = sizeof(struct sockaddr_in);
int recv_len = 0;
int q=0;
int z=0;
int len;
//struct array y1,y2;
char buf1[4096];
int fw;
struct sockaddr_in addr;
struct sockaddr_in serv_addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; //pint TCP/IP addr_set;
addr.sin_port = htons(PORT); //port number;
addr.sin_addr.s_addr = htonl(INADDR_ANY); //auto fill IP address;
/*create UDP socket*/
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0)
{
perror("socket");
return -1;
}
else
{
printf("create socket %d success.\n",fd);
}
//设置套接字选项避免地址使用错误-----后来添加的 避免 bind address already in use
int on =1;
if((setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)
{
perror("setsocketopt failed");
return 1;
}
/*bind data port*/
if(bind(fd,(struct sockaddr *)&addr,sizeof(addr)) < 0)
{
perror("bind");
close(fd);
return -1;
}
else
{
printf("bind the addr to socket %d success.\n",fd);
}
if(listen(fd, 3) < 0)
{
perror("listen");
close(fd);
return -1;
}
else
{
printf("listen to the socket %d, port %d success.\n",fd,PORT);
}
if((newfd = accept(fd, (void *)&serv_addr, &addr_len)) < 0)
{
perror("accept");
close(fd);
return -1;
}
else
{
printf("accept new socket %d from addr:%s port:%d sucess.\n",newfd,inet_ntoa(serv_addr.sin_addr),ntohs(serv_addr.sin_port));
服务端任务
- Socket()函数创建服务端套接字,声明套接字的协议族与数据格式
- 声明代表客户端的SOCKADDR_IN结构,并填充接收的IP(INADDR_ANY所有),端口号,协议族
- Bind()绑定服务端套接字与SOCKADDR_IN结构
- Listen()监听
- Accept()阻塞进程,知道有客户端连接
- Recv或者send接收发送数据
- Closesocket()
客户端完成的工作
- Socket()函数创建客户端套接字,声明套接字的协议族与数据格式;
- 声明代表服务端的SOCKADDR_IN结构,并填充服务端的IP(服务端IP地址),端口号,协议族
- Bind()绑定服务端套接字与SOCKADDR_IN结构
- connect()连接服务端
- Recv或者send接收发送数据
- Closesocket()
相关的函数
htol:
在Linux系统下:htonl(),htons(), ntohl(), ntohs()的头文件及函数定义:
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
htonl()--"Host to Network Long int" 32Bytes
ntohl()--"Network to Host Long int" 32Bytes
htons()--"Host to Network Short int" 16Bytes
ntohs()--"Network to Host Short int" 16Bytes
这里涉及到
网络字节序与主机字节序
网络字节序就是常见的TCP/IP的IP地址顺序,
主机字节序是不同指令集的处理器处理内存的方式不用,存储顺序不同,例如
I网络字节序都是大端排序
X86指令集的CPU是小端,大端的排序所以就使用到上述的几个函数进行转换
struct sockaddr结构体
这个结构体是linux的网络编程接口中用来表示IP地址的标准结构体,bind、connect等函数中都需要这个结构体,这个结构体是兼容IPV4和IPV6的。在实际编程中这个结构体会被一个struct sockaddr_in所填充。
通常这两个结构是为了给socket套接字相关的bind,accept等函数当参数,一般可以用sockaddr_in结构声明,然后填充IP,端口号,协议族;
头文件#include <sys/socket.h>
Bind函数
把套接字与一套sockaddr结构(包含了协议族,IP,端口号)绑定
在linux 环境下为:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #include <sys/types.h> #include <sys/socket.h> /**** * sockfd:标识一未捆绑套接口的描述字。 * my_addr:赋予套接口的地址。sockaddr结构定义如下: * struct sockaddr{ * u_short sa_family; * char sa_data[14]; * }; * addrlen:name名字的长度。 * 返回值:成功返回0,失败返回-1. ****/ int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen); |
参数列表中,sockfd 表示已经建立的socket编号(描述符);
my_addr 是一个指向sockaddr结构体类型的指针;
参数addrlen表示my_addr结构的长度,可以用sizeof操作符获得。
如无错误发生,则bind()返回0。否则的话,将返回-1,
Listen()函数
创建套接口,监听申请的连接。
int listen( int sockfd, int backlog);
sockfd:用于标识一个已捆绑未连接套接口的描述字。
backlog:等待连接队列的最大长度。
如无错误发生,listen()返回0。否则的话,返回-1,
Accept函数
接收连接请求,通常是服务端listen之后
SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:套接字描述符,该套接口在listen()后监听连接。
addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。
如果没有错误产生,则accept()返回一个描述所接受包的SOCKET类型的值。否则的话,返回INVALID_SOCKET错误,
Connect()函数
客户端连接制定socket
int connect(SOCKET s, const struct sockaddr * name, int namelen);
参数:
s:标识一个未连接socket
name:指向要连接套接字的sockaddr结构体的指针
namelen:sockaddr结构体的字节长度
返回值:成功则返回0, 失败返回-1, 错误原因存于errno 中.
Recv用于已经连接的,数据报或者流数据的套接字进行数据接收
int recv( _In_ SOCKET s, _Out_ char *buf, _In_ int len, _In_ int flags);
该函数的第一个参数指定接收端套接字描述符;
第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;
第三个参数指明buf的长度;
第四个参数一般置0。
这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,
如果协议在传送s的发送缓冲中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据
被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直
等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据copy到buf中(注意协议接收到的数据
可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据copy完。recv函数仅仅是copy数据,真正的
接收数据是协议来完成的),recv函数返回其实际copy的字节数。如果recv在copy时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0
send不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。
int send( SOCKET s, const char FAR *buf, int len, int flags );
该函数的第一个参数指定发送端套接字描述符;
第二个参数指明一个存放应用程序要发送数据的缓冲区;
第三个参数指明实际要发送的数据的字节数;
第四个参数一般置0。
如果send函数copy数据成功,
就返回实际copy的字节数,如果send在copy数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。