#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* bind
* listen
* accept
* send/recv
*/
#define SERVER_PORT 8888
#define BACKLOG 10
int main(int argc, char **argv)
{
int iSocketServer;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRet;
int iAddrLen;
int iRecvLen;
unsigned char ucRecvBuf[1000];
int iClientNum = -1;
signal(SIGCHLD,SIG_IGN);//处理僵尸进程的,可以看我那篇关于僵尸进程的博客
/*
* int socket(int domain, int type,int protocol)
* domain: 说明我们网络程序所在的主机采用的通讯协族(AF_UNIX和AF_INET等).
* AF_UNIX只能够用于单一的Unix 系统进程间通信,
* 而AF_INET是针对Internet的,因而可以允许在远程
*
* type: 我们网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM等)
* SOCK_STREAM表明我们用的是TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流.
* SOCK_DGRAM 表明我们用的是UDP协议,这样只会提供定长的,不可靠,无连接的通信.
*
* protocol: 由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了
*/
iSocketServer = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == iSocketServer)
{
printf("socket error!\n");
return -1;
}
/*
* int bind(int sockfd, struct sockaddr *my_addr, int addrlen)
* 把文件描述符和ip以及端口绑定起来
*
* sockfd:是由socket调用返回的文件描述符.
* addrlen:是sockaddr结构的长度.
*
* my_addr:是一个指向sockaddr的指针
*
* struct sockaddr{
* unisgned short as_family;
* char sa_data[14];
* };
*
* 不过由于系统的兼容性,我们一般不用这个头文件,而使用另外一个结构(struct sockaddr_in) 来代替
* struct sockaddr_in{
* unsigned short sin_family;
* unsigned short int sin_port;
* struct in_addr sin_addr;
* unsigned char sin_zero[8];
* }
* 我们主要使用Internet所以
* sin_family一般为AF_INET,
* sin_addr设置为INADDR_ANY表示可以和任何的主机通信
* sin_port是我们要监听的端口号.
* sin_zero[8]是用来填充的,一般设置为01就行了
* bind将本地的端口同socket返回的文件描述符捆绑在一起.成功是返回0,失败的情况和socket一样
*/
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero, 0, 8);
iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("bind error!\n");
return -1;
}
/*
* int listen(int sockfd,int backlog)
* sockfd: 是bind后的文件描述符.(不是指bind的返回值,而是指socket函数返回的文件句柄经过bind函数后)
* backlog: 设置请求排队的最大长度.当有多个客户端程序和服务端相连时, 使用这个表示可以介绍的排队长度. 这里我们设置为10
* listen函数将bind的文件描述符变为监听套接字.返回的情况和bind一样.
*/
iRet = listen(iSocketServer, BACKLOG);
if (-1 == iRet)
{
printf("listen error!\n");
return -1;
}
/* 这里的while(1)为父进程 */
while (1)
{
iAddrLen = sizeof(struct sockaddr);
/*
* int accept(int sockfd, struct sockaddr *addr,int *addrlen)
* sockfd:是listen后的文件描述符.
* addr,addrlen是用来给客户端的程序填写的,服务器端只要传递指针就可以了。
* bind,listen和accept是服务器端用的函数,
* accept调用时,服务器端的程序会一直阻塞到有一个 客户程序发出了连接.
* accept成功时返回最后的服务器端的文件描述符,
* 这个时候服务器端可以向该描述符写信息了. 失败时返回-1
*/
iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
/* 连接上了的话打印相应信息,并创建子进程对各个客户端发来的消息进行处理 */
if (-1 != iSocketClient)
{
iClientNum++;
printf("Get connect from client %d : %s\n", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
if (!fork())
{
/* 子进程的源码 */
while (1)
{
/* 接收客户端发来的数据并显示出来 */
/* ssize_t recv(int sockfd, void *buf, size_t len, int flags);
* flags设置为0就行了
*/
iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0); //类似于read函数
if (iRecvLen <= 0)
{
close(iSocketClient);
return -1;
}
else
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf);
}
}
}
}
}
close(iSocketServer);
return 0;
}
上面是代码,编译成功后我们运行程序并用手机连接该服务器发送数据进行测试,可以看到,我们在手机端输入的字符串成功发送到Linux系统的服务器上面去了,当然,有一点要强调一下,因为没有做内网穿透,这个实验必须手机和PC(Linux)在同一网段才能执行成功。