服务器端
// 流程: socket()-->bind()-->listen()-->accept() {send(); recv()}
socket()
:使用系统调用 socket()来获得文件描述符
// int socket(int domain, int type, int protocol) ,在头文件<sys/socket.h>
int socketStatus = socket(AF_INET,SOCK_STREAM,0); //通信类型,最常用的值是 "AF_INET"
//套接口的类型:SOCK_STREAM 或SOCK_DGRAM
//0
// 返回一个套接口描述符,如果出错,则返回-1。
bind()
:把套接口绑定到本地计算机的某一个端口。不绑定,则会随机选择一个端口
//int bindStatus = bind(int sockfd, struct sockaddr* my_addr, int addrlen);
//结构体 struct sockaddr 和 struct sockaddr_in
//一般先把sockaddr_in变量赋值后,强制类型转换后传入用sockaddr做参数的函数x
struct sockaddr
{
__SOCKADDR_COMMON (sa_); //unsigned short sa_family;
/* Common data: address family and length. 协议族*/
char sa_data[14]; /* Address data. 地址+端口号*/
};
//在头文件<netinet/in.h> 地址和端口号分离
/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_); /* 协议族 */ //unsigned short sa_family;
in_port_t sin_port; /* Port number. 端口号 */ //unsigned short int sin_port;
struct in_addr sin_addr; /* Internet address. IP地址 */
/* Pad to size of `struct sockaddr'. 用于填充的0字节 */ //unsigned char sin_zero[8];
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
构建地址结构体:
struct sockaddr_in mysock;
bzero(&mysock,sizeof(mysock)); //初始化结构体,填充0个0,使得2个结构体大小相等
mysock.sin_family = AF_INET; //设置地址家族
mysock.sin_port = htons(800); //设置端口 htons() "Host to Network Short"
//计算机数据表示存在两种字节顺序:NBO与HBO
//网络字节顺序NBO(Network Byte Order)
//主机字节顺序(HBO,Host Byte Order)
mysock.sin_addr.s_addr = inet_addr("192.168.1.0"); //设置地址
//将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)
bind():
int bindStatus = bind(socketStatus,(struct sockaddr *)&mysock,sizeof(struct sockaddr); /* bind的时候进行转化 */
listen()
:希望等待一个进入的连接请求,然后再处理它们
//int listen(int sockfd, int backlog); //第一个参数是系统调用 socket()返回的套接口文件描述符。
//第二个参数是进入队列中允许的连接的个数。
int listenStatus = listen(socketStatus, 5);
accept()
:在远程的主机可能试图使用 connect()连接你使用listen()正在监听的端口。但此连接将会在队列中等待,直到使用 accept()处理它。调用 accept()之后,将会返回一个全新的套接口文件描述符来处理这个单个的连接。这样,对于同一个连接来说,你就有了两个文件描述符。原先的一个文件描述符正在监听你指定的端口,新的文件描述符可以用来调用 send()和 recv()。
//int accept(int sockfd, void *addr, unsigned int *addrlen); //第一个参数是正在监听端口的套接口文件描述符。
//第二个参数 addr 是指向本地的数据结构sockaddr_in 的指针。通过它你可以了解哪个主机在哪个端口呼叫你。
//第三个参数同样可以使用 sizeof(struct sockaddr_in)来获得
unsigned int *sin_size=sizeof(struct sockaddr_in);
struct sockaddr_in Client_addr;
int newSocketStatus = accept(socketStatus, &Client_addr, &sin_size);
send()
和recv()
:信息发送和接受。正确返回数据长度,错误返回 -1
// int send(int sockfd, const void* msg, int len, int flags);
//第一个参数是你希望给发送数据的套接口文件描述符。
//它可以是你通过 socket()系统调用返回的,也可以是通过 accept()系统调用得到的。
//第二个参数是指向你希望发送的数据的指针。第三个参数是数据的字节长度。第四个参数标志设置为 0。
//int recv(int sockfd, void* buf, int len, unsigned int flags);
//第一个参数是要读取的套接口文件描述符.第二个参数是保存读入信息的地址。第三个参数是缓冲区的最大长度。第四个参数设置为 0。
客户端
多一个主动的connect()
:
int connect(int sockfd, struct sockaddr * serv_addr, int addrlen);
//第一个参数还是套接口文件描述符,它是由系统调用 socket()返回的。第二个参数是 serv_addr 是指向数据结构 sockaddr 的指针,
//其中包括目的端口和 IP 地址。第三个参数可以使用 sizeof(struct sockaddr)而获得。
代码
server.c
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main()
{
int sfp,nfp;
struct sockaddr_in s_add,c_add;
unsigned int sin_size;
unsigned short portnum=2333; //port number
sfp = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sfp)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=htonl(INADDR_ANY);
s_add.sin_port=htons(portnum);
bzero(&(s_add.sin_zero), 8);
if(-1 == bind(sfp,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("bind fail !\r\n");
return -1;
}
if(-1 == listen(sfp,5))
{
printf("listen fail !\r\n");
return -1;
}
while(1)
{
sin_size = sizeof(struct sockaddr_in);
nfp = accept(sfp, (struct sockaddr *)(&c_add), &sin_size);
if(-1 == nfp)
{
printf("accept fail !\r\n");
return -1;
}
printf("waiting for %#x : %#x\r\n",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port));
if(-1 == send(nfp,"hello,socket\r\n", 14, 0))
{
printf("write fail!\r\n");
return -1;
}
printf("write ok!\r\n");
shutdown(nfp, 2);
}
shutdown(sfp, 2);
return 0;
}
client.c
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
int main()
{
int cfd;
int recbytes;
int sin_size;
char buffer[1024]={0};
struct sockaddr_in s_add,c_add;
unsigned short portnum=2333; //port number
cfd = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == cfd)
{
printf("socket fail ! \r\n");
return -1;
}
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr= inet_addr("192.168.0.13"); //IP of your PC
s_add.sin_port=htons(portnum);
printf("server_addr = %#x : %#x\r\n",ntohl(s_add.sin_addr.s_addr),ntohs(s_add.sin_port));
if(-1 == connect(cfd,(struct sockaddr *)(&s_add), sizeof(struct sockaddr)))
{
printf("connect fail !\r\n");
return -1;
}
printf("connect ok !\r\n");
if(-1 == (recbytes = recv(cfd,buffer,1024, 0)))
{
printf("read data fail !\r\n");
return -1;
}
buffer[recbytes]='\0';
printf("%s\r\n",buffer);
//getchar();
shutdown(cfd, 2);
return 0;
}
参考
socket编程为什么需要htons(), ntohl(), ntohs(),htons() 函数
[转]socket编程——sockaddr_in结构体操作
sockaddr和sockaddr_in的区别