ตัวอย่าง C ง่ายๆของการทำ HTTP POST และใช้การตอบสนอง


92

ฉันต้องการสร้างแอปพลิเคชัน C ที่เรียบง่ายซึ่งทำโพสต์ HTTP ใช้พารามิเตอร์สองสามตัวและใช้พารามิเตอร์เหล่านี้เพื่อสร้าง URL ฉันแค่ต้องการทำ HTTP POST แบบง่ายๆและรับการตอบสนองโดยไม่ต้องใช้ curl (ไลบรารีไม่ได้และจะไม่ได้รับการติดตั้งบนเครื่องซึ่งจำเป็นต้องเรียกใช้)

รหัสหลอก:

  1. กระบวนการ 2 args

  2. ใส่ args ลงใน URL เทมเพลต: http://api.somesite.com/apikey=ARG1&command=ARG2

  3. ทำ POST บน URL ที่สร้างขึ้น

  4. ใช้การตอบสนอง

การค้นหา Google และ SO ของฉันไม่ได้ให้ผลอะไรในเรื่องนี้


2
คุณใช้โครงข่ายเครือข่ายประเภทใด คุณใช้ OS อะไร?
cnicutar

มันจะเป็นแค่กล่อง Fedora หรือ Cent พื้นฐาน โครงข่ายเครือข่ายคือ sys / socket, netdb, arpa / inet ตามปกติ ไม่ใช่แค่ libcurl
kmarks2

1
ไม่ใช่ libcurl คุณยินดีที่จะไปกับไลบรารีอื่น ๆ หรือต้องเป็นแบบ POSIX ทั้งหมด
cnicutar

POSIX ทั้งหมดน่าเสียดาย จะต้องมีสถานะเป็นอิสระอย่างสมบูรณ์ในระบบใด ๆ
kmarks2

2
ฉันมีตัวอย่างที่สร้างมาให้คุณ แต่ฉันไม่เข้าใจว่าทำไมคุณถึงใช้ POST หากไม่มีเนื้อหาสำหรับข้อความ หากพารามิเตอร์ทั้งหมดอยู่ในสตริงข้อความค้นหาทำไมคุณไม่ต้องการทำ GET?
Jerry Jeremiah

คำตอบ:


196

ข้อความมีส่วนหัวและเนื้อหาของข้อความคั่นด้วยบรรทัดว่าง จำเป็นต้องใช้บรรทัดว่างเสมอแม้ว่าจะไม่มีเนื้อหาของข้อความก็ตาม ส่วนหัวเริ่มต้นด้วยคำสั่งและมีบรรทัดเพิ่มเติมของคู่คีย์ค่าที่คั่นด้วยเครื่องหมายจุดคู่และช่องว่าง หากมีเนื้อหาข้อความอาจเป็นอะไรก็ได้ที่คุณต้องการ

เส้นในส่วนหัวและบรรทัดว่างที่ส่วนท้ายของส่วนหัวจะต้องลงท้ายด้วยคู่ผลตอบแทน carraige และ linefeed (ดูลักษณะการแบ่งบรรทัดส่วนหัว HTTP ) ดังนั้นจึงเป็นสาเหตุที่บรรทัดเหล่านั้นมี \ r \ n ที่ส่วนท้าย

URL มีรูปแบบของ http://host:port/path?query_string

มีสองวิธีหลักในการส่งคำขอไปยังเว็บไซต์:

  • GET: สตริงการสืบค้นเป็นทางเลือก แต่หากระบุไว้ต้องสั้นพอสมควร ด้วยเหตุนี้ส่วนหัวอาจเป็นเพียงคำสั่ง GET และไม่มีอะไรอื่น ข้อความตัวอย่างอาจเป็น:

  • โพสต์: สิ่งที่ปกติจะอยู่ในสตริงข้อความค้นหาจะอยู่ในเนื้อความของข้อความแทน ด้วยเหตุนี้ส่วนหัวจึงต้องมีแอตทริบิวต์ Content-Type: และ Content-Length: รวมทั้งคำสั่ง POST ข้อความตัวอย่างอาจเป็น:

ดังนั้นเพื่อตอบคำถามของคุณ: หาก URL ที่คุณสนใจในการโพสต์คือhttp://api.somesite.com/apikey=ARG1&command=ARG2 แสดงว่าไม่มีเนื้อความหรือสตริงการค้นหาดังนั้นจึงไม่มีเหตุผลที่จะโพสต์เนื่องจากมี ไม่มีอะไรที่จะใส่ไว้ในเนื้อหาของข้อความและไม่มีอะไรให้ใส่ใน Content-Type: และ Content-Length:

ฉันเดาว่าคุณสามารถโพสต์ได้ถ้าคุณต้องการจริงๆ ในกรณีนี้ข้อความของคุณจะมีลักษณะดังนี้:

ดังนั้นในการส่งข้อความโปรแกรม C ต้องการ:

  • สร้างซ็อกเก็ต
  • ค้นหาที่อยู่ IP
  • เปิดซ็อกเก็ต
  • ส่งคำขอ
  • รอการตอบกลับ
  • ปิดซ็อกเก็ต

การส่งและรับสายไม่จำเป็นต้องส่ง / รับข้อมูลทั้งหมดที่คุณให้มา แต่จะส่งคืนจำนวนไบต์ที่รับ / ส่งจริง ขึ้นอยู่กับคุณที่จะเรียกพวกเขาแบบวนซ้ำและส่ง / รับส่วนที่เหลือของข้อความ

สิ่งที่ฉันไม่ได้ทำในตัวอย่างนี้คือการตรวจสอบข้อผิดพลาดจริงใด ๆ - เมื่อมีบางอย่างล้มเหลวฉันเพียงแค่ออกจากโปรแกรม แจ้งให้เราทราบหากเหมาะสำหรับคุณ:

เช่นเดียวกับคำตอบอื่น ๆ ที่ระบุไว้ 4096 ไบต์ไม่ใช่คำตอบที่ใหญ่มาก ฉันเลือกหมายเลขนั้นโดยสุ่มโดยสมมติว่าการตอบกลับคำขอของคุณจะสั้น ถ้ามันใหญ่คุณมีสองทางเลือก:

  • อ่านส่วนหัว Content-Length: จากการตอบกลับจากนั้นจัดสรรหน่วยความจำให้เพียงพอแบบไดนามิกเพื่อรองรับการตอบสนองทั้งหมด
  • เขียนการตอบกลับไปยังไฟล์เมื่อชิ้นส่วนมาถึง

ข้อมูลเพิ่มเติมเพื่อตอบคำถามที่ถามในความคิดเห็น:

จะเกิดอะไรขึ้นหากคุณต้องการโพสต์ข้อมูลในเนื้อหาของข้อความ จากนั้นคุณจะต้องรวมส่วนหัว Content-Type: และ Content-Length: ความยาวของเนื้อหา: คือความยาวจริงของทุกสิ่งหลังบรรทัดว่างที่แยกส่วนหัวออกจากเนื้อหา

นี่คือตัวอย่างที่ใช้อาร์กิวเมนต์บรรทัดคำสั่งต่อไปนี้:

  • เจ้าภาพ
  • ท่าเรือ
  • คำสั่ง (GET หรือ POST)
  • เส้นทาง (ไม่รวมข้อมูลแบบสอบถาม)
  • ข้อมูลการสืบค้น (ใส่ลงในสตริงการสืบค้นสำหรับ GET และในเนื้อหาสำหรับ POST)
  • รายการส่วนหัว (ความยาวเนื้อหา: เป็นไปโดยอัตโนมัติหากใช้ POST)

ดังนั้นสำหรับคำถามเดิมที่คุณจะเรียกใช้:

และสำหรับคำถามที่ถามในความคิดเห็นคุณจะเรียกใช้:

นี่คือรหัส:


ข้อโต้แย้งใดที่ควรส่งผ่านเมื่อถูกเรียก
Santiago Martí Olbrich

คุณต้องส่งบางสิ่งที่จะใช้เป็น apikey เป็นพารามิเตอร์แรกและบางอย่างในพารามิเตอร์ที่สองที่จะใช้เป็นคำสั่ง หากคุณต้องการใช้สตริงการสืบค้นที่แตกต่างกันโดยสิ้นเชิงคุณต้องเปลี่ยนสตริงรูปแบบจำนวนพารามิเตอร์และข้อความการใช้งาน
Jerry Jeremiah

2
รหัสนี้ส่งคำขอ HTTP ที่ผิดรูปแบบ HTTP ระบุว่าบรรทัดคำขอต้องถูกยกเลิกโดยคู่ carriage-return / line-feed ( \r\n) แต่รหัสนี้ใช้ฟีดบรรทัดเปล่า
John Bollinger

@JohnBollinger นั่นจริงมาก ขอบคุณที่ชี้ให้เห็น หวังว่าคำตอบที่แก้ไขจะดีกว่า
Jerry Jeremiah

ข้อความโพสต์นี้มีอะไรผิดพลาด "POST /variableName=%s&value=%s HTTP/1.1\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 4\r\n\r\n\r\n"ฉันต้องการโพสต์เช่น name = reaz ตอบสนอง 400 คำขอที่ไม่ถูกต้อง
Reaz Murshed

12

คำตอบของเจอร์รี่เยี่ยมมาก อย่างไรก็ตามไม่สามารถตอบสนองได้มาก การเปลี่ยนแปลงง่ายๆในการจัดการสิ่งนี้:


3
คุณสามารถทำให้อาร์เรย์ตอบสนองในตัวอย่างของฉันใหญ่ขึ้นได้ ฉันสมมติว่าเขาเพิ่งได้รับ json คืนมาและไม่ได้ดาวน์โหลดไฟล์ขนาดใหญ่ แต่แน่นอนว่าแม้แต่ json ก็สามารถเป็นเมกะไบต์ได้ขึ้นอยู่กับข้อความค้นหา ...
Jerry Jeremiah

1
ฉันเป็นมือใหม่ระดับ C และคำตอบของคุณอาจจะถูกต้อง แต่คุณช่วยเพิ่มคำอธิบายในคำตอบของคุณได้ไหม
boop

2
นี่เป็นเพียงความคิดเห็นเกี่ยวกับคำตอบที่ได้รับการยอมรับและไม่ควรทำเป็นความพยายามที่จะตอบแยกต่างหาก
Michael Gaskill

1
เพิ่มสิ่งเดียวที่นี่ซึ่งใช้งานได้ดี แต่คุณควรอ่านขนาดของบัฟเฟอร์ - 1 ไบต์ และเพื่อดูอย่างถูกต้องฉันจะไม่ใช้ขึ้นบรรทัดใหม่ในคำสั่งการพิมพ์นั้น ควรมีลักษณะดังนี้bytes = recv(sockfd, response, 1023, 0)
xjsc16x

11

หลังจากการวิจัยหลายสัปดาห์ ฉันคิดรหัสต่อไปนี้ ฉันเชื่อว่านี่เป็นเพียงขั้นต่ำที่จำเป็นในการเชื่อมต่ออย่างปลอดภัยด้วย SSL กับเว็บเซิร์ฟเวอร์

โค้ดด้านบนจะอธิบายรายละเอียดวิธีสร้างการเชื่อมต่อ TLS กับเซิร์ฟเวอร์ระยะไกล

โน๊ตสำคัญ : รหัสนี้ไม่ได้ตรวจสอบว่าคีย์สาธารณะลงนามโดยหน่วยงานที่ถูกต้องหรือไม่ หมายความว่าฉันไม่ได้ใช้ใบรับรองหลักในการตรวจสอบความถูกต้อง อย่าลืมใช้การตรวจสอบนี้ไม่เช่นนั้นคุณจะไม่รู้ว่าคุณกำลังเชื่อมต่อกับเว็บไซต์ที่ถูกต้องหรือไม่

เมื่อมาถึงคำขอนั้นเอง ไม่มีอะไรมากแล้วการเขียนคำขอ HTTP ด้วยมือ

นอกจากนี้คุณยังสามารถค้นหาภายใต้การเชื่อมโยงคำอธิบายวิธีการ OpenSSL Instal ในระบบของคุณและวิธีการรวบรวมรหัสเพื่อจะใช้ห้องสมุดที่เชื่อถือได้


2
คำอธิบายที่ดี!
Satyam Koyani

ไม่ตัวแปรถัดไปคือพอร์ตเราเชื่อมต่อกับพอร์ตที่ถูกต้องแล้ว
David Gatti

3

เพิ่มแฮนเดิลแล้ว
เพิ่มส่วนหัวของโฮสต์
เพิ่มการรองรับ linux / windows ทดสอบแล้ว (XP, WIN7)
คำเตือน: ข้อผิดพลาด: "การแบ่งส่วนผิด" หากไม่มีโฮสต์เส้นทางหรือพอร์ตเป็นอาร์กิวเมนต์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.