概念了解
- 网络由下往上分为物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。
我们知道IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层,三者从本质上来说没有可比性,为了概念更加的清晰,我们还是得了解一下三者之间的关系。 - 我们在传输数据时,可以只使用(传输层)TCP/IP协议,但是那样的话,如果没有应用层,便无法识别数据内容。 如果想要使传输的数据有意义,则必须使用到应用层协议。应用层协议有很多,比如HTTP、FTP、TELNET等,也可以自己定义应用层协议。WEB使用HTTP协议作应用层协议,以封装HTTP文本信息,然后使用TCP/IP做传输层协议将它发到网络上。
- 而我们平时说的最多的socket是什么呢,实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API)。通过Socket,我们才能使用TCP/IP协议。 实际上,Socket跟TCP/IP协议没有必然的联系。 Socket编程接口在设计的时候,就希望也能适应其他的网络协议。 所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,从而形成了我们知道的一些最基本的函数接口,比如create、listen、connect、accept、send、read和write等等。大家可别忘了socket的英语意思是插座,就是提供接口的意思。不同语言的socket都是这些基本函数实现的。我们并不需要了解这些函数的内部实现机制,只需要会调用就行,站在巨人头上摘苹果。
基本模型
编程实例
客户端
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdlib.h>
#define MAXLINE 1024
#define SERV_PORT 9998
int main(int argc, char *argv[])
{
char sendbuf[MAXLINE],receivebuf[MAXLINE];
struct sockaddr_in servaddr; //定义sockaddr类型结构体servaddr
int client_sockfd;
int rec_len;
// 判断命令端输入的参数是否正确
if( argc != 2)
{
printf("usage: ./client <ipaddress>\n");
exit(0);
}
// 调用socket函数
if((client_sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket");//调用perror函数,该函数会自行打印出错信息
exit(0);
}
//初始化结构体
memset(&servaddr,0,sizeof(servaddr)); //数据初始化-清零
servaddr.sin_family = AF_INET; //设置IPv4通信
servaddr.sin_port = htons(SERV_PORT); //设置服务器端口号
// IP地址转换函数inet_pton,将点分十进制转换为二进制
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
printf("inet_pton error for %s\n",argv[1]);
exit(0);
}
//调用connect函数
if( connect(client_sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0)
{
perror("connected failed");
exit(0);
}
//循环发送接收数据,send发送数据,recv接收数据
while(1)
{
printf("send msg to server: \n");
fgets(sendbuf, 1024, stdin);//从标准输入读数据
// 向服务器端发送数据
if( send(client_sockfd, sendbuf, strlen(sendbuf), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
// 接受服务器端传过来的数据
if((rec_len = recv(client_sockfd,receivebuf, MAXLINE,0)) == -1)
{
perror("recv error");
exit(1);
}
printf("Response from server: %s\n",receivebuf);
}
//关闭套接字
close(client_sockfd);
return 0;
}
服务器端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#define SERV_PORT 9998
#define MAXLINE 4096
int main(int argc, char** argv)
{
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
char buf[MAXLINE],sendbuf[MAXLINE];
int len
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//初始化
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
servaddr.sin_port = htons(SERV_PORT);
//调用bind函数,绑定
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) <0)
{
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//调用listen函数监听端口
if( listen(socket_fd, 10) <0)
{
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("waiting for client's connection......\n");
//调用accept函数
if((connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) <0)
{
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
exit(1);
}
//接受客户端传过来的数据
while((len= recv(connect_fd, buf, MAXLINE, 0))>0)
{
printf("receive message from client: %s\n", buf);
//向客户端发送回应数据
printf("send message to client: \n");
fgets(sendbuf, 4096, stdin);
if( send(connect_fd, sendbuf, strlen(sendbuf), 0) < 0)
{
printf("send messaeg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
}
close(connect_fd);
close(socket_fd);
}
功能实现
这两个基本程序能够实现C/S间通信,如果将服务端程序交叉编译后放在fl2440开发板上跑,可以直接连上其他服务器的公网ip地址,前提是服务器上有server.c的程序在跑。如果是采用端口转发的服务器还需登录路由器,设置对应的端口号以开启端口转发,比如这里提供的9998号端口。
补充知识点
Window和linux的socket的不同之处:
◇.头文件
Window:winsock.h或winsock2.h
linux: netinet/in.h(大部分都在这),unistd.h(close函数)
◇.初始化
window: 需要WSAStartup启动Ws2_32.lib;#program comment(lib, “Ws2_32”)加载lib库
linux: 无需
◇.关闭socket
window: closesocket()
linux: close()
◇.socket的类型
window: SOCKET
linux: int
◇.错误代码的获取
window: getlasterror()/WSAGetLastError()
linux: 为成功返回-1;错误代码在errno中(头文件errno.h)
◇.设置非阻塞
window: ioctlsocket()
linux: fcntl(), (头文件fcnt.h)
◇.send函数最后一个参数
window: 一般是0
linux: 最好设置为MSG_NOSIGNAL;表示出错后不向系统发信号,否则程序会退出!
◇.多线程
window:包含process.h,用_beginthread/_endthread
linux:包含pthread.h,用pthread_create/pthread_exit
◇.使用IP地址
window:addr_var.sin_addr.S_un.S_addr
linux:addr_var.sin_addr.s_addr