今天写给大家一些干货。场景是这样的:客户需要http的方式对接我们原有的的一个程序,但是我不想每台机器上都部署IIS服务,因为这样会增加部署复杂度和成本。而且也不能保证每个机器都可以部署成功。于是我想换一种方式来应答对方程序的请求。于是有了以下尝试:
我使用tcp工具建立一个端口为6000的监听服务,再用浏览器访问 http://127.0.0.1:6000这个地址。可以发现tcp工具获得到了浏览起的请求信息。
我们在尝试将访问的地址改为:http://127.0.0.1:6000?para1=123。 可以看到在tcp工具中可以获得到请求的参数信息。如下图:
经过上面的尝试,我们发现用tcp服务来监听http请求这个方法是可行的,有兴趣的同学可以使用postman这个工具来尝试各种get请求或者post提交请求,来看看tcp服务到底会收到什么样格式的数据。
##那么我们怎么来响应对方的http的请求呢? 假设业务上我们需要给对方返回的http状态有: 成功200、未找到页面或者文件404、服务器内部错误400、无需操作304;在返回类型上需要:普通的文本、本地文件。那么我们只要按照如下格式返回给对方:
byte[] buffer = new byte[1024];
int revCount = client.Available;
int revLen = client.Receive(buffer, 0, 1024, SocketFlags.None);
DateTime revDateTime = DateTime.UtcNow;
if (revLen <= 0)
{
keepalive = false;
netMsg = "Receive Empty String";
LogHelper.TcpLog(netMsg);
continue;
}
//将获取到的数据转成字符串
string clientRequest = System.Text.Encoding.ASCII.GetString(buffer, 0, revLen);
if (clientRequest.Contains("login"))
{
if (clientRequest.Contains("sn=") &&
clientRequest.Contains("model=") &&
clientRequest.Contains("mac=") &&
clientRequest.Contains("ver=") &&
clientRequest.Contains("sense=") &&
clientRequest.Contains("display="))
{
try
{
string sn, model, mac, ver, sense, display, ipaddrs;
clientRequest = clientRequest.Substring(clientRequest.IndexOf("sn="));
sn = clientRequest.Substring(3, clientRequest.IndexOf('&') - 3);
clientRequest = clientRequest.Substring(clientRequest.IndexOf("model="));
model = clientRequest.Substring(6, clientRequest.IndexOf('&') - 6);
clientRequest = clientRequest.Substring(clientRequest.IndexOf("mac="));
mac = clientRequest.Substring(4, clientRequest.IndexOf('&') - 4);
clientRequest = clientRequest.Substring(clientRequest.IndexOf("ver="));
ver = clientRequest.Substring(4, clientRequest.IndexOf('&') - 4);
clientRequest = clientRequest.Substring(clientRequest.IndexOf("sense="));
sense = clientRequest.Substring(6, clientRequest.IndexOf('&') - 6);
clientRequest = clientRequest.Substring(clientRequest.IndexOf("display="));
display = clientRequest.Substring(8, clientRequest.IndexOf(" ") - 8);
Console.WriteLine("display++++++++++++++" + display);
ipaddrs = ((IPEndPoint)client.RemoteEndPoint).Address.ToString();
TerminalType = model;
string resolution = "";
switch (sense)
{
case "1":
resolution = "1920*1080";
break;
case "2":
resolution = "1080*1920";
break;
case "3":
resolution = "1366*768";
break;
case "4":
resolution = "768*1366";
break;
case "5":
resolution = "720*1280";
break;
case "6":
resolution = "800*600";
break;
case "7":
resolution = "1024*768";
break;
case "8":
resolution = "1280*768";
break;
case "9":
resolution = "1280*800";
break;
case "10":
resolution = "1280*1024";
break;
case "11":
resolution = "1360*768";
break;
case "12":
resolution = "768*1360";
break;
case "13":
resolution = "1280*720";
break;
case "14":
resolution = "1024*1280";
break;
case "15":
resolution = "2048*1536";
break;
case "16":
resolution = "1536*2048";
break;
case "17":
resolution = "768*1024";
break;
case "18":
resolution = "1600*900";
break;
case "19":
resolution = "900*1600";
break;
case "20":
resolution = "1440*900";
break;
case "21":
resolution = "900*1440";
break;
case "22":
resolution = "1680*1050";
break;
case "23":
resolution = "1050*1680";
break;
default:
resolution = "1920*1080";
break;
}
// 更新数据库终端信息
TerminalInfo result = SqLiteDataManger.Query<TerminalInfo>(string.Format("select * from tbTerminal where SN = '{0}'", sn)).FirstOrDefault();
if (result != null)
{
string updateTerminal = string.Format("update tbTerminal set Online={0},IP='{1}',Mac='{2}',Screen='{3}' ,TerminalType='{4}',Version='{5}' where SN='{6}'", 1, (client.RemoteEndPoint as IPEndPoint).Address.ToString(), mac, resolution, TerminalType, ver, sn);
SqLiteDataManger.ExecuteSql(updateTerminal);
}
else
{
string httpHeaderSNNotExistsString = "HTTP/1.1 400 \r\n";
httpHeaderSNNotExistsString += "Server: Apache-Coyote/1.1 \r\n";
httpHeaderSNNotExistsString += "X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderSNNotExistsString += "Content-Disposition: attachment;filename=" + "PlayList.SCH" + "\r\n";
httpHeaderSNNotExistsString += "Content-Type: text/plain\r\n";
httpHeaderSNNotExistsString += "Content-Length: 0\r\n";
httpHeaderSNNotExistsString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderSNNotExistsString += "Connection: close" + "\r\n";
httpHeaderSNNotExistsString += "\r\n";
byte[] byteSNNotMessage = Encoding.ASCII.GetBytes(httpHeaderSNNotExistsString);
client.Send(byteSNNotMessage);
netMsg = "Terminal Not Exists";
Tools.LogHelper.TcpLog(netMsg);
continue;
}
string httpHeaderString = "HTTP/1.1 200 OK\r\n";
httpHeaderString += "Apache-Coyote/1.1\r\n";
httpHeaderString += "Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderString += "0" + "\r\n";
httpHeaderString += "\r\n";
byte[] byteMessage = Encoding.ASCII.GetBytes(httpHeaderString);
client.Send(byteMessage);
netMsg = sn + " 登录成功";
Tools.LogHelper.TcpLog(netMsg);
}
catch (Exception e) // “解析、更新数据库”出错
{
string httpHeaderString = "HTTP/1.1 400 \r\n";
httpHeaderString += "Server: Apache-Coyote/1.1 \r\n";
httpHeaderString += "X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderString += "Content-Disposition: attachment;filename=" + "PlayList.SCH" + "\r\n";
httpHeaderString += "Content-Type: text/plain\r\n";
httpHeaderString += "Content-Length: 0\r\n";
httpHeaderString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderString += "Connection: close" + "\r\n";
httpHeaderString += "\r\n";
byte[] byteMessage = Encoding.ASCII.GetBytes(httpHeaderString);
client.Send(byteMessage);
WriteErrLog("登录出错", e);
netMsg = "login failed";
Tools.LogHelper.TcpLog(netMsg);
}
}
else // 格式错误
{
string httpHeaderString = "HTTP/1.1 400 \r\n";
httpHeaderString += "Server: Apache-Coyote/1.1 \r\n";
httpHeaderString += "X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderString += "Content-Disposition: attachment;filename=" + "PlayList.SCH" + "\r\n";
httpHeaderString += "Content-Type: text/plain\r\n";
httpHeaderString += "Content-Length: 0\r\n";
httpHeaderString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderString += "Connection: close" + "\r\n";
httpHeaderString += "\r\n";
byte[] byteMessage = Encoding.ASCII.GetBytes(httpHeaderString);
client.Send(byteMessage);
netMsg = "参数格式错误";
Tools.LogHelper.TcpLog(netMsg);
}
}
上面这段代码是客户端的一个http的登录请求,里面包含了登录提交的一些信息,服务端可以用receive()方法将接收到的字节转化为字符串解析。至于需要返回什么样的信息,可以通用实际的业务需要来组装响应的字符串发送给http的客户端。
##延伸 如何给客户端发送文件呢?
发送文件我们需要注意以下几点: 1.在返回的字符串中需要包含文件的大小信息。 2.必选先发送文本头信息,再发送文件。
为什么需要注意上面两点呢。因为tcp是传输层的协议,能传输字节流,保证流的顺序,但不明确每次请求应答的流的大小;而http是应用层协议,是包含数据包格式的。所以我们用tcp响应的时候需要遵守http协议的格式来发送数据流。 下面是给http客户端传送文件的代码:
// 如果文件存在则发送
if (filePath != null && fileSig != null && File.Exists(filePath))
{
netMsg = string.Format("{0} {1} {2}", sn, "StartDownload", filePath);
//MainForm.m_Logger.Log(Level.Info, netMsg);
Console.WriteLine(string.Format("{0} {1}", filePath, "Start"));
FileInfo fi = new FileInfo(filePath);
string httpHeaderString = "HTTP/1.1 200 OK\r\n";
httpHeaderString += "Server: Apache-Coyote/1.1\r\n";
httpHeaderString += "X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderString += "Content-Disposition:attachment;filename=" + fileSig + "\r\n";
httpHeaderString += "Last-modified: " + fi.LastWriteTime.ToString("r") + "\r\n";
httpHeaderString += "Content-Type:text/plain\r\n";
if (clientRange != "" && clientRange != "0")
{
int startPos = Convert.ToInt32(clientRange);
FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
br.BaseStream.Position = startPos;
httpHeaderString += "Content-Length:" + (fi.Length - startPos).ToString() + "\r\n";
httpHeaderString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderString += "Connection: close" + "\r\n";
httpHeaderString += "\r\n";
Byte[] bufferSend = new Byte[1024];
int num = 0;
while ((num = br.Read(bufferSend, 0, bufferSend.Length)) != 0)
{
client.Send(bufferSend, num, SocketFlags.None);
}
netMsg += "断点续传:" + startPos;
netMsg = string.Format(" {0} {1} {2}", sn, "EndDownload", filePath);
Tools.LogHelper.TcpLog(netMsg);
}
else
{
httpHeaderString += "Content-Length:" + fi.Length.ToString() + "\r\n";
httpHeaderString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderString += "Connection: close" + "\r\n";
httpHeaderString += "\r\n";
byte[] byteMessage = Encoding.ASCII.GetBytes(httpHeaderString);
client.Send(byteMessage);
client.SendFile(filePath);
TerminalInfo terminalInfo = Sqlite.SqLiteDataManger.Query<TerminalInfo>(string.Format("select * from tbTerminal where SN='{0}'", sn)).FirstOrDefault();
//terminalInfo.Status = 6;
Sqlite.SqLiteDataManger.ExecuteSql(string.Format("update tbTerminal set DownMediaCount={0},Status={1} where SN='{2}'", ++terminalInfo.DownMediaCount, terminalInfo.Status, terminalInfo.SN));
//即使更新页面数据
Messenger.Default.Send(terminalInfo, MessageToken.UpdateTerminalDownloadNum);
netMsg = string.Format("{0} {1} {2}", sn, "EndDownload", filePath);
Tools.LogHelper.TcpLog(netMsg);
}
Sqlite.SqLiteDataManger.ExecuteSql(string.Format("update tbMedia set RefCount={0} where FID='{1}'", mediaInfo.RefCount - 1, fid));
}
else // “串号/文件”不存在
{
string bodyStr = "Http Status 404 Media File Not Found";
string httpHeaderString = "HTTP/1.1 404 \r\n";
httpHeaderString += "Server: Apache-Coyote/1.1 \r\n";
httpHeaderString += "X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderString += "Content-Disposition: attachment;filename=" + "PlayList.SCH" + "\r\n";
httpHeaderString += "Content-Type: text/plain\r\n";
httpHeaderString += "Content-Length: " + bodyStr.Length.ToString() + "\r\n";
httpHeaderString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderString += "Connection: close" + "\r\n";
httpHeaderString += "\r\n";
httpHeaderString += bodyStr;
byte[] byteMessage = Encoding.ASCII.GetBytes(httpHeaderString);
client.Send(byteMessage);
netMsg = string.Format("{0} {1} {2} {3}", sn, fid, filePath, "fid/文件不存在");
Tools.LogHelper.TcpLog(netMsg);
}
}
catch (Exception e) // “解析/查表/发送文件”出错
{
string httpHeaderString = "HTTP/1.1 400 \r\n";
httpHeaderString += "Server: Apache-Coyote/1.1 \r\n";
httpHeaderString += "X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderString += "Content-Disposition: attachment;filename=" + "PlayList.SCH" + "\r\n";
httpHeaderString += "Content-Type: text/plain\r\n";
httpHeaderString += "Content-Length: 0\r\n";
httpHeaderString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderString += "Connection: close" + "\r\n";
httpHeaderString += "\r\n";
byte[] byteMessage = Encoding.ASCII.GetBytes(httpHeaderString);
client.Send(byteMessage);
Tools.LogHelper.ErrorLog(e);
Tools.LogHelper.TcpLog("发送素材异常 " + e.Message);
}
}
else // 格式错误
{
string httpHeaderString = "HTTP/1.1 400 \r\n";
httpHeaderString += "Server: Apache-Coyote/1.1 \r\n";
httpHeaderString += "X-Powered-By: Servlet 2.4; JBoss-4.0.5.GA (build: CVSTag=Branch_4_0 date=200610162339)/Tomcat-5.5\r\n";
httpHeaderString += "Content-Disposition: attachment;filename=" + "PlayList.SCH" + "\r\n";
httpHeaderString += "Content-Type: text/plain\r\n";
httpHeaderString += "Content-Length: 0\r\n";
httpHeaderString += "Date: " + DateTime.Now.ToString("r") + "\r\n";
httpHeaderString += "Connection: close" + "\r\n";
httpHeaderString += "\r\n";
byte[] byteMessage = Encoding.ASCII.GetBytes(httpHeaderString);
client.Send(byteMessage);
Tools.LogHelper.TcpLog("请求素材格式错误");
}
通过上面的代码可以看出 ,需要先得到文件大小信息放置在响应的头中 httpHeaderString += "Content-Length: " + bodyStr.Length.ToString() + "\r\n";
;在发送文件,发送文件可以使用 client.SendFile(filePath);
;其中client为socket对象,sendfile()为内置方法,可以异步发送文件,但无法监控进度。