在TCP/IP协议中,一个IP地址标识网络通讯中唯一一台主机,而一个IP地址+一个TCP(或UDP)端口号就可以标识网络通讯中的一个进程,此时的IP地址+端口号即称为socket。
内存中的多字节数据相对于内存地址有大小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大小端之分,网络数据流同样也有大小端之分:先发出的数据是低地址,后发出的数据是搞地质,TCP/IP协议规定网络数据流采用大端字节序,即低地址高字节。
1)计算机分为大端机和小端机,那么如何使同样的C代码在大端机和小端机上都能正常运行呢?库函数一节为我们提供了转换的接口:
include
unit32_t htonl(unit32_t hostlong); //32位的长整数从主机字节序转换为网络字节序
unit16_t htons(unit16_t hostshort);//16位的短整数从主机字节序转换为网络字节序
unit32_t ntohl(unit32_t netlong);//32位的长整数从网络字节序转换为主机字节序
unit16_t ntohs(unit16_t netshort);//16位的短整数从网络字节序转换为主机字节序
2)由于网络传输是二进制比特流传输,所以必须将我们常用的十进制的IP地址与网络字节序的二进制形式的IP源码互相转换才可以将数据传输到准确的地址,下面是地址转换函数介绍:
int inet_aton(const char* cp, struct in_addr *inp);//将字符串cp的十进制转换为网络字节序的二进制形式后存储到inp中
char* inet_ntoa(struct in_addr *in);//将网络字节序的二进制形式转换为十进制的字符串形式,返回字符串的首地址
1)结构体
IPV4套接字地址结构体:
struct sockaddr_in{
unit8_t sin_len;
sa_famliy_t sin_famliy;//协议家族
in_port_t sin_port;//端口号
struct in_addr sin_addr;//IP地址
char sin_zero[8];
};
通用套接字地址结构体:
struct sockaddr{
unit8_t sa_len;
sa_famliy sa_famlity;
char sa_data[14];
};
2)相关函数
socket:
server端即服务器端:服务器由于不知道客户何时回请求建立连接,所以必须绑定端口之后进行监听;
client端即客户端:只需向服务器端发送连接请求(connect);
客户端主动发起请求连接,服务器接受连接请求,完成“三次握手”;服务器与客户端都可以发起断开连接请求,完成”四次挥手“的过程。
基本框架图(此图为摘录):
#include
#include
#include
#include
#include
#include
#include
#define _PORT_ 9999
#define _BACKLOG_ 10
int main()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
perror("socket");
exit(1);
}
int opt = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in server_socket;
struct sockaddr_in client_socket;
bzero(&server_socket, sizeof(server_socket));
server_socket.sin_family = AF_INET;
server_socket.sin_addr.s_addr = htonl(INADDR_ANY);
server_socket.sin_port = htons(_PORT_);
if(bind(sock, (struct sockaddr*)&server_socket, sizeof(struct sockaddr_in)) < 0)
{
perror("bind");
close(sock);
exit(2);
}
if(listen(sock, _BACKLOG_) < 0)
{
perror("listen");
close(sock);
exit(3);
}
printf("bind and listen success, wait accept..\n");
while(1)
{
socklen_t len = 0;
int client_sock = accept(sock, (struct sockaddr*)&client_socket, &len);
if(client_sock < 0)
{
perror("accept");
close(sock);
exit(4);
}
char buf_ip[INET_ADDRSTRLEN];
memset(buf_ip, '\0', sizeof(buf_ip));
inet_ntop(AF_INET,&client_socket.sin_addr, buf_ip, sizeof(buf_ip));
printf("get connect, ip is : %s port is : %d\n",buf_ip, ntohs(client_socket.sin_port));
while(1)
{
char buf[1024];
memset(buf, '\0',sizeof(buf));
read(client_sock, buf, sizeof(buf));
printf("client#:%s\n",buf);
printf("server#:");
memset(buf, '\0', sizeof(buf));
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0';
write(client_sock, buf, strlen(buf)+1);
printf("please wait...\n");
}
}
close(sock);
return 0;
}
client.c:
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 9999
#define SERVER_IP "192.168.43.121"
int main(int argc, char *argv[])
{
if(argc != 2)
{ printf("Usage: client IP \n");
return 1;
}
char *str = argv[1];
char buf[1024];
memset(buf, '\0', sizeof(buf));
struct sockaddr_in server_sock;
int sock = socket(AF_INET, SOCK_STREAM, 0);
bzero(&server_sock, sizeof(server_sock));
server_sock.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_IP, &server_sock.sin_addr);
server_sock.sin_port = htons(SERVER_PORT);
int ret = connect(sock, (struct sockaddr*)&server_sock, sizeof(server_sock));
if(ret < 0)
{
printf("perror");
return 1;
}
printf("connect success....\n");
while(1)
{
printf("client#:");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0';
write(sock, buf, sizeof(buf));
if(strncasecmp(buf, "quit", 4) == 0)
{
perror("perror");
break;
}
printf("please wait...\n");
read(sock, buf, sizeof(buf));
printf("server$: %s\n",buf);
}
close(sock);
return 0;
}
打开一个terminal运行服务器端server,再打开另外一个terminal运行客户端client,并加上通过ifconfig查出的IP地址即可实现服务器端与客户端的连接。