分3个部分,首先介绍Socket基本概念(主要参考《Socket编程》);然后分别是基于MFC的Socket编程(参考《MFC socket网络编程(流程示例)》)和基于C#的Socket编程(参考《你得学会并且学得会的Socket编程基础知识》)
Socket是什么
如图1所示,传输控制协议/网间协议(TCP/IP)是一个工业标准的协议集,包含了运输层、网络层、链路层
Socket 是应用层与TCP/IP协议族通信的中间软件抽象层,将复杂的TCP/IP隐藏在一系列Socket接口后面
图1. Scoket抽象层
一次标准的Socket通信为(图2):
服务端首先初始化Socket,然后绑定并监听端口,阻塞直到有客户端连接,接着使用send和receive方法与客户端交互,最后关闭Socket;
客户端首先初始化Socket,然后根据Ip和端口与服务端连接,成功后通过send和receive方法与之交互,最后关闭Socket
图2. Socket通信
基于MFC的Socket编程
首先添加链接库:ws2_32.lib
服务端
- 加载套接字(socket)库
- 创建套接字
- 绑定套接字到本地地址和端口
- 将套接字设为监听模式
- 等待客户请求到来,当请求到来后接受并返回此次连接的套接字
- 使用上一步的套接字和客户端通信(send/recv)
- 关闭套接字
#include <Winsock2.h>//加裁头文件
#include <stdio.h>//加载标准输入输出头文件
void main()
{
WORD wVersionRequested;//版本号
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );//1.1版本的套接字
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) { return; }//加载套接字库,加裁失败则返回
if ( LOBYTE( wsaData.wVersion ) != 1 ||HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup( );
return;
}//如果不是1.1的则退出
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);//创建套接字(socket)。
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//转换Unsigned short为网络字节序的格式
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //将套接字绑定到一个本地地址和端口上(bind)
listen(sockSrv,5);//将套接字设为监听模式,准备接收客户请求(listen)。
SOCKADDR_IN addrClient;//定义地址族
int len=sizeof(SOCKADDR);//初始化这个参数,这个参数必须被初始化
while(1)
{
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len); //accept的第三个参数一定要有初始值。 //等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。 //此时程序在此发生阻塞
char sendBuf[100];
sprintf(sendBuf,"Welcome %s to www.msn.com.cn",inet_ntoa(addrClient.sin_addr)); //用返回的套接字和客户端进行通信(send/recv)。
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
char recvBuf[100];
recv(sockConn,recvBuf,100,0);
printf("%s\n",recvBuf);
closesocket(sockConn);//关闭套接字。等待另一个用户请求
}
}
客户端
- 加载套接字库
- 创建套接字
- 向服务器发出连接请求
- 和服务器进行通信(send/recv)
- 关闭套接字
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );//加载套接字库
if ( err != 0 ) { return; }
if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
{
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);//创建套接字(socket)。
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//向服务器发出连接请求(connect)。
char recvBuf[100];//和服务器端进行通信(send/recv)。
recv(sockClient,recvBuf,100,0);
printf("%s\n",recvBuf);
send(sockClient,"This is blues_j",strlen("This is blues_j")+1,0);
closesocket(sockClient);//关闭套接字。
WSACleanup();//必须调用这个函数清除参数
}
基于C#的Socket编程
服务端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
namespace socket_server
{
class Program
{
static IPAddress[] ServerIp;
static int port = 6000;
static void Main(string[] args)
{
// 基于TCP的Stream Scoket
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 绑定端口
ServerIp = Dns.GetHostAddresses("");
socket.Bind(new IPEndPoint(ServerIp[2], port));
// 开始监听,设定最大队列长度
socket.Listen(4);
// 接受客户端连接请求
socket.BeginAccept(new AsyncCallback((ar) =>
{
var client = socket.EndAccept(ar);
client.Send(Encoding.Unicode.GetBytes("Accept your request at " + DateTime.Now.ToString()));
//定时器
var timer = new System.Timers.Timer();
timer.Interval = 2000D;
timer.Enabled = true;
timer.Elapsed += (o, a) =>
{
//检测客户端连接状态
if (client.Connected)
{
try
{
//Console.WriteLine("Heart Beat " + DateTime.Now.ToString());
;
}
catch (SocketException ex)
{
Console.WriteLine(ex.Message);
}
}
else
{
timer.Stop();
timer.Enabled = false;
Console.WriteLine("Client is disconnectd");
}
};
timer.Start();
//接收客户端消息
client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), client);
}), null);
Console.WriteLine("[" + ServerIp[2].ToString() + ":" + port.ToString() + "]" + "Server is ready!");
Console.Read();
}
static byte[] buffer = new byte[1024];
public static void ReceiveMessage(IAsyncResult ar)
{
try
{
var socket = ar.AsyncState as Socket;
var length = socket.EndReceive(ar);
//var message = Encoding.Unicode.GetString(buffer, 0, length);
var message = Encoding.UTF8.GetString(buffer, 0, length);
Console.WriteLine("[" + socket.RemoteEndPoint.ToString() + "]" + message);
socket.Send(Encoding.UTF8.GetBytes("[" + ServerIp[2].ToString() + ":" + port.ToString() + "]" + "Hey this is a response from server " + DateTime.Now.ToString() + '\0'));
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
}
catch (Exception ex){
Console.WriteLine(ex.Message);
}
}
}
}
客户端
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
namespace SocketClient
{
class Program
{
static void Main(string[] args)
{
//创建一个Socket
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接到指定服务器的指定端口
socket.Connect("localhost", 6000);
Console.WriteLine("connect to the server");
//实现接受消息的方法
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
//接受用户输入,将消息发送给服务器端
while(true)
{
var message = "Message from client : " + Console.ReadLine();
var outputBuffer = Encoding.Unicode.GetBytes(message);
socket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, null, null);
}
}
static byte[] buffer = new byte[1024];
public static void ReceiveMessage(IAsyncResult ar)
{
try
{
var socket = ar.AsyncState as Socket;
var length = socket.EndReceive(ar);
//读取出来消息内容
var message = Encoding.Unicode.GetString(buffer, 0, length);
//显示消息
Console.WriteLine(message);
//递归接收下一个消息
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), socket);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}