这两天复习了很多有关Windows平台和Linux平台的Socket编程知识,以前曾经写过一篇博文,示范过怎么使用C语言实现TCP链接,可是那篇文章写得很随意,因此如今我决定要重写这篇文章,既是为了总结这两天学到的知识,也为了给往后我写代码一个参考。linux
下面我用给出两份C语言代码来实现TCP链接,采用边写代码边解释的办法讲解这些程序,越复杂的代码越难看懂,所以我将给出最简洁的代码、包含最少的头文件来实现一个只发出一句话就随即关闭的程序。编程
首先总结一下TCP的链接过程:小程序
服务端:windows
建立一个Socket 「 socket() 」服务器
建立并初始化本机地址结构体 「 struct sockaddr_in 」网络
绑定Socket和地址结构体 「 bind() 」多线程
监听链接 「 listen() 」socket
接受链接 「 accept() 」函数
开始自由通讯 「 Linux: read()/write() 」 「 Windows: recv()/send() 」spa
关闭Socket 「 close() 」
客户端:
建立一个Socket 「 socket() 」
建立并初始化服务器地址结构 「 struct sockaddr_in 」
链接服务器 「 connect() 」
开始自由通讯 「 Linux: read()/write() 」「 Windows: recv()/send() 」
关闭Socket 「close() 」
代码:
服务端(Windows平台):
#include
#include
int main(){
//启动Socket前的初始化
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
/*在windows平台上使用Socket编程前 *必须* 先调用WSAStartup()函数初始化一些工做,
是关于ws2_32.dll调用的初始化,ws2_32.dll提供了实现网络链接必要的函数,
当程序运行结束之后须要调用WSACleanup()来清理、释放资源,
若是以linux为服务器,则不须要这项工做*/
//建立SOCKET
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/*在一次通讯中,通讯的双方每边都会有一个SOCKET,用于标识本身
SOCKET这东西在我看来,就跟一个文件流没什么差异,
在linux上咱们能够调用write()向要通讯的对方的SOCKET中写入数据,
对方能够调用read()从他本身的SCOKET中读取数据咱们刚发送的数据,
就像操做一个普通文件同样,只不过这个"文件"是储存在另外一台电脑上而已,
因此把它理解成一个通讯的通道就行了*/
//绑定本机的地址到SOCKET
//首先须要构造一个地址结构体来储存咱们本身的IP和端口
//这个结构体的具体描述在网上有不少介绍,也能够打开winsock2.h头文件查看详细信息,这里就很少说了
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET; //TCP协议这么写就行了
serverAddr.sin_port = htons(1187); //端口号,htons()这个函数你们有必要去了解一下
serverAddr.sin_addr.s_addr = INADDR_ANY; //本机的IP地址,上次我在这里填了127.0.0.1,
//结果局域网里的其余机器都链接不上,因而改为了这样才能够
//这样就把本机的IP和SOCKET绑定起来了
bind(serverSocket,(struct sockaddr*)&serverAddr,sizeof(serverAddr));
//设置监听参数
listen(serverSocket,1);
/*第二个参数的意思不是最多能接受多少个链接,而是同时能处理几个主机的链接请求,
这里设置为1,假设服务器上线之后,同时有两台主机请求链接,
那么就最多只会处理一个链接,而另外一台主机只能链接失败
值得注意的是,程序运行到这里并不会中止下来,监听端口,等待链接。
我以前一直觉得服务器等待链接是在这里发生的,真正等待链接的函数是下面的accept() */
//接受链接
struct sockaddr_in clientAddr;
int clientAddrSize = sizeof(struct sockaddr_in);
SOCKET clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrSize);
/*这只是个简单示例,因此只用了一个客户SOCKET,也就是说只要有一台主机链接进来,
咱们就不会再处理新的链接,而真实的服务器通常都是用多线程来接受客户链接的,
每接收到一个新的链接,就建立一个新线程来跟客户端交互。
程序运行到accept()函数这里就会阻塞住,直到有新的链接才会继续运行下去,
accpet()函数须要一个sockaddr_in结构图做为参数,
函数调用完毕后这个结构体里就储存着客户端的IP地址,
accpet()函数返回的是新链接的SOCKET,咱们下面跟对方交互就用这个SOCKET。*/
//向客户端发送数据
char str[] = "Hello,friend!";
send(clientSocket, str, sizeof(str), 0);
/*发送数据很简单,参数基本都能理解,最后一个参数我尚未仔细研究过,只知道通常置0就行了*/
//关闭套接字
closesocket(serverSocket);
closesocket(clientSocket);
//首尾呼应
WSACleanup();
return 0;
}
客户端(Linux平台):
#include
#include
#include
int main(){
//构造socket
int mySocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("Socket: %d\n",mySocket);
//链接服务器,用的是connect()函数,调用须要一个sockaddr_in结构体
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(1187);
serverAddr.sin_addr.s_addr = inet_addr("192.168.0.XXX"); //这里填上服务器的IP地址
connect(mySocket, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr));
//接收服务器发送的数据
char buffer[40];
read(mySocket, buffer, sizeof(buffer)-1);
printf("Message form server: %s\n", buffer);
//关闭链接
close(mySocket);
return 0;
}
这只是一个简单的、不完整的、有BUG的小程序,还有不少没有作到位的地方,望高手指点。